From af8a76387c6518f56c1f2ad6b0ee7755dc3698a1 Mon Sep 17 00:00:00 2001 From: Hondacrx Date: Sun, 8 Jun 2025 18:18:22 -0400 Subject: [PATCH] Core/Vehicle: Added RideSpellID field to override npc_spellclick_spells Port From (https://github.com/TrinityCore/TrinityCore/commit/46251b56553b610588eea89b83b4b4e8b88e47da) --- Source/Game/Entities/Unit/Unit.Spells.cs | 154 +++++++++++++---------- Source/Game/Entities/Vehicle.cs | 37 +++--- Source/Game/Globals/ObjectManager.cs | 36 +++++- 3 files changed, 139 insertions(+), 88 deletions(-) diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index 0be643ada..b0700ec2f 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -1497,7 +1497,7 @@ namespace Game.Entities // // We need to be immune to all types return (schoolImmunityMask & schoolMask) == schoolMask; - }; + } // If m_immuneToSchool type contain this school type, IMMUNE damage. if (hasImmunity(m_spellImmune[(int)SpellImmunity.School])) @@ -2745,72 +2745,8 @@ namespace Game.Entities if (!Global.ConditionMgr.IsObjectMeetingSpellClickConditions(spellClickEntry, clickInfo.spellId, clicker, this)) continue; - Unit caster = Convert.ToBoolean(clickInfo.castFlags & (byte)SpellClickCastFlags.CasterClicker) ? clicker : this; - Unit target = Convert.ToBoolean(clickInfo.castFlags & (byte)SpellClickCastFlags.TargetClicker) ? clicker : this; - ObjectGuid origCasterGUID = Convert.ToBoolean(clickInfo.castFlags & (byte)SpellClickCastFlags.OrigCasterOwner) ? GetOwnerGUID() : clicker.GetGUID(); - - SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(clickInfo.spellId, caster.GetMap().GetDifficultyID()); + spellClickHandled = HandleSpellClick(clicker, seatId, clickInfo.spellId, flags, clickInfo); // if (!spellEntry) should be checked at npc_spellclick load - - SpellCastResult castResult = SpellCastResult.Success; - if (seatId > -1) - { - byte i = 0; - bool valid = false; - foreach (var spellEffectInfo in spellEntry.GetEffects()) - { - if (spellEffectInfo.ApplyAuraName == AuraType.ControlVehicle) - { - valid = true; - break; - } - ++i; - } - - if (!valid) - { - Log.outError(LogFilter.Sql, "Spell {0} specified in npc_spellclick_spells is not a valid vehicle enter aura!", clickInfo.spellId); - continue; - } - - if (IsInMap(caster)) - { - CastSpellExtraArgs args = new(flags); - args.OriginalCaster = origCasterGUID; - args.AddSpellMod(SpellValueMod.BasePoint0 + i, seatId + 1); - castResult = caster.CastSpell(target, clickInfo.spellId, args); - } - else // This can happen during Player._LoadAuras - { - int[] bp = new int[SpellConst.MaxEffects]; - foreach (var spellEffectInfo in spellEntry.GetEffects()) - bp[spellEffectInfo.EffectIndex] = (int)spellEffectInfo.BasePoints; - - bp[i] = seatId; - - AuraCreateInfo createInfo = new(ObjectGuid.Create(HighGuid.Cast, SpellCastSource.Normal, GetMapId(), spellEntry.Id, GetMap().GenerateLowGuid(HighGuid.Cast)), spellEntry, GetMap().GetDifficultyID(), SpellConst.MaxEffectMask, this); - createInfo.SetCaster(clicker); - createInfo.SetBaseAmount(bp); - createInfo.SetCasterGUID(origCasterGUID); - - Aura.TryRefreshStackOrCreate(createInfo); - } - } - else - { - if (IsInMap(caster)) - castResult = caster.CastSpell(target, spellEntry.Id, new CastSpellExtraArgs().SetOriginalCaster(origCasterGUID)); - else - { - AuraCreateInfo createInfo = new(ObjectGuid.Create(HighGuid.Cast, SpellCastSource.Normal, GetMapId(), spellEntry.Id, GetMap().GenerateLowGuid(HighGuid.Cast)), spellEntry, GetMap().GetDifficultyID(), SpellConst.MaxEffectMask, this); - createInfo.SetCaster(clicker); - createInfo.SetCasterGUID(origCasterGUID); - - Aura.TryRefreshStackOrCreate(createInfo); - } - } - - spellClickHandled = castResult == SpellCastResult.Success; } Creature creature = ToCreature(); @@ -2818,6 +2754,92 @@ namespace Game.Entities creature.GetAI().OnSpellClick(clicker, ref spellClickHandled); } + public bool HandleSpellClick(Unit clicker, sbyte seatId, uint spellId, TriggerCastFlags flags = TriggerCastFlags.None, SpellClickInfo spellClickInfo = null) + { + Unit caster = clicker; + Unit target = this; + ObjectGuid origCasterGUID = caster.GetGUID(); + SpellCastResult castResult = SpellCastResult.Success; + + if (spellClickInfo != null) + { + caster = (spellClickInfo.castFlags & (byte)SpellClickCastFlags.CasterClicker) != 0 ? clicker : this; + target = (spellClickInfo.castFlags & (byte)SpellClickCastFlags.TargetClicker) != 0 ? clicker : this; + origCasterGUID = (spellClickInfo.castFlags & (byte)SpellClickCastFlags.OrigCasterOwner) != 0 ? GetOwnerGUID() : clicker.GetGUID(); + } + + if (spellId == 0) + { + Log.outError(LogFilter.Sql, $"No valid spell specified for clickee {target.GetGUID()} and clicker {caster.GetGUID()}!"); + return false; + } + + SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(spellId, caster.GetMap().GetDifficultyID()); + + byte effectIndex = 0; + bool hasControlVehicleAura = false; + foreach (SpellEffectInfo spellEffectInfo in spellEntry.GetEffects()) + { + if (spellEffectInfo.ApplyAuraName == AuraType.ControlVehicle) + { + hasControlVehicleAura = true; + break; + } + + ++effectIndex; + } + + if (seatId > -1) + { + if (!hasControlVehicleAura) + { + if (spellClickInfo == null) + Log.outError(LogFilter.Sql, $"RideSpell {spellId} specified in vehicle_accessory or vehicle_template_accessory is not a valid vehicle enter aura!"); + else + Log.outError(LogFilter.Sql, $"Spell {spellId} specified in npc_spellclick_spells is not a valid vehicle enter aura!"); + return false; + } + + if (IsInMap(caster)) + { + CastSpellExtraArgs args = new(flags); + args.OriginalCaster = origCasterGUID; + args.AddSpellMod(SpellValueMod.BasePoint0 + effectIndex, seatId + 1); + castResult = caster.CastSpell(target, spellId, args); + } + else // This can happen during Player::_LoadAuras + { + int[] bp = new int[SpellConst.MaxEffects]; + foreach (SpellEffectInfo spellEffectInfo in spellEntry.GetEffects()) + bp[spellEffectInfo.EffectIndex] = (int)spellEffectInfo.BasePoints; + + bp[effectIndex] = seatId; + + AuraCreateInfo createInfo = new(ObjectGuid.Create(HighGuid.Cast, SpellCastSource.Normal, GetMapId(), spellEntry.Id, GetMap().GenerateLowGuid(HighGuid.Cast)), spellEntry, GetMap().GetDifficultyID(), SpellConst.MaxEffectMask, this); + createInfo.SetCaster(clicker); + createInfo.SetBaseAmount(bp); + createInfo.SetCasterGUID(origCasterGUID); + + Aura.TryRefreshStackOrCreate(createInfo); + } + } + else + { + if (IsInMap(caster)) + castResult = caster.CastSpell(target, spellEntry.Id, new CastSpellExtraArgs().SetOriginalCaster(origCasterGUID)); + else + { + AuraCreateInfo createInfo = new(ObjectGuid.Create(HighGuid.Cast, SpellCastSource.Normal, GetMapId(), spellEntry.Id, GetMap().GenerateLowGuid(HighGuid.Cast)), spellEntry, GetMap().GetDifficultyID(), SpellConst.MaxEffectMask, this); + createInfo.SetCaster(clicker); + createInfo.SetCasterGUID(origCasterGUID); + + + Aura.TryRefreshStackOrCreate(createInfo); + } + } + return castResult == SpellCastResult.Success; + } + public bool HasAura(uint spellId, ObjectGuid casterGUID = default, ObjectGuid itemCasterGUID = default, uint reqEffMask = 0) { return GetAuraApplication(spellId, casterGUID, itemCasterGUID, reqEffMask) != null; diff --git a/Source/Game/Entities/Vehicle.cs b/Source/Game/Entities/Vehicle.cs index 34cf78614..5e66d0649 100644 --- a/Source/Game/Entities/Vehicle.cs +++ b/Source/Game/Entities/Vehicle.cs @@ -42,7 +42,7 @@ namespace Game.Entities // Set or remove correct flags based on available seats. Will overwrite db data (if wrong). if (UsableSeatNum != 0) _me.SetNpcFlag(_me.IsTypeId(TypeId.Player) ? NPCFlags.PlayerVehicle : NPCFlags.SpellClick); - else if(unit.m_unitData.InteractSpellID == 0) + else if (unit.m_unitData.InteractSpellID == 0) _me.RemoveNpcFlag(_me.IsTypeId(TypeId.Player) ? NPCFlags.PlayerVehicle : NPCFlags.SpellClick); InitMovementInfoForBase(); @@ -74,7 +74,7 @@ namespace Game.Entities foreach (var acc in accessories) if (!evading || acc.IsMinion) // only install minions on evade mode - InstallAccessory(acc.AccessoryEntry, acc.SeatId, acc.IsMinion, acc.SummonedType, acc.SummonTime); + InstallAccessory(acc.AccessoryEntry, acc.SeatId, acc.IsMinion, acc.SummonedType, acc.SummonTime, acc.RideSpellID); } public void Uninstall() @@ -260,11 +260,11 @@ namespace Game.Entities return null; } - - void InstallAccessory(uint entry, sbyte seatId, bool minion, byte type, uint summonTime) + + void InstallAccessory(uint entry, sbyte seatId, bool minion, byte type, uint summonTime, uint? rideSpellId = null) { // @Prevent adding accessories when vehicle is uninstalling. (Bad script in OnUninstall/OnRemovePassenger/PassengerBoarded hook.) - + if (_status == Status.UnInstalling) { Log.outError(LogFilter.Vehicle, "Vehicle ({0}, Entry: {1}) attempts to install accessory (Entry: {2}) on seat {3} with STATUS_UNINSTALLING! " + @@ -280,7 +280,10 @@ namespace Game.Entities if (minion) accessory.AddUnitTypeMask(UnitTypeMask.Accessory); - _me.HandleSpellClick(accessory, seatId); + if (rideSpellId.HasValue) + _me.HandleSpellClick(accessory, seatId, rideSpellId.Value); + else + _me.HandleSpellClick(accessory, seatId); // If for some reason adding accessory to vehicle fails it will unsummon in // @VehicleJoinEvent.Abort @@ -362,7 +365,7 @@ namespace Game.Entities var seat = GetSeatKeyValuePairForPassenger(unit); Cypher.Assert(seat.Value != null); - Log.outDebug( LogFilter.Vehicle, "Unit {0} exit vehicle entry {1} id {2} dbguid {3} seat {4}", + Log.outDebug(LogFilter.Vehicle, "Unit {0} exit vehicle entry {1} id {2} dbguid {3} seat {4}", unit.GetName(), _me.GetEntry(), _vehicleInfo.Id, _me.GetGUID().ToString(), seat.Key); if (seat.Value.SeatInfo.CanEnterOrExit() && ++UsableSeatNum != 0) @@ -392,7 +395,7 @@ namespace Game.Entities _me.ToCreature().GetAI().PassengerBoarded(unit, seat.Key, false); if (GetBase().IsTypeId(TypeId.Unit)) - Global.ScriptMgr.OnRemovePassenger(this, unit); + Global.ScriptMgr.OnRemovePassenger(this, unit); unit.SetVehicle(null); return this; @@ -441,7 +444,7 @@ namespace Game.Entities return false; } - + void InitMovementInfoForBase() { VehicleFlags vehicleFlags = (VehicleFlags)GetVehicleInfo().Flags; @@ -491,11 +494,11 @@ namespace Game.Entities public float GetTransportOrientation() { return GetBase().GetOrientation(); } public void AddPassenger(WorldObject passenger) { Log.outFatal(LogFilter.Vehicle, "Vehicle cannot directly gain passengers without auras"); } - + public void CalculatePassengerPosition(ref float x, ref float y, ref float z, ref float o) { - ITransport.CalculatePassengerPosition(ref x, ref y, ref z, ref o, - GetBase().GetPositionX(), GetBase().GetPositionY(), + ITransport.CalculatePassengerPosition(ref x, ref y, ref z, ref o, + GetBase().GetPositionX(), GetBase().GetPositionY(), GetBase().GetPositionZ(), GetBase().GetOrientation()); } @@ -507,7 +510,7 @@ namespace Game.Entities } public int GetMapIdForSpawning() { return (int)GetBase().GetMapId(); } - + public void RemovePendingEvent(VehicleJoinEvent e) { foreach (var Event in _pendingJoinEvents) @@ -535,7 +538,7 @@ namespace Game.Entities public void RemovePendingEventsForPassenger(Unit passenger) { - for (var i = 0; i< _pendingJoinEvents.Count; ++i) + for (var i = 0; i < _pendingJoinEvents.Count; ++i) { var joinEvent = _pendingJoinEvents[i]; if (joinEvent.Passenger == passenger) @@ -556,7 +559,7 @@ namespace Game.Entities } return false; } - + public TimeSpan GetDespawnDelay() { VehicleTemplate vehicleTemplate = Global.ObjectMgr.GetVehicleTemplate(this); @@ -797,19 +800,21 @@ namespace Game.Entities public struct VehicleAccessory { - public VehicleAccessory(uint entry, sbyte seatId, bool isMinion, byte summonType, uint summonTime) + public VehicleAccessory(uint entry, sbyte seatId, bool isMinion, byte summonType, uint summonTime, uint? rideSpellID) { AccessoryEntry = entry; IsMinion = isMinion; SummonTime = summonTime; SeatId = seatId; SummonedType = summonType; + RideSpellID = rideSpellID; } public uint AccessoryEntry; public bool IsMinion; public uint SummonTime; public sbyte SeatId; public byte SummonedType; + public uint? RideSpellID; } public class VehicleTemplate diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index 71697d7f4..14591322d 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -11404,8 +11404,8 @@ namespace Game uint count = 0; - // 0 1 2 3 4 5 - SQLResult result = DB.World.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_template_accessory`"); + // 0 1 2 3 4 5 6 + SQLResult result = DB.World.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer`, `RideSpellID` FROM `vehicle_template_accessory`"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty."); @@ -11421,6 +11421,18 @@ namespace Game byte summonType = result.Read(4); uint summonTimer = result.Read(5); + uint? rideSpellId = null; + if (!result.IsNull(6)) + { + rideSpellId = result.Read(6); + + if (!Global.SpellMgr.HasSpellInfo(rideSpellId.Value, Difficulty.None)) + { + Log.outError(LogFilter.Sql, $"Table `vehicle_template_accessory`: rideSpellId {rideSpellId} does not exist for entry {entry}."); + continue; + } + } + if (GetCreatureTemplate(entry) == null) { Log.outError(LogFilter.Sql, "Table `vehicle_template_accessory`: creature template entry {0} does not exist.", entry); @@ -11439,7 +11451,7 @@ namespace Game continue; } - _vehicleTemplateAccessoryStore.Add(entry, new VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer)); + _vehicleTemplateAccessoryStore.Add(entry, new VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer, rideSpellId)); ++count; } @@ -11455,8 +11467,8 @@ namespace Game uint count = 0; - // 0 1 2 3 4 5 - SQLResult result = DB.World.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_accessory`"); + // 0 1 2 3 4 5 6 + SQLResult result = DB.World.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer`, `RideSpellID` FROM `vehicle_accessory`"); if (result.IsEmpty()) { @@ -11473,13 +11485,25 @@ namespace Game byte uiSummonType = result.Read(4); uint uiSummonTimer = result.Read(5); + uint? rideSpellId = null; + if (!result.IsNull(6)) + { + rideSpellId = result.Read(6); + + if (!Global.SpellMgr.HasSpellInfo(rideSpellId.Value, Difficulty.None)) + { + Log.outError(LogFilter.Sql, $"Table `vehicle_accessory`: rideSpellId {rideSpellId} does not exist for guid {uiGUID}."); + continue; + } + } + if (GetCreatureTemplate(uiAccessory) == null) { Log.outError(LogFilter.Sql, "Table `vehicle_accessory`: Accessory {0} does not exist.", uiAccessory); continue; } - _vehicleAccessoryStore.Add(uiGUID, new VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer)); + _vehicleAccessoryStore.Add(uiGUID, new VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer, rideSpellId)); ++count; }