fix(DB/SpellCone): implement spell_cone table and radius calculation (#26024)

This commit is contained in:
sogladev
2026-05-30 13:11:09 +02:00
committed by GitHub
parent cbb4937ee6
commit dab09941a5
9 changed files with 268 additions and 2 deletions
@@ -0,0 +1,7 @@
--
DROP TABLE IF EXISTS `spell_cone`;
CREATE TABLE `spell_cone` (
`ID` INT UNSIGNED NOT NULL COMMENT 'Spell ID',
`ConeDegrees` SMALLINT NOT NULL DEFAULT '60' COMMENT 'Cone angle in degrees',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -0,0 +1,106 @@
--
DELETE FROM `spell_cone` WHERE `ID` > 0;
INSERT INTO `spell_cone` VALUES
(5708, 90),
(8374, 90),
-- (9457, 90), -- Tharnariun's Heal
(11019, 90),
(12882, 90),
(12887, 90),
-- (15529, 359), -- Gout of Flame
(15847, -120),
(16094, 90),
(16099, 90),
(16169, 90),
(16340, 90),
(16350, 90),
(16359, 90),
(16390, 90),
(16396, 120),
(18144, 90),
(18435, 120),
(18500, 120),
(19630, 120),
(19641, 120),
(19813, 180),
(20667, 90),
(20712, 120),
(20716, 90),
(20717, 90),
(21071, 120),
(21099, 120),
(21189, 90),
(21333, 180),
(22479, 90),
(22539, 120),
(22558, 90),
(22559, 90),
(22560, 90),
(22561, 90),
-- (22600, 120), -- Force Reactive Disk
(22642, 90),
-- (22682, 120), -- Shadow Flame
(22833, 120),
(22945, 120),
-- (22993, 120), -- Shadow Flame
-- (23187, 120), -- Frost Burn
-- (23189, 120), -- Frost Burn
-- (23308, 120), -- Incinerate
-- (23309, 120), -- Incinerate
-- (23310, 120), -- Time Lapse
-- (23311, 120), -- Time Lapse
-- (23312, 120), -- Time Lapse
-- (23313, 120), -- Corrosive Acid
-- (23314, 120), -- Corrosive Acid
-- (23315, 120), -- Ignite Flesh
-- (23316, 120), -- Ignite Flesh
(23339, 120),
(23364, -120),
(23461, 120),
(23618, 90),
(23919, 120),
(24192, 120),
(24682, 120),
(24818, 120),
(24821, 45),
(24822, 135),
(24823, 90),
(24835, 180),
(24836, 225),
(24837, 270),
(24838, 315),
(24839, 120),
(24933, 7),
-- (25024, 45), -- Drop Mine
-- (25026, 45), Activate MG Turret
-- (25027, 30), -- Flamethrower
(25029, 30), -- Flamethrower
(25030, 90), -- Shoot Rocket
-- (25031, 45), -- Shoot Missile
-- (25032, 10), -- Machine Gun
-- (25060, 90), -- Updraft
(25145, 90),
(25149, 90),
(25150, 90),
(25322, 90),
(25653, -120),
(25993, 120),
(26025, 180), -- Impale used by 15233 Vekniss Guardian; Knockback AoE, Bleed Cone
-- (26027, 180), -- Knockback
(26029, 5), -- Dark Glare
-- (27766, 90), -- Cannon
(27807, 90),
(28127, 90),
(28157, 45),
(28168, 120),
(28405, 180),
(28438, 180),
(28459, 90),
-- (28460, 90), -- Wail of Souls
-- (28469, 360), -- Ghost Vision
(28741, 120),
(28783, 30),
-- (29998, 180), -- Decrepit Fever
(30043, 90),
(30056, 180),
(30732, 180);
@@ -0,0 +1,7 @@
--
-- 25029 Flamethrower TARGET_UNIT_CONE_ENTRY
-- 25030 Shoot Rocket TARGET_UNIT_CONE_ENTRY
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceEntry` IN (25029, 25030)) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 31) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 3) AND (`ConditionValue2` = 15328) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(13, 5, 25029, 0, 0, 31, 0, 3, 15328, 0, 0, 0, 0, '', 'target must be \'Steam Stonk\''),
(13, 1, 25030, 0, 0, 31, 0, 3, 15328, 0, 0, 0, 0, '', 'target must be \'Steam Stonk\'');
+22 -2
View File
@@ -52,6 +52,7 @@
#include "World.h"
#include "WorldPacket.h"
#include <cmath>
#include <G3D/g3dmath.h>
/// @todo: this import is not necessary for compilation and marked as unused by the IDE
// however, for some reasons removing it would cause a damn linking issue
@@ -1225,9 +1226,28 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge
SpellTargetObjectTypes objectType = targetType.GetObjectType();
SpellTargetCheckTypes selectionType = targetType.GetCheckType();
ConditionList* condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions;
float coneAngle = M_PI / 2;
float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
float coneAngle = G3D::toRadians(60.0f);
if (SpellCone const* sc = sSpellMgr->GetSpellCone(m_spellInfo->Id))
coneAngle = G3D::toRadians(static_cast<float>(sc->cone_degrees));
else
{
switch (targetType.GetTarget())
{
case TARGET_UNIT_CONE_ENEMY_24:
coneAngle = G3D::toRadians(24.0f);
break;
case TARGET_UNIT_CONE_ENEMY_54:
coneAngle = G3D::toRadians(54.0f);
break;
case TARGET_UNIT_CONE_ENEMY_104:
coneAngle = G3D::toRadians(104.0f);
break;
default:
break;
}
}
float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
if (uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList))
{
Acore::WorldObjectSpellConeTargetCheck check(coneAngle, radius, m_caster, m_spellInfo, selectionType, condList);
@@ -5207,6 +5207,13 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Effects[EFFECT_0].BasePoints = 1;
});
// 26025 Impale
ApplySpellFix({ 26025 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_1].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CONE_ENEMY_54);
spellInfo->Effects[EFFECT_1].TargetB = SpellImplicitTargetInfo(0);
});
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
SpellInfo* spellInfo = mSpellInfoMap[i];
+97
View File
@@ -695,6 +695,16 @@ SpellTargetPosition const* SpellMgr::GetSpellTargetPosition(uint32 spell_id, Spe
return nullptr;
}
SpellCone const* SpellMgr::GetSpellCone(uint32 spell_id) const
{
spell_id = GetFirstSpellInChain(spell_id);
auto itr = mSpellCones.find(spell_id);
if (itr != mSpellCones.end())
return &itr->second;
return nullptr;
}
SpellSpellGroupMapBounds SpellMgr::GetSpellSpellGroupMapBounds(uint32 spell_id) const
{
spell_id = GetFirstSpellInChain(spell_id);
@@ -1580,6 +1590,93 @@ void SpellMgr::LoadSpellTargetPositions()
LOG_INFO("server.loading", " ");
}
void SpellMgr::LoadSpellCones()
{
uint32 oldMSTime = getMSTime();
mSpellCones.clear(); // need for reload case
// 0 1
QueryResult result = WorldDatabase.Query("SELECT ID, ConeDegrees FROM spell_cone");
if (!result)
{
LOG_WARN("server.loading", ">> Loaded 0 spell cone definitions. DB table `spell_cone` is empty.");
LOG_INFO("server.loading", " ");
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
uint32 spellId = fields[0].Get<uint32>();
int16 coneDeg = fields[1].Get<int16>();
SpellInfo const* spellInfo = GetSpellInfo(spellId);
if (!spellInfo)
{
LOG_ERROR("sql.sql", "Spell (ID:{}) listed in `spell_cone` does not exist.", spellId);
continue;
}
if (coneDeg < -360 || coneDeg > 360)
{
LOG_ERROR("sql.sql", "Spell (Id: {}) cone degrees {} out of range (-360..360).", spellId, coneDeg);
continue;
}
uint32 firstRankId = GetFirstSpellInChain(spellId);
SpellInfo const* firstSpellInfo = GetSpellInfo(firstRankId);
SpellInfo const* checkInfo = firstSpellInfo ? firstSpellInfo : spellInfo;
bool hasCone = false;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (checkInfo->Effects[i].TargetA.GetSelectionCategory() == TARGET_SELECT_CATEGORY_CONE ||
checkInfo->Effects[i].TargetB.GetSelectionCategory() == TARGET_SELECT_CATEGORY_CONE)
{
hasCone = true;
break;
}
}
if (!hasCone)
{
LOG_ERROR("sql.sql", "Spell (ID:{}) listed in `spell_cone` does not have a cone implicit target.", spellId);
continue;
}
if (firstRankId != spellId)
LOG_INFO("server.loading", "Spell (ID:{}) listed in `spell_cone` is not first rank; mapping to first rank {}.", spellId, firstRankId);
SpellCone sc{};
sc.cone_degrees = coneDeg;
// Avoid overwriting an existing first-rank entry with conflicting values
auto itr = mSpellCones.find(firstRankId);
if (itr != mSpellCones.end())
{
if (itr->second.cone_degrees != sc.cone_degrees)
LOG_ERROR("sql.sql",
"Conflicting `spell_cone` entries for first-rank spell ID {}: {} vs {}. Keeping first value.",
firstRankId,
itr->second.cone_degrees,
sc.cone_degrees);
}
else
{
mSpellCones[firstRankId] = sc;
++count;
}
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} Spell Cone definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
void SpellMgr::LoadSpellGroups()
{
uint32 oldMSTime = getMSTime();
+10
View File
@@ -393,6 +393,13 @@ struct SpellTargetPosition
typedef std::map<std::pair<uint32 /*spell_id*/, SpellEffIndex /*effIndex*/>, SpellTargetPosition> SpellTargetPositionMap;
struct SpellCone
{
int16_t cone_degrees;
};
typedef std::map<uint32 /*spell_id*/, SpellCone> SpellConeMap;
// Enum with EffectRadiusIndex and their actual radius
enum EffectRadiusIndex
{
@@ -683,6 +690,7 @@ public:
// Spell target coordinates
[[nodiscard]] SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const;
[[nodiscard]] SpellCone const* GetSpellCone(uint32 spell_id) const;
// Spell Groups table
SpellSpellGroupMapBounds GetSpellSpellGroupMapBounds(uint32 spell_id) const;
@@ -775,6 +783,7 @@ public:
void LoadSpellRequired();
void LoadSpellLearnSkills();
void LoadSpellTargetPositions();
void LoadSpellCones();
void LoadSpellGroups();
void LoadSpellGroupStackRules();
void LoadSpellProcs();
@@ -807,6 +816,7 @@ private:
SpellRequiredMap mSpellReq;
SpellLearnSkillMap mSpellLearnSkills;
SpellTargetPositionMap mSpellTargetPositions;
SpellConeMap mSpellCones;
SpellSpellGroupMap mSpellSpellGroup;
SpellGroupSpellMap mSpellGroupSpell;
SpellGroupStackMap mSpellGroupStack;
+3
View File
@@ -682,6 +682,9 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading Spell Target Coordinates...");
sSpellMgr->LoadSpellTargetPositions();
LOG_INFO("server.loading", "Loading Spell Cone definitions...");
sSpellMgr->LoadSpellCones();
LOG_INFO("server.loading", "Loading Enchant Custom Attributes...");
sSpellMgr->LoadEnchantCustomAttr();
@@ -164,6 +164,7 @@ public:
{ "spell_proc", HandleReloadSpellProcsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PROC, Console::Yes },
{ "spell_scripts", HandleReloadSpellScriptsCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes },
{ "spell_target_position", HandleReloadSpellTargetPositionCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION, Console::Yes },
{ "spell_cone", HandleReloadSpellConeCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION, Console::Yes },
{ "spell_threats", HandleReloadSpellThreatsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_THREATS, Console::Yes },
{ "spell_group_stack_rules", HandleReloadSpellGroupStackRulesCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_GROUP_STACK_RULES, Console::Yes },
{ "player_loot_template", HandleReloadLootTemplatesPlayerCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes },
@@ -936,6 +937,14 @@ public:
return true;
}
static bool HandleReloadSpellConeCommand(ChatHandler* handler)
{
LOG_INFO("server.loading", "Reloading Spell cone definitions...");
sSpellMgr->LoadSpellCones();
handler->SendGlobalGMSysMessage("DB table `spell_cone` reloaded.");
return true;
}
static bool HandleReloadSpellThreatsCommand(ChatHandler* handler)
{
LOG_INFO("server.loading", "Reloading Aggro Spells Definitions...");