mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Core/Spells: Reimplemented target type 106 as random point from points predefined in database
Closes #30116
This commit is contained in:
3
sql/updates/world/master/2025_01_21_00_world.sql
Normal file
3
sql/updates/world/master/2025_01_21_00_world.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `spell_target_position` DROP PRIMARY KEY;
|
||||
ALTER TABLE `spell_target_position` ADD `OrderIndex` int NOT NULL DEFAULT '0' AFTER `EffectIndex`;
|
||||
ALTER TABLE `spell_target_position` ADD PRIMARY KEY(`ID`,`EffectIndex`,`OrderIndex`);
|
||||
@@ -2874,7 +2874,7 @@ enum Targets
|
||||
TARGET_UNIT_PASSENGER_7 = 103,
|
||||
TARGET_UNIT_CONE_CASTER_TO_DEST_ENEMY = 104,
|
||||
TARGET_UNIT_CASTER_AND_PASSENGERS = 105,
|
||||
TARGET_DEST_CHANNEL_CASTER = 106,
|
||||
TARGET_DEST_NEARBY_DB = 106,
|
||||
TARGET_DEST_NEARBY_ENTRY_2 = 107,
|
||||
TARGET_GAMEOBJECT_CONE_CASTER_TO_DEST_ENEMY = 108,
|
||||
TARGET_GAMEOBJECT_CONE_CASTER_TO_DEST_ALLY = 109,
|
||||
|
||||
@@ -1057,16 +1057,6 @@ void Spell::SelectImplicitChannelTargets(SpellEffectInfo const& spellEffectInfo,
|
||||
TC_LOG_DEBUG("spells", "SPELL: cannot find channel spell destination for spell ID {}, effect {}", m_spellInfo->Id, uint32(spellEffectInfo.EffectIndex));
|
||||
}
|
||||
break;
|
||||
case TARGET_DEST_CHANNEL_CASTER:
|
||||
{
|
||||
SpellDestination dest(*channeledSpell->GetCaster());
|
||||
if (m_spellInfo->HasAttribute(SPELL_ATTR4_USE_FACING_FROM_SPELL))
|
||||
dest._position.SetOrientation(spellEffectInfo.PositionFacing);
|
||||
|
||||
CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
|
||||
m_targets.SetDst(dest);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ABORT_MSG("Spell::SelectImplicitChannelTargets: received not implemented target type");
|
||||
break;
|
||||
@@ -1579,6 +1569,25 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffectInfo const& spellEffectIn
|
||||
if (WorldObject const* summoner = casterSummon->GetSummoner())
|
||||
dest = SpellDestination(*summoner);
|
||||
break;
|
||||
case TARGET_DEST_NEARBY_DB:
|
||||
{
|
||||
Optional<std::pair<float, float>> radiusBounds = spellEffectInfo.CalcRadiusBounds(m_caster, targetIndex, this);
|
||||
std::vector<SpellTargetPosition const*> positionsInRange;
|
||||
for (auto const& [_, position] : sSpellMgr->GetSpellTargetPositions(m_spellInfo->Id, spellEffectInfo.EffectIndex))
|
||||
if (m_caster->GetMapId() == position.GetMapId() && (!radiusBounds || (!m_caster->IsInDist(position, radiusBounds->first) && m_caster->IsInDist(position, radiusBounds->second))))
|
||||
positionsInRange.push_back(&position);
|
||||
|
||||
if (positionsInRange.empty())
|
||||
{
|
||||
TC_LOG_DEBUG("spells", "SPELL: unknown target coordinates for spell ID {}", m_spellInfo->Id);
|
||||
SendCastResult(SPELL_FAILED_NO_VALID_TARGETS);
|
||||
finish(SPELL_FAILED_NO_VALID_TARGETS);
|
||||
return;
|
||||
}
|
||||
|
||||
dest = Trinity::Containers::SelectRandomContainerElement(positionsInRange)->GetPosition();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
float dist = spellEffectInfo.CalcRadius(m_caster, targetIndex);
|
||||
@@ -1599,7 +1608,7 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffectInfo const& spellEffectIn
|
||||
case TARGET_DEST_CASTER_FRONT_RIGHT:
|
||||
case TARGET_DEST_CASTER_BACK_RIGHT:
|
||||
{
|
||||
constexpr float DefaultTotemDistance = 3.0f;
|
||||
static constexpr float DefaultTotemDistance = 3.0f;
|
||||
if (!spellEffectInfo.HasRadius(targetIndex))
|
||||
dist = DefaultTotemDistance;
|
||||
break;
|
||||
|
||||
@@ -347,7 +347,7 @@ std::array<SpellImplicitTargetInfo::StaticData, TOTAL_SPELL_TARGETS> SpellImplic
|
||||
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 103 TARGET_UNIT_PASSENGER_7
|
||||
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENEMY, TARGET_DIR_FRONT}, // 104 TARGET_UNIT_CONE_CASTER_TO_DEST_ENEMY
|
||||
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 105 TARGET_UNIT_CASTER_AND_PASSENGERS
|
||||
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CHANNEL, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 106 TARGET_DEST_CHANNEL_CASTER
|
||||
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 106 TARGET_DEST_NEARBY_DB
|
||||
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_ENTRY, TARGET_DIR_NONE}, // 107 TARGET_DEST_NEARBY_ENTRY_2
|
||||
{TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENEMY, TARGET_DIR_FRONT}, // 108 TARGET_GAMEOBJECT_CONE_CASTER_TO_DEST_ENEMY
|
||||
{TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ALLY, TARGET_DIR_FRONT}, // 109 TARGET_GAMEOBJECT_CONE_CASTER_TO_DEST_ALLY
|
||||
@@ -715,6 +715,39 @@ float SpellEffectInfo::CalcRadius(WorldObject* caster /*= nullptr*/, SpellTarget
|
||||
return radius;
|
||||
}
|
||||
|
||||
Optional<std::pair<float, float>> SpellEffectInfo::CalcRadiusBounds(WorldObject* caster, SpellTargetIndex targetIndex, Spell* spell) const
|
||||
{
|
||||
// TargetA -> TargetARadiusEntry
|
||||
// TargetB -> TargetBRadiusEntry
|
||||
// Aura effects have TargetARadiusEntry == TargetBRadiusEntry (mostly)
|
||||
SpellRadiusEntry const* entry = TargetARadiusEntry;
|
||||
if (targetIndex == SpellTargetIndex::TargetB && HasRadius(targetIndex))
|
||||
entry = TargetBRadiusEntry;
|
||||
|
||||
Optional<std::pair<float, float>> bounds;
|
||||
if (!entry)
|
||||
return bounds;
|
||||
|
||||
bounds.emplace(entry->RadiusMin, entry->RadiusMax);
|
||||
|
||||
if (caster)
|
||||
{
|
||||
if (Player* modOwner = caster->GetSpellModOwner())
|
||||
modOwner->ApplySpellMod(_spellInfo, SpellModOp::Radius, bounds->second, spell);
|
||||
|
||||
if (!_spellInfo->HasAttribute(SPELL_ATTR9_NO_MOVEMENT_RADIUS_BONUS))
|
||||
{
|
||||
if (Unit const* casterUnit = caster->ToUnit(); casterUnit && Spell::CanIncreaseRangeByMovement(casterUnit))
|
||||
{
|
||||
bounds->first = std::max(bounds->first - 2.0f, 0.0f);
|
||||
bounds->second += 2.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
uint32 SpellEffectInfo::GetProvidedTargetMask() const
|
||||
{
|
||||
return GetTargetFlagMask(TargetA.GetObjectType()) | GetTargetFlagMask(TargetB.GetObjectType());
|
||||
|
||||
@@ -266,7 +266,8 @@ public:
|
||||
float CalcDamageMultiplier(WorldObject* caster, Spell* spell = nullptr) const;
|
||||
|
||||
bool HasRadius(SpellTargetIndex targetIndex) const;
|
||||
float CalcRadius(WorldObject* caster = nullptr, SpellTargetIndex targetIndex = SpellTargetIndex::TargetA, Spell* = nullptr) const;
|
||||
float CalcRadius(WorldObject* caster = nullptr, SpellTargetIndex targetIndex = SpellTargetIndex::TargetA, Spell* spell = nullptr) const;
|
||||
Optional<std::pair<float, float>> CalcRadiusBounds(WorldObject* caster, SpellTargetIndex targetIndex, Spell* spell) const;
|
||||
|
||||
uint32 GetProvidedTargetMask() const;
|
||||
uint32 GetMissingTargetMask(bool srcSet = false, bool dstSet = false, uint32 mask = 0) const;
|
||||
|
||||
@@ -355,6 +355,11 @@ SpellTargetPosition const* SpellMgr::GetSpellTargetPosition(uint32 spell_id, Spe
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Trinity::IteratorPair<SpellTargetPositionMap::const_iterator> SpellMgr::GetSpellTargetPositions(uint32 spell_id, SpellEffIndex effIndex) const
|
||||
{
|
||||
return Trinity::Containers::MapEqualRange(mSpellTargetPositions, { spell_id, effIndex });
|
||||
}
|
||||
|
||||
SpellSpellGroupMapBounds SpellMgr::GetSpellSpellGroupMapBounds(uint32 spell_id) const
|
||||
{
|
||||
spell_id = GetFirstSpellInChain(spell_id);
|
||||
@@ -1156,15 +1161,14 @@ void SpellMgr::LoadSpellTargetPositions()
|
||||
|
||||
mSpellTargetPositions.clear(); // need for reload case
|
||||
|
||||
// 0 1 2 3 4 5 6
|
||||
QueryResult result = WorldDatabase.Query("SELECT ID, EffectIndex, MapID, PositionX, PositionY, PositionZ, Orientation FROM spell_target_position");
|
||||
// 0 1 2 3 4 5 6 7
|
||||
QueryResult result = WorldDatabase.Query("SELECT ID, EffectIndex, OrderIndex, MapID, PositionX, PositionY, PositionZ, Orientation FROM spell_target_position");
|
||||
if (!result)
|
||||
{
|
||||
TC_LOG_INFO("server.loading", ">> Loaded 0 spell target coordinates. DB table `spell_target_position` is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 count = 0;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
@@ -1172,7 +1176,7 @@ void SpellMgr::LoadSpellTargetPositions()
|
||||
uint32 spellId = fields[0].GetUInt32();
|
||||
SpellEffIndex effIndex = SpellEffIndex(fields[1].GetUInt8());
|
||||
|
||||
SpellTargetPosition st(fields[2].GetUInt16(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat());
|
||||
SpellTargetPosition st(fields[3].GetUInt16(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
|
||||
|
||||
MapEntry const* mapEntry = sMapStore.LookupEntry(st.GetMapId());
|
||||
if (!mapEntry)
|
||||
@@ -1200,61 +1204,62 @@ void SpellMgr::LoadSpellTargetPositions()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fields[6].IsNull())
|
||||
st.SetOrientation(fields[6].GetFloat());
|
||||
SpellEffectInfo const& spellEffectInfo = spellInfo->GetEffect(effIndex);
|
||||
if (!fields[7].IsNull())
|
||||
st.SetOrientation(fields[7].GetFloat());
|
||||
else
|
||||
{
|
||||
// target facing is in degrees for 6484 & 9268...
|
||||
if (spellInfo->GetEffect(effIndex).PositionFacing > 2 * float(M_PI))
|
||||
st.SetOrientation(spellInfo->GetEffect(effIndex).PositionFacing * float(M_PI) / 180);
|
||||
if (spellEffectInfo.PositionFacing > 2 * float(M_PI))
|
||||
st.SetOrientation(spellEffectInfo.PositionFacing * float(M_PI) / 180);
|
||||
else
|
||||
st.SetOrientation(spellInfo->GetEffect(effIndex).PositionFacing);
|
||||
st.SetOrientation(spellEffectInfo.PositionFacing);
|
||||
}
|
||||
|
||||
auto hasTarget = [&](Targets target)
|
||||
{
|
||||
SpellEffectInfo const& spellEffectInfo = spellInfo->GetEffect(effIndex);
|
||||
return spellEffectInfo.TargetA.GetTarget() == target || spellEffectInfo.TargetB.GetTarget() == target;
|
||||
};
|
||||
|
||||
if (hasTarget(TARGET_DEST_DB) || hasTarget(TARGET_DEST_NEARBY_ENTRY_OR_DB))
|
||||
if (!hasTarget(TARGET_DEST_NEARBY_DB))
|
||||
{
|
||||
std::pair<uint32, SpellEffIndex> key = std::make_pair(spellId, effIndex);
|
||||
mSpellTargetPositions[key] = st;
|
||||
++count;
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Spell (Id: {}, effIndex: {}) listed in `spell_target_position` does not have a target TARGET_DEST_DB (17).", spellId, uint32(effIndex));
|
||||
continue;
|
||||
if (!hasTarget(TARGET_DEST_DB) && !hasTarget(TARGET_DEST_NEARBY_ENTRY_OR_DB))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Spell (Id: {}, effIndex: {}) listed in `spell_target_position` does not have a target TARGET_DEST_DB ({}) or TARGET_DEST_NEARBY_DB ({}) or TARGET_DEST_NEARBY_ENTRY_OR_DB ({}).",
|
||||
spellId, uint32(effIndex), TARGET_DEST_DB, TARGET_DEST_NEARBY_DB, TARGET_DEST_NEARBY_ENTRY_OR_DB);
|
||||
continue;
|
||||
}
|
||||
if (fields[2].GetInt32() != 0)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Spell (Id: {}, effIndex: {}) listed in `spell_target_position` does not have a target TARGET_DEST_NEARBY_DB ({}) but lists multiple points, only one is allowed.",
|
||||
spellId, uint32(effIndex), TARGET_DEST_NEARBY_DB);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
mSpellTargetPositions.emplace(std::make_pair(spellId, effIndex), st);
|
||||
|
||||
} while (result->NextRow());
|
||||
|
||||
/*
|
||||
// Check all spells
|
||||
for (uint32 i = 1; i < GetSpellInfoStoreSize(); ++i)
|
||||
for (SpellInfo const& spellInfo : mSpellInfoMap)
|
||||
{
|
||||
SpellInfo const* spellInfo = GetSpellInfo(i);
|
||||
if (!spellInfo)
|
||||
if (spellInfo.Difficulty != DIFFICULTY_NONE)
|
||||
continue;
|
||||
|
||||
for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
|
||||
for (SpellEffectInfo const& effect : spellInfo.GetEffects())
|
||||
{
|
||||
SpellEffectInfo const* effect = spellInfo->GetEffect(j);
|
||||
if (!effect)
|
||||
if (effect.TargetA.GetTarget() != TARGET_DEST_DB && effect.TargetB.GetTarget() != TARGET_DEST_DB)
|
||||
continue;
|
||||
|
||||
if (effect->TargetA.GetTarget() != TARGET_DEST_DB && effect->TargetB.GetTarget() != TARGET_DEST_DB)
|
||||
continue;
|
||||
|
||||
if (!GetSpellTargetPosition(i, SpellEffIndex(j)))
|
||||
TC_LOG_DEBUG("spells", "Spell (Id: {}, EffectIndex: {}) does not have record in `spell_target_position`.", i, j);
|
||||
if (!GetSpellTargetPosition(spellInfo.Id, effect.EffectIndex))
|
||||
TC_LOG_DEBUG("spells", "Spell (Id: {}, EffectIndex: {}) does not have record in `spell_target_position`.", spellInfo.Id, effect.EffectIndex);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
TC_LOG_INFO("server.loading", ">> Loaded {} spell teleport coordinates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
TC_LOG_INFO("server.loading", ">> Loaded {} spell teleport coordinates in {} ms", mSpellTargetPositions.size(), GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
void SpellMgr::LoadSpellGroups()
|
||||
|
||||
@@ -409,7 +409,7 @@ typedef std::unordered_map<uint32, SpellThreatEntry> SpellThreatMap;
|
||||
// coordinates for spells (accessed using SpellMgr functions)
|
||||
using SpellTargetPosition = WorldLocation;
|
||||
|
||||
typedef std::map<std::pair<uint32 /*spell_id*/, SpellEffIndex /*effIndex*/>, SpellTargetPosition> SpellTargetPositionMap;
|
||||
typedef std::multimap<std::pair<uint32 /*spell_id*/, SpellEffIndex /*effIndex*/>, SpellTargetPosition> SpellTargetPositionMap;
|
||||
|
||||
// Enum with EffectRadiusIndex and their actual radius
|
||||
enum EffectRadiusIndex
|
||||
@@ -729,6 +729,7 @@ class TC_GAME_API SpellMgr
|
||||
|
||||
// Spell target coordinates
|
||||
SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const;
|
||||
Trinity::IteratorPair<SpellTargetPositionMap::const_iterator> GetSpellTargetPositions(uint32 spell_id, SpellEffIndex effIndex) const;
|
||||
|
||||
// Spell Groups table
|
||||
SpellSpellGroupMapBounds GetSpellSpellGroupMapBounds(uint32 spell_id) const;
|
||||
|
||||
Reference in New Issue
Block a user