Skip to content

Commit

Permalink
Core/Spells: Reimplemented target type 106 as random point from point…
Browse files Browse the repository at this point in the history
…s predefined in database

Closes TrinityCore#30116
  • Loading branch information
Shauren committed Jan 21, 2025
1 parent 476b895 commit 2a51a64
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 46 deletions.
3 changes: 3 additions & 0 deletions sql/updates/world/master/2025_01_21_00_world.sql
Original file line number Diff line number Diff line change
@@ -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`);
2 changes: 1 addition & 1 deletion src/server/game/Miscellaneous/SharedDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
31 changes: 20 additions & 11 deletions src/server/game/Spells/Spell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
35 changes: 34 additions & 1 deletion src/server/game/Spells/SpellInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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());
Expand Down
3 changes: 2 additions & 1 deletion src/server/game/Spells/SpellInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ class TC_GAME_API SpellEffectInfo
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;
Expand Down
67 changes: 36 additions & 31 deletions src/server/game/Spells/SpellMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1156,23 +1161,22 @@ 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();

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)
Expand Down Expand Up @@ -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))
{
std::pair<uint32, SpellEffIndex> key = std::make_pair(spellId, effIndex);
mSpellTargetPositions[key] = st;
++count;
}
else
if (!hasTarget(TARGET_DEST_NEARBY_DB))
{
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)
continue;

if (effect->TargetA.GetTarget() != TARGET_DEST_DB && effect->TargetB.GetTarget() != TARGET_DEST_DB)
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()
Expand Down
3 changes: 2 additions & 1 deletion src/server/game/Spells/SpellMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 2a51a64

Please sign in to comment.