From 4e29c11dc06ffe5eb8e9c6bbf1057f6a37e2e025 Mon Sep 17 00:00:00 2001 From: Hondacrx Date: Sun, 8 Jun 2025 14:36:57 -0400 Subject: [PATCH] Core/Spells: Reimplemented target type 106 as random point from points predefined in database Port From (https://github.com/TrinityCore/TrinityCore/commit/2a51a6454912df635099572d07789a1d6789ff4d) --- .../Framework/Constants/Spells/SpellConst.cs | 2 +- Source/Game/Spells/Spell.cs | 29 ++++++---- Source/Game/Spells/SpellInfo.cs | 37 +++++++++++- Source/Game/Spells/SpellManager.cs | 57 ++++++++++--------- 4 files changed, 87 insertions(+), 38 deletions(-) diff --git a/Source/Framework/Constants/Spells/SpellConst.cs b/Source/Framework/Constants/Spells/SpellConst.cs index c8ef6bed3..6b588ceb9 100644 --- a/Source/Framework/Constants/Spells/SpellConst.cs +++ b/Source/Framework/Constants/Spells/SpellConst.cs @@ -2916,7 +2916,7 @@ namespace Framework.Constants UnitPassenger7 = 103, UnitConeCasterToDestEnemy = 104, UnitCasterAndPassengers = 105, - DestChannelCaster = 106, + DestNearbyDb = 106, DestNearbyEntry2 = 107, GameobjectConeCasterToDestEnemy = 108, GameobjectConeCasterToDestAlly = 109, diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index dbf2d19f3..0177409de 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -567,16 +567,6 @@ namespace Game.Spells } break; } - case Targets.DestChannelCaster: - { - SpellDestination dest = new(channeledSpell.GetCaster()); - if (m_spellInfo.HasAttribute(SpellAttr4.UseFacingFromSpell)) - dest.Position.SetOrientation(spellEffectInfo.PositionFacing); - - CallScriptDestinationTargetSelectHandlers(ref dest, spellEffectInfo.EffectIndex, targetType); - m_targets.SetDst(dest); - break; - } default: Cypher.Assert(false, "Spell.SelectImplicitChannelTargets: received not implemented target type"); break; @@ -1139,6 +1129,25 @@ namespace Game.Spells } break; } + case Targets.DestNearbyDb: + { + var radiusBounds = spellEffectInfo.CalcRadiusBounds(m_caster, targetIndex, this); + List positionsInRange = new(); + foreach (var position in Global.SpellMgr.GetSpellTargetPositions(m_spellInfo.Id, spellEffectInfo.EffectIndex)) + if (m_caster.GetMapId() == position.GetMapId() && (radiusBounds == default || (!m_caster.IsInDist(position, radiusBounds.Item1) && m_caster.IsInDist(position, radiusBounds.Item2)))) + positionsInRange.Add(position); + + if (positionsInRange.Empty()) + { + Log.outDebug(LogFilter.Spells, $"SPELL: unknown target coordinates for spell ID {m_spellInfo.Id}"); + SendCastResult(SpellCastResult.NoValidTargets); + Finish(SpellCastResult.NoValidTargets); + return; + } + + dest = new(positionsInRange.SelectRandom().GetPosition()); + break; + } default: { float dist = spellEffectInfo.CalcRadius(m_caster, targetIndex); diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index 511dace5c..c3c996e45 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -4312,6 +4312,41 @@ namespace Game.Spells return radius; } + public (float, float) CalcRadiusBounds(WorldObject caster, SpellTargetIndex targetIndex, Spell spell) + { + // TargetA -> TargetARadiusEntry + // TargetB -> TargetBRadiusEntry + // Aura effects have TargetARadiusEntry == TargetBRadiusEntry (mostly) + SpellRadiusRecord entry = TargetARadiusEntry; + if (targetIndex == SpellTargetIndex.TargetB && HasRadius(targetIndex)) + entry = TargetBRadiusEntry; + + (float, float) bounds = default; + if (entry == null) + return bounds; + + bounds = (entry.RadiusMin, entry.RadiusMax); + + if (caster != null) + { + Player modOwner = caster.GetSpellModOwner(); + if (modOwner != null) + modOwner.ApplySpellMod(_spellInfo, SpellModOp.Radius, ref bounds.Item2, spell); + + if (!_spellInfo.HasAttribute(SpellAttr9.NoMovementRadiusBonus)) + { + Unit casterUnit = caster.ToUnit(); ; + if (casterUnit != null && Spell.CanIncreaseRangeByMovement(casterUnit)) + { + bounds.Item1 = Math.Max(bounds.Item1 - 2.0f, 0.0f); + bounds.Item2 += 2.0f; + } + } + } + + return bounds; + } + public SpellCastTargetFlags GetProvidedTargetMask() { return SpellInfo.GetTargetFlagMask(TargetA.GetObjectType()) | SpellInfo.GetTargetFlagMask(TargetB.GetObjectType()); @@ -5123,7 +5158,7 @@ namespace Game.Spells new StaticData(SpellTargetObjectTypes.Unit, SpellTargetReferenceTypes.Caster, SpellTargetSelectionCategories.Default, SpellTargetCheckTypes.Default, SpellTargetDirectionTypes.None), // 103 TARGET_UNIT_PASSENGER_7 new StaticData(SpellTargetObjectTypes.Unit, SpellTargetReferenceTypes.Dest, SpellTargetSelectionCategories.Cone, SpellTargetCheckTypes.Enemy, SpellTargetDirectionTypes.Front), // 104 TARGET_UNIT_CONE_CASTER_TO_DEST_ENEMY new StaticData(SpellTargetObjectTypes.Unit, SpellTargetReferenceTypes.Caster, SpellTargetSelectionCategories.Area, SpellTargetCheckTypes.Default, SpellTargetDirectionTypes.None), // 105 TARGET_UNIT_CASTER_AND_PASSENGERS - new StaticData(SpellTargetObjectTypes.Dest, SpellTargetReferenceTypes.Caster, SpellTargetSelectionCategories.Channel, SpellTargetCheckTypes.Default, SpellTargetDirectionTypes.None), // 106 TARGET_DEST_CHANNEL_CASTER + new StaticData(SpellTargetObjectTypes.Dest, SpellTargetReferenceTypes.Caster, SpellTargetSelectionCategories.Default, SpellTargetCheckTypes.Default, SpellTargetDirectionTypes.None), // 106 TARGET_DEST_NEARBY_DB new StaticData(SpellTargetObjectTypes.Dest, SpellTargetReferenceTypes.Caster, SpellTargetSelectionCategories.Nearby, SpellTargetCheckTypes.Entry, SpellTargetDirectionTypes.None), // 107 TARGET_DEST_NEARBY_ENTRY_2 new StaticData(SpellTargetObjectTypes.Gobj, SpellTargetReferenceTypes.Dest, SpellTargetSelectionCategories.Cone, SpellTargetCheckTypes.Enemy, SpellTargetDirectionTypes.Front), // 108 TARGET_GAMEOBJECT_CONE_CASTER_TO_DEST_ENEMY new StaticData(SpellTargetObjectTypes.Gobj, SpellTargetReferenceTypes.Dest, SpellTargetSelectionCategories.Cone, SpellTargetCheckTypes.Ally, SpellTargetDirectionTypes.Front), // 109 TARGET_GAMEOBJECT_CONE_CASTER_TO_DEST_ALLY diff --git a/Source/Game/Spells/SpellManager.cs b/Source/Game/Spells/SpellManager.cs index bfe55e3c8..83f964ce8 100644 --- a/Source/Game/Spells/SpellManager.cs +++ b/Source/Game/Spells/SpellManager.cs @@ -296,7 +296,12 @@ namespace Game.Entities public WorldLocation GetSpellTargetPosition(uint spell_id, uint effIndex) { - return mSpellTargetPositions.LookupByKey(new KeyValuePair(spell_id, effIndex)); + return mSpellTargetPositions.LookupByKey((spell_id, effIndex)).FirstOrDefault(); + } + + public List GetSpellTargetPositions(uint spell_id, uint effIndex) + { + return mSpellTargetPositions.LookupByKey((spell_id, effIndex)); } public List GetSpellSpellGroupMapBounds(uint spell_id) @@ -1006,21 +1011,20 @@ namespace Game.Entities mSpellTargetPositions.Clear(); // need for reload case - // 0 1 2 3 4 5 6 - SQLResult result = DB.World.Query("SELECT ID, EffectIndex, MapID, PositionX, PositionY, PositionZ, Orientation FROM spell_target_position"); + // 0 1 2 3 4 5 6 7 + SQLResult result = DB.World.Query("SELECT ID, EffectIndex, OrderIndex, MapID, PositionX, PositionY, PositionZ, Orientation FROM spell_target_position"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 spell target coordinates. DB table `spell_target_position` is empty."); return; } - uint count = 0; do { uint spellId = result.Read(0); uint effIndex = result.Read(1); - WorldLocation st = new(result.Read(2), result.Read(3), result.Read(4), result.Read(5)); + WorldLocation st = new(result.Read(3), result.Read(4), result.Read(5), result.Read(6)); var mapEntry = CliDB.MapStorage.LookupByKey(st.GetMapId()); if (mapEntry == null) @@ -1048,38 +1052,39 @@ namespace Game.Entities continue; } - if (!result.IsNull(6)) - st.SetOrientation(result.Read(6)); + SpellEffectInfo spellEffectInfo = spellInfo.GetEffect(effIndex); + if (!result.IsNull(7)) + st.SetOrientation(result.Read(7)); else { // target facing is in degrees for 6484 & 9268... - if (spellInfo.GetEffect(effIndex).PositionFacing > 2 * MathF.PI) - st.SetOrientation(spellInfo.GetEffect(effIndex).PositionFacing * MathF.PI / 180); + if (spellEffectInfo.PositionFacing > 2 * MathF.PI) + st.SetOrientation(spellEffectInfo.PositionFacing * MathF.PI / 180); else - st.SetOrientation(spellInfo.GetEffect(effIndex).PositionFacing); + st.SetOrientation(spellEffectInfo.PositionFacing); } - bool hasTarget(Targets target) + bool hasTarget(Targets target) => spellEffectInfo.TargetA.GetTarget() == target || spellEffectInfo.TargetB.GetTarget() == target; + + if (!hasTarget(Targets.DestNearbyDb)) { - SpellEffectInfo spellEffectInfo = spellInfo.GetEffect(effIndex); - return spellEffectInfo.TargetA.GetTarget() == target || spellEffectInfo.TargetB.GetTarget() == target; - } - - if (hasTarget(Targets.DestDb) || hasTarget(Targets.DestNearbyEntryOrDB)) - { - mSpellTargetPositions[(spellId, effIndex)] = st; - ++count; - } - else - { - Log.outError(LogFilter.Sql, "Spell (Id: {0}, effIndex: {1}) listed in `spell_target_position` does not have target TARGET_DEST_DB (17).", spellId, effIndex); - continue; + if (!hasTarget(Targets.DestDb) && !hasTarget(Targets.DestNearbyEntryOrDB)) + { + Log.outError(LogFilter.Sql, $"Spell (Id: {spellId}, effIndex: {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."); + continue; + } + if (result.Read(2) != 0) + { + Log.outError(LogFilter.Sql, $"Spell (Id: {spellId}, effIndex: {effIndex}) listed in `spell_target_position` does not have a target TARGET_DEST_NEARBY_DB but lists multiple points, only one is allowed."); + continue; + } } + mSpellTargetPositions.Add((spellId, effIndex), st); } while (result.NextRow()); - Log.outInfo(LogFilter.ServerLoading, "Loaded {0} spell teleport coordinates in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); + Log.outInfo(LogFilter.ServerLoading, $"Loaded {mSpellTargetPositions.Count} spell teleport coordinates in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); } public void LoadSpellGroups() @@ -4942,7 +4947,7 @@ namespace Game.Entities Dictionary mSpellLearnSkills = new(); MultiMap mSpellLearnSpells = new(); MultiMap mSpellLearnedBySpells = new(); - Dictionary<(uint, uint), WorldLocation> mSpellTargetPositions = new(); + MultiMap<(uint, uint), WorldLocation> mSpellTargetPositions = new(); MultiMap mSpellSpellGroup = new(); MultiMap mSpellGroupSpell = new(); Dictionary mSpellGroupStack = new();