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

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);

View File

@@ -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];

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();

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;

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();

View File

@@ -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...");