diff --git a/etc/databases/world/updates/updates.sql b/etc/databases/world/updates/updates.sql index 62d6951c8..af6b3cc95 100644 --- a/etc/databases/world/updates/updates.sql +++ b/etc/databases/world/updates/updates.sql @@ -13716,6 +13716,280 @@ begin not atomic insert into applied_updates values ('171220251'); end if; + -- 23/12/2025 1 + if (select count(*) from applied_updates where id='231220251') = 0 then + -- Fix Engineering items. + UPDATE `item_template` SET `display_id` = '7435' WHERE (`entry` = '4358'); + UPDATE `item_template` SET `display_id` = '7435' WHERE (`entry` = '4378'); + UPDATE `item_template` SET `display_id` = '7435' WHERE (`entry` = '10507'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4358'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4378'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4367'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4398'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4370'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4406'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4375'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4376'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4381'); + UPDATE `item_template` SET `subclass` = '0' WHERE (`entry` = '4388'); + + -- Burning War Axe. + UPDATE `item_template` SET `spellid_1` = '7711', `spellcharges_1` = '-1' WHERE (`entry` = '2299'); + + -- Thundersnout. + UPDATE `creature_template` SET `rank` = '1' WHERE (`entry` = '5055'); + UPDATE `creature_template` SET `damage_multiplier` = '1.7', `health_multiplier` = '2.5', `armor_multiplier` = '1' WHERE (`entry` = '5055'); + UPDATE `creature_template` SET `detection_range` = '20' WHERE (`entry` = '5055'); + + -- Deviate Crocolisks are now non-elite. + UPDATE `creature_template` SET `rank` = '1' WHERE (`entry` = '5053'); + UPDATE `creature_template` SET `damage_multiplier` = '1.7', `health_multiplier` = '2.5', `armor_multiplier` = '1' WHERE (`entry` = '5053'); + UPDATE `creature_template` SET `detection_range` = '20' WHERE (`entry` = '5053'); + UPDATE `creature_template` SET `level_max` = '20' WHERE (`entry` = '5053'); + + -- New creature spell lists. + REPLACE INTO `creature_spells` (`entry`, `name`, `spellId_1`, `probability_1`, `castTarget_1`, `targetParam1_1`, `targetParam2_1`, `castFlags_1`, `delayInitialMin_1`, `delayInitialMax_1`, `delayRepeatMin_1`, `delayRepeatMax_1`, `scriptId_1`, `spellId_2`, `probability_2`, `castTarget_2`, `targetParam1_2`, `targetParam2_2`, `castFlags_2`, `delayInitialMin_2`, `delayInitialMax_2`, `delayRepeatMin_2`, `delayRepeatMax_2`, `scriptId_2`, `spellId_3`, `probability_3`, `castTarget_3`, `targetParam1_3`, `targetParam2_3`, `castFlags_3`, `delayInitialMin_3`, `delayInitialMax_3`, `delayRepeatMin_3`, `delayRepeatMax_3`, `scriptId_3`, `spellId_4`, `probability_4`, `castTarget_4`, `targetParam1_4`, `targetParam2_4`, `castFlags_4`, `delayInitialMin_4`, `delayInitialMax_4`, `delayRepeatMin_4`, `delayRepeatMax_4`, `scriptId_4`, `spellId_5`, `probability_5`, `castTarget_5`, `targetParam1_5`, `targetParam2_5`, `castFlags_5`, `delayInitialMin_5`, `delayInitialMax_5`, `delayRepeatMin_5`, `delayRepeatMax_5`, `scriptId_5`, `spellId_6`, `probability_6`, `castTarget_6`, `targetParam1_6`, `targetParam2_6`, `castFlags_6`, `delayInitialMin_6`, `delayInitialMax_6`, `delayRepeatMin_6`, `delayRepeatMax_6`, `scriptId_6`, `spellId_7`, `probability_7`, `castTarget_7`, `targetParam1_7`, `targetParam2_7`, `castFlags_7`, `delayInitialMin_7`, `delayInitialMax_7`, `delayRepeatMin_7`, `delayRepeatMax_7`, `scriptId_7`, `spellId_8`, `probability_8`, `castTarget_8`, `targetParam1_8`, `targetParam2_8`, `castFlags_8`, `delayInitialMin_8`, `delayInitialMax_8`, `delayRepeatMin_8`, `delayRepeatMax_8`, `scriptId_8`) VALUES (5055, 'Deviate Lasher', 6255, 80, 1, 0, 0, 0, 5, 10, 12, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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 `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '18684'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '18685'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '18686'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '18681'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '18680'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '33980'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '33981'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '38126'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '38132'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '38127'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '48752'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '45721'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '85912'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '85918'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '85916'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87111'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '85989'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87112'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '85994'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '85995'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '85996'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '86325'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '86415'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '86105'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87098'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87101'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87102'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87103'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87106'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87110'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87119'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87120'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87135'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87137'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87136'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87148'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87152'); + UPDATE `spawns_creatures` SET `ignored` = '1' WHERE (`spawn_id` = '87151'); + + -- https://github.com/The-Alpha-Project/alpha-core/issues/1614 + UPDATE `areatrigger_teleport` SET `target_position_x` = '7200', `target_position_y` = '-838', `target_position_z` = '-2', `target_orientation` = '1.69' WHERE (`id` = '259'); + UPDATE `worldports` SET `x` = '7200', `y` = '-838', `z` = '-2', `o` = '4.60' WHERE (`entry` = '116'); + UPDATE `worldports` SET `x` = '7200', `y` = '-838', `z` = '-2', `o` = '4.60' WHERE (`entry` = '115'); + + -- https://github.com/The-Alpha-Project/alpha-core/issues/1615 + UPDATE `spawns_creatures` SET `spawn_entry2` = '4803', `position_x` = '7329.976', `position_y` = '-926.581', `position_z` = '26.886', `ignored` = '0' WHERE (`spawn_id` = '33285'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4803', `position_x` = '7330.661', `position_y` = '-956.526', `position_z` = '21.355', `ignored` = '0' WHERE (`spawn_id` = '33286'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4803', `position_x` = '7361.977', `position_y` = '-956.526', `position_z` = '15.8', `ignored` = '0' WHERE (`spawn_id` = '33287'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4803', `position_x` = '7360.347', `position_y` = '-924.597', `position_z` = '10.244', `ignored` = '0' WHERE (`spawn_id` = '33288'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4803', `position_x` = '7331.168', `position_y` = '-969.052', `position_z` = '1.274', `ignored` = '0' WHERE (`spawn_id` = '33289'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4803', `position_x` = '7345.075', `position_y` = '-956.467', `position_z` = '-2.549', `ignored` = '0' WHERE (`spawn_id` = '33290'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4803', `position_x` = '7335.171', `position_y` = '-994.75', `position_z` = '4.594', `ignored` = '0' WHERE (`spawn_id` = '33291'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4803', `position_x` = '7337.593', `position_y` = '-1014.914', `position_z` = '5.07', `ignored` = '0' WHERE (`spawn_id` = '33292'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4803', `position_x` = '7324.585', `position_y` = '-1006.85', `position_z` = '7.31', `ignored` = '0' WHERE (`spawn_id` = '33293'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7307.925', `position_y` = '-998.504', `position_z` = '10.407', `ignored` = '0' WHERE (`spawn_id` = '33294'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7291.623', `position_y` = '-1003.646', `position_z` = '13.72', `ignored` = '0' WHERE (`spawn_id` = '33295'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7264.027', `position_y` = '-985.447', `position_z` = '14.062', `ignored` = '0' WHERE (`spawn_id` = '33296'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7242.308', `position_y` = '-986.236', `position_z` = '12.419', `ignored` = '0' WHERE (`spawn_id` = '33297'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7215.997', `position_y` = '-1001.37', `position_z` = '8.272', `ignored` = '0' WHERE (`spawn_id` = '33298'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7218.884', `position_y` = '-1023.406', `position_z` = '5.734', `ignored` = '0' WHERE (`spawn_id` = '33299'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7186.713', `position_y` = '-1018.934', `position_z` = '6.373', `ignored` = '0' WHERE (`spawn_id` = '33300'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7172.797', `position_y` = '-976.058', `position_z` = '7.43', `ignored` = '0' WHERE (`spawn_id` = '33302'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7220.33', `position_y` = '-1024.405', `position_z` = '5.534', `ignored` = '0' WHERE (`spawn_id` = '33303'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7181.906', `position_y` = '-966.99', `position_z` = '8.068', `ignored` = '0' WHERE (`spawn_id` = '33304'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7198.093', `position_y` = '-952.203', `position_z` = '13.206', `ignored` = '0' WHERE (`spawn_id` = '33305'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7210.375', `position_y` = '-933.342', `position_z` = '19.135', `ignored` = '0' WHERE (`spawn_id` = '33306'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7215.71', `position_y` = '-903.382', `position_z` = '13.826', `ignored` = '0' WHERE (`spawn_id` = '33307'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7234.793', `position_y` = '-876.929', `position_z` = '5.612', `ignored` = '0' WHERE (`spawn_id` = '33308'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7226.869', `position_y` = '-847.338', `position_z` = '1.036', `ignored` = '0' WHERE (`spawn_id` = '33309'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7238.033', `position_y` = '-830.053', `position_z` = '-0.934', `ignored` = '0' WHERE (`spawn_id` = '33310'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7261.411', `position_y` = '-822.074', `position_z` = '-6.646', `ignored` = '0' WHERE (`spawn_id` = '33311'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7228.346', `position_y` = '-817.803', `position_z` = '-2.083', `ignored` = '0' WHERE (`spawn_id` = '33312'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7200.409', `position_y` = '-832.39', `position_z` = '-3.295', `ignored` = '0' WHERE (`spawn_id` = '38665'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7197.827', `position_y` = '-809.274', `position_z` = '-5.784', `ignored` = '0' WHERE (`spawn_id` = '38666'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7234.529', `position_y` = '-853.076', `position_z` = '1.142', `ignored` = '0' WHERE (`spawn_id` = '38808'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7225.653', `position_y` = '-869.667', `position_z` = '3.192', `ignored` = '0' WHERE (`spawn_id` = '38974'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7162.353', `position_y` = '-987.953', `position_z` = '5.219', `ignored` = '0' WHERE (`spawn_id` = '38976'); + UPDATE `spawns_creatures` SET `spawn_entry2` = '4802', `position_x` = '7154.26', `position_y` = '-976.811', `position_z` = '5.219', `ignored` = '0' WHERE (`spawn_id` = '38977'); + + -- Sapphire of Aku'Mai. + UPDATE `gameobject_template` SET `displayId` = '219' WHERE (`entry` = '178184'); + UPDATE `gameobject_template` SET `displayId` = '219' WHERE (`entry` = '178186'); + + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7170.637', `spawn_positionY` = '-835.637', `spawn_positionZ` = '-4.33', `ignored` = '0' WHERE (`spawn_id` = '47700'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7207.226', `spawn_positionY` = '-820.836', `spawn_positionZ` = '-4.359', `ignored` = '0' WHERE (`spawn_id` = '47701'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7194.21', `spawn_positionY` = '-822.588', `spawn_positionZ` = '-2.327', `ignored` = '0' WHERE (`spawn_id` = '47702'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7240.419', `spawn_positionY` = '-801.943', `spawn_positionZ` = '-0.804', `ignored` = '0' WHERE (`spawn_id` = '47703'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7242.694', `spawn_positionY` = '-796.015', `spawn_positionZ` = '-0.703', `ignored` = '0' WHERE (`spawn_id` = '12597'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7218.879', `spawn_positionY` = '-856.85', `spawn_positionZ` = '1.475', `ignored` = '0' WHERE (`spawn_id` = '12680'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7230.974', `spawn_positionY` = '-889.777', `spawn_positionZ` = '8.922', `ignored` = '0' WHERE (`spawn_id` = '12737'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7207.672', `spawn_positionY` = '-922.717', `spawn_positionZ` = '19.06', `ignored` = '0' WHERE (`spawn_id` = '12971'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7188.083', `spawn_positionY` = '-969.881', `spawn_positionZ` = '9.085', `ignored` = '0' WHERE (`spawn_id` = '12974'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7143.786', `spawn_positionY` = '-1009.911', `spawn_positionZ` = '1.745', `ignored` = '0' WHERE (`spawn_id` = '12975'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7150.199', `spawn_positionY` = '-978.224', `spawn_positionZ` = '1.997', `ignored` = '0' WHERE (`spawn_id` = '12976'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7134.333', `spawn_positionY` = '-972.33', `spawn_positionZ` = '1.834', `ignored` = '0' WHERE (`spawn_id` = '47704'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7172.979', `spawn_positionY` = '-1008.211', `spawn_positionZ` = '8.581', `ignored` = '0' WHERE (`spawn_id` = '47705'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7215.15', `spawn_positionY` = '-1031.327', `spawn_positionZ` = '5.573', `ignored` = '0' WHERE (`spawn_id` = '47706'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7204.904', `spawn_positionY` = '-990.651', `spawn_positionZ` = '12.862', `ignored` = '0' WHERE (`spawn_id` = '48527'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7258.449', `spawn_positionY` = '-992.256', `spawn_positionZ` = '15.103', `ignored` = '0' WHERE (`spawn_id` = '66114'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7290.677', `spawn_positionY` = '-987.456', `spawn_positionZ` = '12.206', `ignored` = '0' WHERE (`spawn_id` = '66117'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7323.686', `spawn_positionY` = '-1011.742', `spawn_positionZ` = '8.448', `ignored` = '0' WHERE (`spawn_id` = '12607'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7353.294', `spawn_positionY` = '-1004.24', `spawn_positionZ` = '4.619', `ignored` = '0' WHERE (`spawn_id` = '12608'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7344.894', `spawn_positionY` = '-979.081', `spawn_positionZ` = '5.518', `ignored` = '0' WHERE (`spawn_id` = '12633'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7339.358', `spawn_positionY` = '-936.26', `spawn_positionZ` = '-5.943', `ignored` = '0' WHERE (`spawn_id` = '12970'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7361.472', `spawn_positionY` = '-946.255', `spawn_positionZ` = '-2.422', `ignored` = '0' WHERE (`spawn_id` = '12973'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7232.584', `spawn_positionY` = '-1088.886', `spawn_positionZ` = '-2.906', `ignored` = '0' WHERE (`spawn_id` = '12977'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7236.339', `spawn_positionY` = '-1071.187', `spawn_positionZ` = '-1.655', `ignored` = '0' WHERE (`spawn_id` = '47708'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7231.96', `spawn_positionY` = '-1029.418', `spawn_positionZ` = '5.442', `ignored` = '0' WHERE (`spawn_id` = '47709'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7202.164', `spawn_positionY` = '-1047.757', `spawn_positionZ` = '0.243', `ignored` = '0' WHERE (`spawn_id` = '47710'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7206.83', `spawn_positionY` = '-1054.763', `spawn_positionZ` = '3.452', `ignored` = '0' WHERE (`spawn_id` = '47711'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7187.108', `spawn_positionY` = '-1056.761', `spawn_positionZ` = '3.699', `ignored` = '0' WHERE (`spawn_id` = '55103'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7221.238', `spawn_positionY` = '-873.562', `spawn_positionZ` = '4.657', `ignored` = '0' WHERE (`spawn_id` = '55104'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7228.259', `spawn_positionY` = '-806.796', `spawn_positionZ` = '-2.275', `ignored` = '0' WHERE (`spawn_id` = '55105'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7269.82', `spawn_positionY` = '-816.293', `spawn_positionZ` = '-6.59', `ignored` = '0' WHERE (`spawn_id` = '55106'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7254.73', `spawn_positionY` = '-829.8', `spawn_positionZ` = '-1.72', `ignored` = '0' WHERE (`spawn_id` = '55191'); + + -- Bruiseweed. + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7232.22', `spawn_positionY` = '-912.841', `spawn_positionZ` = '15.0677' WHERE (`spawn_id` = '68490'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7227', `spawn_positionY` = '-813.303', `spawn_positionZ` = '-2.444' WHERE (`spawn_id` = '24253'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7293.99', `spawn_positionY` = '-1007.82', `spawn_positionZ` = '14.1899' WHERE (`spawn_id` = '68491'); + + -- Clams. + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7190.36', `spawn_positionY` = '-801.884', `spawn_positionZ` = '-6.08355' WHERE (`spawn_id` = '48046'); + UPDATE `spawns_gameobjects` SET `spawn_positionX` = '7168.23', `spawn_positionY` = '-770.48', `spawn_positionZ` = '-5.91007' WHERE (`spawn_id` = '48047'); + + -- Fallenroot Satyr. + UPDATE `spawns_creatures` SET `position_x` = '7213.932', `position_y` = '-832.024', `position_z` = '-2.712', `ignored` = '0' WHERE (`spawn_id` = '33166'); + UPDATE `spawns_creatures` SET `position_x` = '7227.299', `position_y` = '-861.059', `position_z` = '1.865', `ignored` = '0' WHERE (`spawn_id` = '33167'); + UPDATE `spawns_creatures` SET `position_x` = '7225.854', `position_y` = '-912.134', `position_z` = '15.323', `ignored` = '0' WHERE (`spawn_id` = '33168'); + UPDATE `spawns_creatures` SET `position_x` = '7172.837', `position_y` = '-1001.29', `position_z` = '9.511', `ignored` = '0' WHERE (`spawn_id` = '33169'); + UPDATE `spawns_creatures` SET `position_x` = '7198.722', `position_y` = '-1010.773', `position_z` = '7.385', `ignored` = '0' WHERE (`spawn_id` = '33170'); + UPDATE `spawns_creatures` SET `position_x` = '7282.656', `position_y` = '-991.871', `position_z` = '13.75', `ignored` = '0' WHERE (`spawn_id` = '33171'); + UPDATE `spawns_creatures` SET `position_x` = '7340.932', `position_y` = '-1004.148', `position_z` = '3.857', `ignored` = '0' WHERE (`spawn_id` = '33172'); + + -- Whaldak Spider Trainer placement. + UPDATE `spawns_creatures` SET `position_x` = '-4833.676', `position_y` = '-2706.077', `orientation` = '3.731' WHERE (`spawn_id` = '400071'); + -- Olthran Craghelm placement. + UPDATE `spawns_creatures` SET `position_x` = '-5037.026', `position_y` = '-1149.393', `position_z` = '530.177', `orientation` = '0.340' WHERE (`spawn_id` = '92'); + + -- Hammerhead Sharks - Theramore. + UPDATE `creature_template` SET `display_id1` = '2851', `display_id2` = '0', `display_id3` = '0', `level_min` = '10', `level_max` = '30' WHERE (`entry` = '5185'); + INSERT INTO `spawns_creatures` (`spawn_id`, `spawn_entry1`, `spawn_entry2`, `spawn_entry3`, `spawn_entry4`, `map`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecsmin`, `spawntimesecsmax`, `wander_distance`, `health_percent`, `mana_percent`, `movement_type`, `spawn_flags`, `visibility_mod`, `ignored`) VALUES ('400474', '5185', '0', '0', '0', '1', '-3937.427', '-4650.228', '-6.616', '0.317', '300', '300', '0', '100', '0', '2', '0', '0', '0'); + INSERT INTO `spawns_creatures` (`spawn_id`, `spawn_entry1`, `spawn_entry2`, `spawn_entry3`, `spawn_entry4`, `map`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecsmin`, `spawntimesecsmax`, `wander_distance`, `health_percent`, `mana_percent`, `movement_type`, `spawn_flags`, `visibility_mod`, `ignored`) VALUES ('400475', '5185', '0', '0', '0', '1', '-4026.160', '-4566.016', '-11.210', '3.257', '300', '300', '0', '100', '0', '2', '0', '0', '0'); + INSERT INTO `spawns_creatures` (`spawn_id`, `spawn_entry1`, `spawn_entry2`, `spawn_entry3`, `spawn_entry4`, `map`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecsmin`, `spawntimesecsmax`, `wander_distance`, `health_percent`, `mana_percent`, `movement_type`, `spawn_flags`, `visibility_mod`, `ignored`) VALUES ('400476', '5185', '0', '0', '0', '1', '-3871.077', '-4715.082', '-11.359', '2.328', '300', '300', '0', '100', '0', '2', '0', '0', '0'); + + DELETE FROM creature_movement WHERE id = 400476; + INSERT INTO creature_movement (id, point, position_x, position_y, position_z, orientation, waittime, wander_distance, script_id) VALUES + (400476, 0, -3871.08, -4715.08, -11.359, 0, 0, 0, 0), + (400476, 1, -3937.43, -4650.23, -6.616, 0, 0, 0, 0); + + DELETE FROM creature_movement WHERE id = 400474; + INSERT INTO creature_movement (id, point, position_x, position_y, position_z, orientation, waittime, wander_distance, script_id) VALUES + (400474, 0, -3937.43, -4650.23, -6.616, 0, 0, 0, 0), + (400474, 1, -4019.872, -4570.601, -10.032, 0, 0, 0, 0); + + DELETE FROM creature_movement WHERE id = 400475; + INSERT INTO creature_movement (id, point, position_x, position_y, position_z, orientation, waittime, wander_distance, script_id) VALUES + (400475, 0, -4026.16, -4566.02, -11.21, 0, 0, 0, 0), + (400475, 1, -3852.411, -4695.709, -8.891, 0, 0, 0, 0); + + -- Hammerhead Sharks - Wetlands. + INSERT INTO `spawns_creatures` (`spawn_id`, `spawn_entry1`, `spawn_entry2`, `spawn_entry3`, `spawn_entry4`, `map`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecsmin`, `spawntimesecsmax`, `wander_distance`, `health_percent`, `mana_percent`, `movement_type`, `spawn_flags`, `visibility_mod`, `ignored`) VALUES ('400477', '5185', '0', '0', '0', '0', '-3619.912', '-667.062', '-5.495', '2.847', '300', '300', '0', '100', '0', '2', '0', '0', '0'); + INSERT INTO `spawns_creatures` (`spawn_id`, `spawn_entry1`, `spawn_entry2`, `spawn_entry3`, `spawn_entry4`, `map`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecsmin`, `spawntimesecsmax`, `wander_distance`, `health_percent`, `mana_percent`, `movement_type`, `spawn_flags`, `visibility_mod`, `ignored`) VALUES ('400478', '5185', '0', '0', '0', '0', '-3860.069', '-970.231', '-9.424', '1.893', '300', '300', '0', '100', '0', '2', '0', '0', '0'); + INSERT INTO `spawns_creatures` (`spawn_id`, `spawn_entry1`, `spawn_entry2`, `spawn_entry3`, `spawn_entry4`, `map`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecsmin`, `spawntimesecsmax`, `wander_distance`, `health_percent`, `mana_percent`, `movement_type`, `spawn_flags`, `visibility_mod`, `ignored`) VALUES ('400479', '5185', '0', '0', '0', '0', '-3086.198', '-878.560', '-8.186', '6.281', '300', '300', '0', '100', '0', '2', '0', '0', '0'); + + DELETE FROM creature_movement WHERE id = 400477; + INSERT INTO creature_movement (id, point, position_x, position_y, position_z, orientation, waittime, wander_distance, script_id) VALUES + (400477, 0, -3619.912, -667.062, -6.0, 0, 0, 0, 0), + (400477, 1, -3679.512, -654.877, -5.768, 0, 0, 0, 0), + (400477, 2, -3754.543, -660.885, -6.968, 0, 0, 0, 0), + (400477, 3, -3840.858, -725.926, -4.462, 0, 0, 0, 0), + (400477, 4, -3895.266, -798.169, -3.192, 0, 0, 0, 0), + (400477, 5, -3888.073, -871.998, -4.459, 0, 0, 0, 0), + (400477, 6, -3886.798, -925.889, -5.656, 0, 0, 0, 0), + (400477, 7, -3926.045, -899.105, -3.271, 0, 0, 0, 0), + (400477, 8, -3944.383, -846.207, -7.741, 0, 0, 0, 0), + (400477, 9, -3916.853, -764.062, -6.979, 0, 0, 0, 0), + (400477, 10, -3789.159, -667.851, -6.656, 0, 0, 0, 0), + (400477, 11, -3643.234, -665.627, -3.28, 0, 0, 0, 0); + + DELETE FROM creature_movement WHERE id = 400479; + INSERT INTO creature_movement (id, point, position_x, position_y, position_z, orientation, waittime, wander_distance, script_id) VALUES + (400479, 0, -3086.198, -878.56, -8.186, 0, 0, 0, 0), + (400479, 1, -2994.042, -890.377, -5.982, 0, 0, 0, 0), + (400479, 2, -2949.162, -914.763, -5.667, 0, 0, 0, 0), + (400479, 3, -2911.086, -967.312, -5.657, 0, 0, 0, 0), + (400479, 4, -2860.667, -1015.364, -7.212, 0, 0, 0, 0), + (400479, 5, -2763.598, -1087.138, -4.957, 0, 0, 0, 0), + (400479, 6, -2757.104, -1009.766, -6.123, 0, 0, 0, 0), + (400479, 7, -2805.729, -952.217, -6.572, 0, 0, 0, 0), + (400479, 8, -2943.019, -953.778, -2.742, 0, 0, 0, 0), + (400479, 9, -2977.853, -888.88, -3.793, 0, 0, 0, 0), + (400479, 10, -3063.256, -876.323, -6.928, 0, 0, 0, 0); + + DELETE FROM creature_movement WHERE id = 400478; + INSERT INTO creature_movement (id, point, position_x, position_y, position_z, orientation, waittime, wander_distance, script_id) VALUES + (400478, 0, -3860.07, -970.231, -9.424, 0, 0, 0, 0), + (400478, 1, -3847.447, -1041.32, -6.086, 0, 0, 0, 0), + (400478, 2, -3818.298, -1131.86, -5.075, 0, 0, 0, 0), + (400478, 3, -3765.189, -1227.954, -3.878, 0, 0, 0, 0), + (400478, 4, -3670.321, -1173.761, -6.452, 0, 0, 0, 0), + (400478, 5, -3577.506, -1101.181, -6.452, 0, 0, 0, 0), + (400478, 6, -3535.464, -1039.792, -6.452, 0, 0, 0, 0), + (400478, 7, -3601.396, -975.107, -6.452, 0, 0, 0, 0), + (400478, 8, -3580.904, -907.404, -6.452, 0, 0, 0, 0), + (400478, 9, -3553.939, -839.087, -6.452, 0, 0, 0, 0), + (400478, 10, -3589.421, -832.031, -6.452, 0, 0, 0, 0), + (400478, 11, -3623.885, -909.151, -6.452, 0, 0, 0, 0), + (400478, 12, -3665.608, -962.974, -6.452, 0, 0, 0, 0), + (400478, 13, -3740.933, -964.078, -6.452, 0, 0, 0, 0), + (400478, 14, -3818.444, -943.057, -6.452, 0, 0, 0, 0), + (400478, 15, -3874.734, -902.968, -6.452, 0, 0, 0, 0), + (400478, 16, -3893.729, -846.193, -6.452, 0, 0, 0, 0), + (400478, 17, -3884.289, -777.849, -6.452, 0, 0, 0, 0), + (400478, 18, -3836.793, -736.491, -6.452, 0, 0, 0, 0), + (400478, 19, -3857.304, -679.108, -6.452, 0, 0, 0, 0), + (400478, 20, -3943.348, -631.051, -6.452, 0, 0, 0, 0), + (400478, 21, -4076.014, -691.217, -6.452, 0, 0, 0, 0), + (400478, 22, -4120.404, -774.243, -6.452, 0, 0, 0, 0), + (400478, 23, -4071.049, -793.069, -6.452, 0, 0, 0, 0), + (400478, 24, -3991.929, -826.605, -6.452, 0, 0, 0, 0), + (400478, 25, -3904.568, -865.126, -6.452, 0, 0, 0, 0), + (400478, 26, -3851.88, -948.064, -6.452, 0, 0, 0, 0); + + -- Wharfmaster Lozgil. + UPDATE `spawns_creatures` SET `position_x` = '-14303.7', `position_y` = '527.608', `position_z` = '8.83', `orientation` = '4.86', `ignored` = '0', `map` = '0' WHERE (`spawn_id` = '184'); + + -- Frostmaw. + UPDATE `creature_template` SET `display_id1` = '934' WHERE (`entry` = '4504'); + DELETE FROM `event_scripts` WHERE `id`=727; + INSERT INTO `event_scripts` (`id`, `delay`, `priority`, `command`, `datalong`, `datalong2`, `datalong3`, `datalong4`, `target_param1`, `target_param2`, `target_type`, `data_flags`, `dataint`, `dataint2`, `dataint3`, `dataint4`, `x`, `y`, `z`, `o`, `condition_id`, `comments`) VALUES + (727, 0, 0, 10, 4504, 0, 1, 0, 0, 0, 0, 0, 8, 0, -1, 7, 234.227, -239.227, 141.325, 2.84489, 0, 'Frostmaw: Summon Creature Frostmaw'); + + -- Fresh Carcass - Player cast flag. + UPDATE `item_template` SET `flags` = '64' WHERE (`entry` = '5810'); + -- Etched Phial - Player cast flag. + UPDATE `item_template` SET `flags` = '2112' WHERE (`entry` = '5867'); + + insert into applied_updates values ('231220251'); + end if; end $ delimiter ; diff --git a/game/world/managers/maps/Cell.py b/game/world/managers/maps/Cell.py index 1c78550b6..d141d807e 100644 --- a/game/world/managers/maps/Cell.py +++ b/game/world/managers/maps/Cell.py @@ -2,6 +2,8 @@ from game.world.managers.objects.farsight.FarSightManager import FarSightManager from threading import RLock +from utils.ConfigManager import config + class Cell: def __init__(self, cell_x=0, cell_y=0, map_id=0, instance_id=0, key=''): @@ -180,26 +182,42 @@ def send_all(self, packet, source, include_source=False, exclude=None, use_ignor camera.broadcast_packet(packet, exclude=players_reached) def send_all_in_range(self, packet, range_, source, include_source=True, exclude=None, use_ignore=False): + # If range is non-positive, send to all players without filtering. if range_ <= 0: self.send_all(packet, source, exclude) - else: - players_reached = set() - for guid, player_mgr in list(self.players.items()): - if not player_mgr.online or not player_mgr.location.distance(source.location) <= range_: - continue - if not include_source and player_mgr.guid == source.guid: - continue - if use_ignore and player_mgr.friends_manager.has_ignore(source.guid): - continue - # Never send messages to a player that does not know the source object. - if not player_mgr.guid == source.guid and source.guid not in player_mgr.known_objects: + return + + is_yell = int(range_) == int(config.World.Chat.ChatRange.yell_range) + is_say = int(range_) == int(config.World.Chat.ChatRange.say_range) + + players_reached = set() + for guid, player_mgr in list(self.players.items()): + # Skip offline players. + if not player_mgr.online: + continue + # Check distance. + distance = player_mgr.location.distance(source.location) + if distance > range_: + continue + # Optionally exclude source. + if not include_source and player_mgr.guid == source.guid: + continue + # Skip players that have ignored the source. + if use_ignore and player_mgr.friends_manager.has_ignore(source.guid): + continue + # Ensure the player knows about the source object if this is not a chat message. + if not is_say and not is_yell: + if guid != source.guid and source.guid not in player_mgr.known_objects: continue - players_reached.add(player_mgr.guid) - player_mgr.enqueue_packet(packet) - # If this cell has cameras, route packets. - for camera in FarSightManager.get_cell_cameras(self): - camera.broadcast_packet(packet, exclude=players_reached) + # Add to reached players. + players_reached.add(guid) + # Send packet. + player_mgr.enqueue_packet(packet) + + # Route packets via cameras if applicable. + for camera in FarSightManager.get_cell_cameras(self): + camera.broadcast_packet(packet, exclude=players_reached) def can_deactivate(self): return not self.has_players() and not self.has_cameras() diff --git a/game/world/managers/maps/GridManager.py b/game/world/managers/maps/GridManager.py index 41f2ce14e..18db5d70a 100644 --- a/game/world/managers/maps/GridManager.py +++ b/game/world/managers/maps/GridManager.py @@ -197,15 +197,16 @@ def _update_players_surroundings(self, cell_key, exclude_cells=None, world_objec return affected_cells - def _get_surrounding_cells_by_cell(self, cell=None, cell_x=0, cell_y=0, map_id=0, instance_id=0): + def _get_surrounding_cells_by_cell(self, cell=None, cell_x=0, cell_y=0, map_id=0, instance_id=0, range_=0): if cell: cell_x = cell.cell_x cell_y = cell.cell_y map_id = cell.map_id instance_id = cell.instance_id + view_distance = VIEW_DISTANCE if not range_ else range_ # Calculate how many cells to include in each direction given the view distance, at least 1. - max_cells_radius = max(1, int(VIEW_DISTANCE // CELL_SIZE)) + max_cells_radius = max(1, int(view_distance // CELL_SIZE)) surrounding_cells = set() for dx in range(-max_cells_radius, max_cells_radius + 1): @@ -222,13 +223,15 @@ def _get_surrounding_cells_by_cell(self, cell=None, cell_x=0, cell_y=0, map_id=0 return surrounding_cells - def _get_surrounding_cells_by_object(self, world_object): + def _get_surrounding_cells_by_object(self, world_object, range_=0): pos = world_object.location - return self._get_surrounding_cells_by_location(pos.x, pos.y, world_object.map_id, world_object.instance_id) + return self._get_surrounding_cells_by_location( + pos.x, pos.y, world_object.map_id, world_object.instance_id, range_=range_) - def _get_surrounding_cells_by_location(self, x, y, map_, instance_id): + def _get_surrounding_cells_by_location(self, x, y, map_, instance_id, range_=0): cell_x, cell_y = CellUtils.generate_coord_data(x, y) - return self._get_surrounding_cells_by_cell(cell_x=cell_x, cell_y=cell_y, map_id=map_, instance_id=instance_id) + return self._get_surrounding_cells_by_cell(cell_x=cell_x, cell_y=cell_y, map_id=map_, + instance_id=instance_id, range_=range_) def send_surrounding(self, packet, world_object, include_self=True, exclude=None, use_ignore=False): if world_object.current_cell: @@ -244,7 +247,7 @@ def send_surrounding_in_range(self, packet, world_object, range_, include_self=T Logger.warning(f'{world_object.get_name()} Cannot send surrounding in range without current cell.') return - for cell in self._get_surrounding_cells_by_object(world_object): + for cell in self._get_surrounding_cells_by_object(world_object, range_=range_): cell.send_all_in_range(packet, range_, world_object, include_self, exclude, use_ignore) def get_surrounding_objects(self, world_object, object_types): diff --git a/game/world/managers/objects/spell/CastingSpell.py b/game/world/managers/objects/spell/CastingSpell.py index 3b700f779..76803a884 100644 --- a/game/world/managers/objects/spell/CastingSpell.py +++ b/game/world/managers/objects/spell/CastingSpell.py @@ -483,66 +483,12 @@ def requires_combo_points(self): def requires_aura_state(self): return self.spell_entry.CasterAuraState != 0 - ''' - TODO: Figure out this for proper spell min max damage calculation. - void __fastcall Spell_C_GetMinMaxPoints(int effectIndex, int a2, int *min, int *max, unsigned int level, int isPet) - { - signed int SpellLevel; // edi - int v10; // eax - double v11; // st7 - char v13; // c0 - double v14; // st7 - int v15; // ecx - int v16; // ecx - int v17; // edi - double v18; // [esp+0h] [ebp-18h] - int dieSides; // [esp+14h] [ebp-4h] - int maxBonus; // [esp+28h] [ebp+10h] - float maxBonusa; // [esp+28h] [ebp+10h] - int minBonus; // [esp+2Ch] [ebp+14h] - - *min = 0; - *max = 0; - if ( effectIndex ) - { - SpellLevel = level; - dieSides = *(_DWORD *)(effectIndex + 4 * a2 + 224); - if ( !level ) - SpellLevel = Spell_C_GetSpellLevel(*(_DWORD *)effectIndex, isPet); - v10 = *(_DWORD *)(effectIndex + 88); - maxBonus = SpellLevel; - if ( v10 > 0 ) - { - SpellLevel -= v10; - maxBonus = SpellLevel; - } - if ( SpellLevel < 0 ) - { - SpellLevel = 0; - maxBonus = 0; - } - v11 = (double)maxBonus * *(float *)(effectIndex + 4 * a2 + 260); - maxBonusa = v11; - minBonus = (__int64)v11; - _floor(maxBonusa); - v18 = maxBonusa; - if ( v13 ) - v14 = _floor(v18); - else - v14 = _ceil(v18); - v15 = SpellLevel * *(_DWORD *)(effectIndex + 4 * a2 + 248) + *(_DWORD *)(effectIndex + 4 * a2 + 236); - *min = v15; - *min = *(_DWORD *)(effectIndex + 4 * a2 + 272) + minBonus + v15; - v16 = dieSides * *(_DWORD *)(effectIndex + 4 * a2 + 236); - *max = v16; - v17 = v16 + *(_DWORD *)(effectIndex + 4 * a2 + 248) * dieSides * SpellLevel; - *max = v17; - *max = *(_DWORD *)(effectIndex + 4 * a2 + 272) + (__int64)v14 + v17; - } - } - ''' def calculate_effective_level(self): - level = self.spell_caster.level + skill = 0 + if self.spell_caster.is_player(): + skill = self.spell_caster.skill_manager.get_skill_value_for_spell_id(self.spell_entry.ID) + + level = self.spell_caster.level if not skill else int(skill / 5) if level > self.spell_entry.MaxLevel > 0: level = self.spell_entry.MaxLevel elif level < self.spell_entry.BaseLevel: diff --git a/game/world/managers/objects/spell/SpellEffect.py b/game/world/managers/objects/spell/SpellEffect.py index dc4eb3d5d..58dd182c2 100644 --- a/game/world/managers/objects/spell/SpellEffect.py +++ b/game/world/managers/objects/spell/SpellEffect.py @@ -33,6 +33,7 @@ class SpellEffect: caster_effective_level: int effect_index: int + effect_points: int = 0 targets: EffectTargets radius_entry: SpellRadius @@ -50,6 +51,7 @@ def __init__(self, casting_spell, index): self.load_effect(casting_spell.spell_entry, index) self.caster_effective_level = casting_spell.caster_effective_level + self.effect_points = self.get_effect_points() self.targets = EffectTargets(casting_spell, self) self.radius_entry = DbcDatabaseManager.spell_radius_get_by_id(self.radius_index) if self.radius_index else None self.casting_spell = casting_spell @@ -158,7 +160,15 @@ def is_periodic(self): return self.aura_period != 0 def get_effect_points(self) -> int: - rolled_points = random.randint(1, self.die_sides + self.dice_per_level) if self.die_sides != 0 else 0 + if self.effect_points: + return self.effect_points + + # Calculate min and max dice roll values. + min_roll = 1 if self.die_sides != 0 else 0 + max_roll = self.die_sides + self.dice_per_level if self.die_sides != 0 else 0 + + # Roll. + rolled_points = random.randint(min_roll, max_roll) if self.die_sides != 0 else 0 return self.base_points + int(self.real_points_per_level * self.caster_effective_level) + rolled_points def get_effect_simple_points(self) -> int: diff --git a/game/world/managers/objects/spell/SpellManager.py b/game/world/managers/objects/spell/SpellManager.py index f1ed8679f..18c78f0fb 100644 --- a/game/world/managers/objects/spell/SpellManager.py +++ b/game/world/managers/objects/spell/SpellManager.py @@ -1,4 +1,3 @@ -import math import time from random import randint from struct import pack @@ -420,6 +419,13 @@ def perform_spell_cast(self, casting_spell: CastingSpell, validate=True): self.send_cast_result(casting_spell, SpellCheckCastResult.SPELL_NO_ERROR) self.send_spell_go(casting_spell) + # Spells that use the KneelLoop animation causes the client to get stuck in this animation until relog. + # Send a KneelEnd animation to resolve this issue. e.g. spell 6717 'Place Lion Carcass' + if casting_spell.spell_visual_entry and casting_spell.spell_visual_entry.CastKit == 380: # KneelLoop. + data = pack(f'QI', self.caster.guid, 444) + packet = PacketWriter.get_packet(OpCode.SMSG_PLAY_SPELL_VISUAL, data) + self.caster.enqueue_packet(packet) + if casting_spell.requires_combo_points(): # Combo points will be reset by consume_resources_for_cast. casting_spell.spent_combo_points = self.caster.combo_points @@ -835,14 +841,15 @@ def send_cast_start(self, casting_spell): if not self.caster.is_unit(by_mask=True): return # Non-unit casters should not broadcast their casts. - is_player = self.caster.is_player() + source_guid = self.caster.guid + if casting_spell.source_item: + source_guid = casting_spell.source_item.guid - source_guid = casting_spell.initial_target.guid if casting_spell.initial_target_is_item() else self.caster.guid cast_flags = casting_spell.cast_flags # Validate if this spell crashes the client. # Force SpellCastFlags.CAST_FLAG_PROC, which hides the start cast. - if not is_player and not ExtendedSpellData.UnitSpellsValidator.spell_has_valid_cast(casting_spell): + if not self.caster.is_player() and not ExtendedSpellData.UnitSpellsValidator.spell_has_valid_cast(casting_spell): Logger.warning(f'Hiding spell {casting_spell.spell_entry.Name_enUS} start cast due invalid cast.') cast_flags |= SpellCastFlags.CAST_FLAG_PROC @@ -866,7 +873,7 @@ def send_cast_start(self, casting_spell): # Spell start. data = pack(signature, *data) packet = PacketWriter.get_packet(OpCode.SMSG_SPELL_START, data) - self.caster.get_map().send_surrounding(packet, self.caster, include_self=is_player) + self.caster.get_map().send_surrounding(packet, self.caster, include_self=self.caster.is_player()) def handle_channel_start(self, casting_spell): if not casting_spell.is_channeled(): @@ -960,13 +967,14 @@ def send_spell_resist_result(self, casting_spell, damage_info): self.caster.get_map().send_surrounding(packet, self.caster, include_self=is_player) def send_spell_go(self, casting_spell): - # The client expects the source to only be set for unit casters. - caster_unit = casting_spell.initial_target.guid if casting_spell.initial_target_is_item() \ - else self.caster.guid + source_guid = self.caster.guid + if casting_spell.source_item: + source_guid = casting_spell.source_item.guid + caster_guid = self.caster.guid if self.caster.is_unit(by_mask=True) else 0 # Exclude proc flag from GO - proc casts are visible in 0.5.5 screenshots. - data = [caster_unit, caster_guid, casting_spell.spell_entry.ID, + data = [source_guid, caster_guid, casting_spell.spell_entry.ID, casting_spell.cast_flags & ~SpellCastFlags.CAST_FLAG_PROC] signature = '<2QIHB' # caster, source, ID, flags .. (targets, ammo info). diff --git a/game/world/managers/objects/spell/aura/AuraEffectHandler.py b/game/world/managers/objects/spell/aura/AuraEffectHandler.py index cd7537568..9c7baad59 100644 --- a/game/world/managers/objects/spell/aura/AuraEffectHandler.py +++ b/game/world/managers/objects/spell/aura/AuraEffectHandler.py @@ -5,7 +5,8 @@ from game.world.managers.objects.units.player.StatManager import UnitStats from game.world.managers.objects.spell import ExtendedSpellData from utils.Logger import Logger -from utils.constants.ItemCodes import InventoryError +from utils.constants import ItemCodes +from utils.constants.ItemCodes import InventoryError, ItemSubClasses from utils.constants.MiscCodes import UnitDynamicTypes, ProcFlags from utils.constants.PetCodes import PetSlot from utils.constants.SpellCodes import ShapeshiftForms, AuraTypes, SpellSchoolMask, SpellImmunity @@ -774,16 +775,27 @@ def handle_mod_dodge_chance(aura, effect_target, remove): effect_target.stat_manager.apply_aura_stat_bonus(aura.index, UnitStats.DODGE_CHANCE, amount_percent, percentual=False) - # TODO: Need to have separate blocking stats depending on item subclass. - # e.g. 'Increases your chance to block with a Shield (not a Buckler) by 2%.' @staticmethod def handle_mod_block_chance(aura, effect_target, remove): if remove: effect_target.stat_manager.remove_aura_stat_bonus(aura.index, percentual=False) return + + item_subclass = aura.spell_effect.casting_spell.spell_entry.EquippedItemSubclass + shield_present = item_subclass & (1 << ItemSubClasses.ITEM_SUBCLASS_SHIELD) + buckler_present = item_subclass & (1 << ItemSubClasses.ITEM_SUBCLASS_BUCKLER) + + if shield_present and buckler_present: + stat_type = UnitStats.BLOCK_SHIELD | UnitStats.BLOCK_BUCKLER + elif shield_present: + stat_type = UnitStats.BLOCK_SHIELD + elif buckler_present: + stat_type = UnitStats.BLOCK_BUCKLER + else: + return + amount_percent = aura.get_effect_points() / 100 - effect_target.stat_manager.apply_aura_stat_bonus(aura.index, UnitStats.BLOCK_CHANCE, - amount_percent, percentual=False) + effect_target.stat_manager.apply_aura_stat_bonus(aura.index, stat_type, amount_percent, percentual=False) @staticmethod def handle_mod_threat(aura, effect_target, remove): diff --git a/game/world/managers/objects/spell/aura/AuraManager.py b/game/world/managers/objects/spell/aura/AuraManager.py index b5cfc2b41..e6ebaf971 100644 --- a/game/world/managers/objects/spell/aura/AuraManager.py +++ b/game/world/managers/objects/spell/aura/AuraManager.py @@ -157,7 +157,7 @@ def check_aura_interrupts(self, moved=False, turned=False, changed_stand_state=F # An interrupt for sitting does not exist. # Food/drink spells do claim that the player must remain seated. - # In later versions an aurainterrupt exists for this purpose. + # In later versions an aura interrupt exists for this purpose. if aura.source_spell.is_refreshment_spell() and changed_stand_state and \ self.unit_mgr.stand_state != StandState.UNIT_SITTING: self.remove_aura(aura) diff --git a/game/world/managers/objects/units/UnitManager.py b/game/world/managers/objects/units/UnitManager.py index 1dfbf7cee..5c895ee4c 100644 --- a/game/world/managers/objects/units/UnitManager.py +++ b/game/world/managers/objects/units/UnitManager.py @@ -1021,6 +1021,12 @@ def can_block(self, attacker_location=None, in_combat=False): return self.has_block_passive and not self.spell_manager.is_casting() and \ not self.unit_state & UnitStates.STUNNED + def has_shield(self): + return False + + def has_buckler(self): + return False + def can_parry(self, attacker_location=None, in_combat=False): if not in_combat: return self.has_parry_passive @@ -1243,11 +1249,6 @@ def set_stealthed(self, active=True, index=-1) -> bool: def set_rooted(self, active=True, index=-1) -> bool: is_rooted = self.set_move_flag(MoveFlags.MOVEFLAG_ROOTED, active, index) is_rooted |= self.set_unit_state(UnitStates.ROOTED, active, index) - - if is_rooted: - # Stop movement if needed. - self.movement_manager.stop() - return is_rooted def set_stunned(self, active=True, index=-1) -> bool: @@ -1337,6 +1338,10 @@ def set_move_flag(self, move_flag, active=True, index=-1) -> bool: else: self.movement_flags &= ~move_flag + # Force movement stop if rooted or immobilized. + if flag_changed and is_active and move_flag in {MoveFlags.MOVEFLAG_ROOTED, MoveFlags.MOVEFLAG_IMMOBILIZED}: + self.movement_manager.stop(force=True) + # Only broadcast swimming, rooted, walking or immobilized. if flag_changed and move_flag in {MoveFlags.MOVEFLAG_SWIMMING, MoveFlags.MOVEFLAG_ROOTED, MoveFlags.MOVEFLAG_IMMOBILIZED, MoveFlags.MOVEFLAG_WALK}: diff --git a/game/world/managers/objects/units/creature/CreatureManager.py b/game/world/managers/objects/units/creature/CreatureManager.py index 273c96225..c93d442ff 100644 --- a/game/world/managers/objects/units/creature/CreatureManager.py +++ b/game/world/managers/objects/units/creature/CreatureManager.py @@ -304,6 +304,7 @@ def finish_loading(self): self.apply_default_auras() # Movement. + self.set_move_flag(MoveFlags.MOVEFLAG_SWIMMING, active=self.static_flags & CreatureStaticFlags.AQUATIC != 0) self.set_move_flag(MoveFlags.MOVEFLAG_WALK, active=not self.should_always_run_ooc()) self.movement_manager.initialize_or_reset() @@ -948,6 +949,9 @@ def _update_swimming_state(self): if not self.can_swim(): return + if not self.get_map().is_active_cell_for_location(self.location): + return + is_under_water = self.is_under_water() if is_under_water and not self.movement_flags & MoveFlags.MOVEFLAG_SWIMMING: diff --git a/game/world/managers/objects/units/player/PlayerManager.py b/game/world/managers/objects/units/player/PlayerManager.py index 96850f584..6defdcf57 100644 --- a/game/world/managers/objects/units/player/PlayerManager.py +++ b/game/world/managers/objects/units/player/PlayerManager.py @@ -36,7 +36,7 @@ from utils.GuidUtils import GuidUtils from utils.Logger import Logger from utils.constants.DuelCodes import * -from utils.constants.ItemCodes import InventoryTypes +from utils.constants.ItemCodes import InventoryTypes, ItemSubClasses from utils.constants.MiscCodes import ChatFlags, LootTypes, LiquidTypes, MountResults, DismountResults, LockTypes from utils.constants.MiscCodes import ObjectTypeFlags, ObjectTypeIds, PlayerFlags, WhoPartyStatus, HighGuid, \ AttackTypes, MoveFlags @@ -540,7 +540,7 @@ def trigger_teleport(self): pending_teleport.destination_location.z, pending_teleport.destination_location.o, self.pitch, - MoveFlags.MOVEFLAG_NONE + MoveFlags.MOVEFLAG_NONE if not self.collision_cheat else MoveFlags.MOVEFLAG_DONTCOLLIDE ) self.enqueue_packet(PacketWriter.get_packet(OpCode.MSG_MOVE_TELEPORT_ACK, data)) @@ -1381,8 +1381,23 @@ def can_block(self, attacker_location=None, in_combat=False): if attacker_location and not self.location.has_in_arc(attacker_location): return False # players can't block from behind. - return self.inventory.has_offhand() and \ - self.inventory.get_offhand().item_template.inventory_type == InventoryTypes.SHIELD + return self.has_shield() or self.has_buckler() + + # override + def has_shield(self): + if not self.inventory.has_offhand(): + return False + item_template = self.inventory.get_offhand().item_template + return (item_template.inventory_type == InventoryTypes.SHIELD + and item_template.subclass == ItemSubClasses.ITEM_SUBCLASS_SHIELD) + + # override + def has_buckler(self): + if not self.inventory.has_offhand(): + return False + item_template = self.inventory.get_offhand().item_template + return (item_template.inventory_type == InventoryTypes.SHIELD + and item_template.subclass == ItemSubClasses.ITEM_SUBCLASS_BUCKLER) # override def can_parry(self, attacker_location=None, in_combat=False): diff --git a/game/world/managers/objects/units/player/StatManager.py b/game/world/managers/objects/units/player/StatManager.py index 2811d8725..131674f59 100644 --- a/game/world/managers/objects/units/player/StatManager.py +++ b/game/world/managers/objects/units/player/StatManager.py @@ -51,6 +51,8 @@ class UnitStats(IntFlag): PARRY_CHANCE = auto() DODGE_CHANCE = auto() BLOCK_CHANCE = auto() + BLOCK_SHIELD = auto() + BLOCK_BUCKLER = auto() # Note: Block value did not exist in 0.5.3 PROC_CHANCE = auto() @@ -868,7 +870,16 @@ def get_attack_result_against_self(self, attacker, attack_type, rating_difference_block = self._get_combat_rating_difference(attacker.level, combat_rating, use_block=self.unit_mgr.can_block(in_combat=True)) - block_chance = self.get_total_stat(UnitStats.BLOCK_CHANCE, accept_float=True) + rating_difference_block * 0.0004 + block_chance = self.get_total_stat(UnitStats.BLOCK_CHANCE, accept_float=True) + + # Shield/Buckler bonuses. + if self.unit_mgr.has_shield(): + block_chance += self.get_total_stat(UnitStats.BLOCK_SHIELD, accept_float=True) + elif self.unit_mgr.has_buckler(): + block_chance += self.get_total_stat(UnitStats.BLOCK_BUCKLER, accept_float=True) + + block_chance += rating_difference_block * 0.0004 + can_block = not (invalid_result_mask & HitInfo.BLOCK) and self.unit_mgr.can_block(attacker.location, in_combat=True) if can_block and random.random() < block_chance: return hit_info | HitInfo.BLOCK @@ -1245,6 +1256,12 @@ def send_block_percentage(self): # Percentual bonuses are stored as 100% = 1, client expects 100% = 100 value = self.get_total_stat(UnitStats.BLOCK_CHANCE, accept_float=True) * 100 + # Shield/Buckler bonuses. + if self.unit_mgr.has_shield(): + value += self.get_total_stat(UnitStats.BLOCK_SHIELD, accept_float=True) * 100 + elif self.unit_mgr.has_buckler(): + value += self.get_total_stat(UnitStats.BLOCK_BUCKLER, accept_float=True) * 100 + # Penalty against player of same level with max skill. value += self._get_combat_rating_difference(use_block=True) * 0.04 diff --git a/game/world/managers/objects/units/player/quest/QuestManager.py b/game/world/managers/objects/units/player/quest/QuestManager.py index c657a52e8..a3464912c 100644 --- a/game/world/managers/objects/units/player/quest/QuestManager.py +++ b/game/world/managers/objects/units/player/quest/QuestManager.py @@ -821,7 +821,8 @@ def handle_accept_quest(self, quest_id, quest_giver_guid, shared=False, quest_gi self.send_cant_take_quest_response(QuestFailedReasons.QUEST_ALREADY_ON) return - if quest_id in self.completed_quests: + # Only one timed quest can be active. + if self.has_timed_quest(): self.send_cant_take_quest_response(QuestFailedReasons.QUEST_ONLY_ONE_TIMED) return @@ -1281,6 +1282,12 @@ def item_needed_by_quests(self, item_entry): return True return False + def has_timed_quest(self): + for active_quest in list(self.active_quests.values()): + if QuestHelpers.is_timed_quest(active_quest.quest): + return True + return False + def update(self, elapsed): self.last_timer_update += elapsed diff --git a/game/world/opcode_handling/Definitions.py b/game/world/opcode_handling/Definitions.py index 78325e230..140fc1171 100644 --- a/game/world/opcode_handling/Definitions.py +++ b/game/world/opcode_handling/Definitions.py @@ -386,6 +386,7 @@ OpCode.CMSG_FORCE_MOVE_ROOT_ACK: MovementHandler.handle_movement_status, OpCode.CMSG_FORCE_MOVE_UNROOT_ACK: MovementHandler.handle_movement_status, OpCode.CMSG_FORCE_SPEED_CHANGE_ACK: MovementHandler.handle_movement_status, + OpCode.CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: MovementHandler.handle_movement_status, # Ignored packets (Use NullHandler) OpCode.CMSG_TUTORIAL_CLEAR: NullHandler.handle,