From 90ee61ae2b54a9f205d5b6c72593dcdb9b77b389 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Sun, 26 Aug 2018 01:08:24 -0400 Subject: [PATCH] Core/AreaTrigger: handle AREATRIGGER_FLAG_HAS_CIRCULAR_MOVEMENT --- .../Framework/Constants/AreaTriggerConst.cs | 2 +- .../DataStorage/AreaTriggerDataStorage.cs | 58 ++++++++ Source/Game/DataStorage/Structs/A_Records.cs | 14 +- .../Game/Entities/AreaTrigger/AreaTrigger.cs | 128 +++++++++++++++++- .../AreaTrigger/AreaTriggerTemplate.cs | 40 ++++++ Source/Game/Entities/Object/Position.cs | 7 + Source/Game/Entities/Object/WorldObject.cs | 28 +--- Source/Game/Maps/Map.cs | 1 + .../Network/Packets/AreaTriggerPackets.cs | 45 +----- .../world/master/2018_08_05_00_world.sql | 13 ++ 10 files changed, 260 insertions(+), 76 deletions(-) create mode 100644 sql/updates/world/master/2018_08_05_00_world.sql diff --git a/Source/Framework/Constants/AreaTriggerConst.cs b/Source/Framework/Constants/AreaTriggerConst.cs index 7a726a2e7..3c9509ac5 100644 --- a/Source/Framework/Constants/AreaTriggerConst.cs +++ b/Source/Framework/Constants/AreaTriggerConst.cs @@ -29,7 +29,7 @@ namespace Framework.Constants Unk2 = 0x080, Unk3 = 0x100, Unk4 = 0x200, - Unk5 = 0x400 + HasCircularMovement = 0x400 } public enum AreaTriggerTypes diff --git a/Source/Game/DataStorage/AreaTriggerDataStorage.cs b/Source/Game/DataStorage/AreaTriggerDataStorage.cs index 1f2f44af0..be6dd3da3 100644 --- a/Source/Game/DataStorage/AreaTriggerDataStorage.cs +++ b/Source/Game/DataStorage/AreaTriggerDataStorage.cs @@ -200,6 +200,64 @@ namespace Game.DataStorage Log.outInfo(LogFilter.ServerLoading, "Loaded 0 Spell AreaTrigger templates. DB table `spell_areatrigger` is empty."); } + // 0 1 2 3 4 5 6 7 + SQLResult circularMovementInfos = DB.World.Query("SELECT SpellMiscId, StartDelay, CircleRadius, BlendFromRadius, InitialAngle, ZOffset, CounterClockwise, CanLoop FROM `spell_areatrigger_circular` ORDER BY `SpellMiscId`"); + if (!circularMovementInfos.IsEmpty()) + { + do + { + uint spellMiscId = circularMovementInfos.Read(0); + + var atSpellMisc = _areaTriggerTemplateSpellMisc.LookupByKey(spellMiscId); + if (atSpellMisc == null) + { + Log.outError(LogFilter.Sql, $"Table `spell_areatrigger_circular` reference invalid SpellMiscId {spellMiscId}"); + continue; + } + + AreaTriggerCircularMovementInfo circularMovementInfo = new AreaTriggerCircularMovementInfo(); + + circularMovementInfo.StartDelay = circularMovementInfos.Read(1); + circularMovementInfo.Radius = circularMovementInfos.Read(2); + if (!float.IsInfinity(circularMovementInfo.Radius)) + { + Log.outError(LogFilter.Sql, $"Table `spell_areatrigger_circular` has listed areatrigger (MiscId: {spellMiscId}) with invalid Radius ({circularMovementInfo.Radius}), set to 0!"); + circularMovementInfo.Radius = 0.0f; + } + + circularMovementInfo.BlendFromRadius = circularMovementInfos.Read(3); + if (!float.IsInfinity(circularMovementInfo.BlendFromRadius)) + { + Log.outError(LogFilter.Sql, $"Table `spell_areatrigger_circular` has listed areatrigger (MiscId: {spellMiscId}) with invalid BlendFromRadius ({circularMovementInfo.BlendFromRadius}), set to 0!"); + circularMovementInfo.BlendFromRadius = 0.0f; + } + + circularMovementInfo.InitialAngle = circularMovementInfos.Read(4); + if (!float.IsInfinity(circularMovementInfo.InitialAngle)) + { + Log.outError(LogFilter.Sql, $"Table `spell_areatrigger_circular` has listed areatrigger (MiscId: {spellMiscId}) with invalid InitialAngle ({circularMovementInfo.InitialAngle}), set to 0!"); + circularMovementInfo.InitialAngle = 0.0f; + } + + circularMovementInfo.ZOffset = circularMovementInfos.Read(5); + if (!float.IsInfinity(circularMovementInfo.ZOffset)) + { + Log.outError(LogFilter.Sql, $"Table `spell_areatrigger_circular` has listed areatrigger (MiscId: {spellMiscId}) with invalid ZOffset ({circularMovementInfo.ZOffset}), set to 0!"); + circularMovementInfo.ZOffset = 0.0f; + } + + circularMovementInfo.CounterClockwise = circularMovementInfos.Read(6); + circularMovementInfo.CanLoop = circularMovementInfos.Read(7); + + atSpellMisc.CircularMovementInfo = circularMovementInfo; + } + while (circularMovementInfos.NextRow()); + } + else + { + Log.outInfo(LogFilter.ServerLoading, "Loaded 0 AreaTrigger templates circular movement infos. DB table `spell_areatrigger_circular` is empty."); + } + Log.outInfo(LogFilter.ServerLoading, "Loaded {0} spell areatrigger templates in {1} ms.", _areaTriggerTemplateStore.Count, Time.GetMSTimeDiffToNow(oldMSTime)); } diff --git a/Source/Game/DataStorage/Structs/A_Records.cs b/Source/Game/DataStorage/Structs/A_Records.cs index b9977e40f..f739608f4 100644 --- a/Source/Game/DataStorage/Structs/A_Records.cs +++ b/Source/Game/DataStorage/Structs/A_Records.cs @@ -217,11 +217,11 @@ namespace Game.DataStorage public class ArtifactTierRecord { public uint ID; - public uint ArtifactTier; - public uint MaxNumTraits; - public uint MaxArtifactKnowledge; - public uint KnowledgePlayerCondition; - public uint MinimumEmpowerKnowledge; + public byte ArtifactTier; + public byte MaxNumTraits; + public byte MaxArtifactKnowledge; + public byte KnowledgePlayerCondition; + public byte MinimumEmpowerKnowledge; } public class ArtifactUnlockRecord @@ -230,8 +230,8 @@ namespace Game.DataStorage public ushort ItemBonusListID; public byte PowerRank; public uint PowerID; - public uint PlayerConditionID; - public byte ArtifactID; + public ushort PlayerConditionID; + public uint ArtifactID; } public sealed class AuctionHouseRecord diff --git a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs index 323ee72e5..8b16313f5 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs @@ -25,6 +25,7 @@ using Game.Spells; using System; using System.Collections.Generic; using Game; +using Framework.Dynamic; namespace Game.Entities { @@ -135,9 +136,20 @@ namespace Game.Entities UpdateShape(); - if (GetMiscTemplate().HasSplines()) + uint timeToTarget = GetMiscTemplate().TimeToTarget != 0 ? GetMiscTemplate().TimeToTarget : GetUInt32Value(AreaTriggerFields.Duration); + + if (GetTemplate().HasFlag(AreaTriggerFlags.HasCircularMovement)) + { + AreaTriggerCircularMovementInfo cmi = GetMiscTemplate().CircularMovementInfo; + if (target && GetTemplate().HasFlag(AreaTriggerFlags.HasAttached)) + cmi.TargetGUID.Set(target.GetGUID()); + else + cmi.Center.Set(new Vector3(pos.posX, pos.posY, pos.posZ)); + + InitCircularMovement(cmi, timeToTarget); + } + else if (GetMiscTemplate().HasSplines()) { - uint timeToTarget = GetMiscTemplate().TimeToTarget != 0 ? GetMiscTemplate().TimeToTarget : GetUInt32Value(AreaTriggerFields.Duration); InitSplineOffsets(GetMiscTemplate().SplinePoints, timeToTarget); } @@ -156,6 +168,10 @@ namespace Game.Entities AI_Initialize(); + // Relocate areatriggers with circular movement again + if (HasCircularMovement()) + Relocate(CalculateCircularMovementPosition()); + if (!GetMap().AddToMap(this)) { // Returning false will cause the object to be deleted - remove from transport if (transport) @@ -175,7 +191,12 @@ namespace Game.Entities base.Update(diff); _timeSinceCreated += diff; - if (GetTemplate().HasFlag(AreaTriggerFlags.HasAttached)) + // "If" order matter here, Circular Movement > Attached > Splines + if (HasCircularMovement()) + { + UpdateCircularMovementPosition(diff); + } + else if(GetTemplate().HasFlag(AreaTriggerFlags.HasAttached)) { Unit target = GetTarget(); if (target) @@ -620,6 +641,103 @@ namespace Game.Entities _reachedDestination = false; } + void InitCircularMovement(AreaTriggerCircularMovementInfo cmi, uint timeToTarget) + { + // Circular movement requires either a center position or an attached unit + Cypher.Assert(cmi.Center.HasValue || cmi.TargetGUID.HasValue); + + // should be sent in object create packets only + updateValues[(int)AreaTriggerFields.TimeToTarget].UnsignedValue = timeToTarget; + + _circularMovementInfo.Set(cmi); + + _circularMovementInfo.Value.TimeToTarget = timeToTarget; + _circularMovementInfo.Value.ElapsedTimeForMovement = 0; + + if (IsInWorld) + { + AreaTriggerReShape reshape = new AreaTriggerReShape(); + reshape.TriggerGUID = GetGUID(); + reshape.AreaTriggerCircularMovement = _circularMovementInfo; + + SendMessageToSet(reshape, true); + } + } + + public bool HasCircularMovement() + { + return _circularMovementInfo.HasValue; + } + + Position GetCircularMovementCenterPosition() + { + if (_circularMovementInfo.HasValue) + return null; + + if (_circularMovementInfo.Value.TargetGUID.HasValue) + { + WorldObject center = Global.ObjAccessor.GetWorldObject(this, _circularMovementInfo.Value.TargetGUID.Value); + if (center) + return center; + } + + if (_circularMovementInfo.Value.Center.HasValue) + return new Position(_circularMovementInfo.Value.Center.Value); + + return null; + } + + Position CalculateCircularMovementPosition() + { + Position centerPos = GetCircularMovementCenterPosition(); + if (centerPos == null) + return GetPosition(); + + AreaTriggerCircularMovementInfo cmi = _circularMovementInfo.Value; + + // AreaTrigger make exactly "Duration / TimeToTarget" loops during his life time + float pathProgress = (float)cmi.ElapsedTimeForMovement / cmi.TimeToTarget; + + // We already made one circle and can't loop + if (!cmi.CanLoop) + pathProgress = Math.Min(1.0f, pathProgress); + + float radius = cmi.Radius; + if (MathFunctions.fuzzyNe(cmi.BlendFromRadius, radius)) + { + float blendCurve = (cmi.BlendFromRadius - radius) / radius; + // 4.f Defines four quarters + blendCurve = MathFunctions.RoundToInterval(ref blendCurve, 1.0f, 4.0f) / 4.0f; + float blendProgress = Math.Min(1.0f, pathProgress / blendCurve); + radius = MathFunctions.lerp(cmi.BlendFromRadius, cmi.Radius, blendProgress); + } + + // Adapt Path progress depending of circle direction + if (!cmi.CounterClockwise) + pathProgress *= -1; + + float angle = cmi.InitialAngle + 2.0f * (float)Math.PI * pathProgress; + float x = centerPos.GetPositionX() + (radius * (float)Math.Cos(angle)); + float y = centerPos.GetPositionY() + (radius * (float)Math.Sin(angle)); + float z = centerPos.GetPositionZ() + cmi.ZOffset; + + return new Position(x, y, z, angle); + } + + void UpdateCircularMovementPosition(uint diff) + { + if (_circularMovementInfo.Value.StartDelay > GetElapsedTimeForMovement()) + return; + + _circularMovementInfo.Value.ElapsedTimeForMovement = (int)(GetElapsedTimeForMovement() - _circularMovementInfo.Value.StartDelay); + + Position pos = CalculateCircularMovementPosition(); + + GetMap().AreaTriggerRelocation(this, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); + + DebugVisualizePosition(); + } + void UpdateSplinePosition(uint diff) { if (_reachedDestination) @@ -742,6 +860,8 @@ namespace Game.Entities public Spline GetSpline() { return _spline; } public uint GetElapsedTimeForMovement() { return GetTimeSinceCreated(); } // @todo: research the right value, in sniffs both timers are nearly identical + public Optional GetCircularMovementInfo() { return _circularMovementInfo; } + ObjectGuid _targetGuid; AuraEffect _aurEff; @@ -761,6 +881,8 @@ namespace Game.Entities int _lastSplineIndex; uint _movementTime; + Optional _circularMovementInfo; + AreaTriggerMiscTemplate _areaTriggerMiscTemplate; List _insideUnits = new List(); diff --git a/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs b/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs index dc7ef2787..b2cb2d4cc 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs @@ -20,6 +20,8 @@ using Framework.GameMath; using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using Framework.Dynamic; +using Game.Network; namespace Game.Entities { @@ -114,6 +116,43 @@ namespace Game.Entities } } + public struct AreaTriggerCircularMovementInfo + { + public void Write(WorldPacket data) + { + data.WriteBit(TargetGUID.HasValue); + data.WriteBit(Center.HasValue); + data.WriteBit(CounterClockwise); + data.WriteBit(CanLoop); + + data.WriteUInt32(TimeToTarget); + data.WriteInt32(ElapsedTimeForMovement); + data.WriteUInt32(StartDelay); + data.WriteFloat(Radius); + data.WriteFloat(BlendFromRadius); + data.WriteFloat(InitialAngle); + data.WriteFloat(ZOffset); + + if (TargetGUID.HasValue) + data.WritePackedGuid(TargetGUID.Value); + + if (Center.HasValue) + data.WriteVector3(Center.Value); + } + + public Optional TargetGUID; + public Optional Center; + public bool CounterClockwise; + public bool CanLoop; + public uint TimeToTarget; + public int ElapsedTimeForMovement; + public uint StartDelay; + public float Radius; + public float BlendFromRadius; + public float InitialAngle; + public float ZOffset; + } + public class AreaTriggerTemplate : AreaTriggerData { public unsafe void InitMaxSearchRadius() @@ -195,6 +234,7 @@ namespace Game.Entities public uint TimeToTargetScale; public AreaTriggerScaleInfo ScaleInfo = new AreaTriggerScaleInfo(); + public AreaTriggerCircularMovementInfo CircularMovementInfo; public AreaTriggerTemplate Template; public List SplinePoints = new List(); diff --git a/Source/Game/Entities/Object/Position.cs b/Source/Game/Entities/Object/Position.cs index 5ea214500..a157662d6 100644 --- a/Source/Game/Entities/Object/Position.cs +++ b/Source/Game/Entities/Object/Position.cs @@ -31,6 +31,13 @@ namespace Game.Entities Orientation = o; } + public Position(Vector3 vector) + { + posX = vector.X; + posY = vector.Y; + posZ = vector.Z; + } + public float GetPositionX() { return posX; diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index cc9133dcf..7bd80cc37 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -516,7 +516,7 @@ namespace Game.Entities bool hasAreaTriggerPolygon = areaTriggerTemplate.IsPolygon(); bool hasAreaTriggerCylinder = areaTriggerTemplate.IsCylinder(); bool hasAreaTriggerSpline = areaTrigger.HasSplines(); - bool hasAreaTriggerUnkType = false; // areaTriggerTemplate.HasFlag(AREATRIGGER_FLAG_UNK5); + bool hasCircularMovement = areaTrigger.HasCircularMovement(); data.WriteBit(hasAbsoluteOrientation); data.WriteBit(hasDynamicShape); @@ -537,7 +537,7 @@ namespace Game.Entities data.WriteBit(hasAreaTriggerPolygon); data.WriteBit(hasAreaTriggerCylinder); data.WriteBit(hasAreaTriggerSpline); - data.WriteBit(hasAreaTriggerUnkType); + data.WriteBit(hasCircularMovement); if (hasUnk3) data.WriteBit(0); @@ -623,28 +623,8 @@ namespace Game.Entities data.WriteFloat(areaTriggerTemplate.CylinderDatas.LocationZOffsetTarget); } - if (hasAreaTriggerUnkType) - { - /*packet.ResetBitReader(); - var unk1 = packet.ReadBit("AreaTriggerUnk1"); - var hasCenter = packet.ReadBit("HasCenter", index); - packet.ReadBit("Unk bit 703 1", index); - packet.ReadBit("Unk bit 703 2", index); - - packet.ReadUInt32(); - packet.ReadInt32(); - packet.ReadUInt32(); - packet.ReadSingle("Radius", index); - packet.ReadSingle("BlendFromRadius", index); - packet.ReadSingle("InitialAngel", index); - packet.ReadSingle("ZOffset", index); - - if (unk1) - packet.ReadPackedGuid128("AreaTriggerUnkGUID", index); - - if (hasCenter) - packet.ReadVector3("Center", index);*/ - } + if (hasCircularMovement) + areaTrigger.GetCircularMovementInfo().Value.Write(data); } if (HasGameObject) diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 929116597..c61637979 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -1284,6 +1284,7 @@ namespace Game.Maps { // update pos at.Relocate(at._newPosition); + at.UpdateShape(); at.UpdateObjectVisibility(false); } else diff --git a/Source/Game/Network/Packets/AreaTriggerPackets.cs b/Source/Game/Network/Packets/AreaTriggerPackets.cs index 27f1a8a19..dde7695fa 100644 --- a/Source/Game/Network/Packets/AreaTriggerPackets.cs +++ b/Source/Game/Network/Packets/AreaTriggerPackets.cs @@ -85,18 +85,18 @@ namespace Game.Network.Packets _worldPacket .WritePackedGuid( TriggerGUID); _worldPacket.WriteBit(AreaTriggerSpline.HasValue); - _worldPacket.WriteBit(AreaTriggerUnkType.HasValue); + _worldPacket.WriteBit(AreaTriggerCircularMovement.HasValue); _worldPacket.FlushBits(); if (AreaTriggerSpline.HasValue) AreaTriggerSpline.Value.Write(_worldPacket); - if (AreaTriggerUnkType.HasValue) - AreaTriggerUnkType.Value.Write(_worldPacket); + if (AreaTriggerCircularMovement.HasValue) + AreaTriggerCircularMovement.Value.Write(_worldPacket); } public Optional AreaTriggerSpline; - public Optional AreaTriggerUnkType; + public Optional AreaTriggerCircularMovement; public ObjectGuid TriggerGUID; } @@ -119,41 +119,4 @@ namespace Game.Network.Packets public uint ElapsedTimeForMovement; public List Points = new List(); } - - struct AreaTriggerUnkTypeInfo - { - public void Write(WorldPacket data) - { - data.WriteBit(AreaTriggerUnkGUID.HasValue); - data.WriteBit(Center.HasValue); - data.WriteBit(UnkBit1); - data.WriteBit(UnkBit2); - - data.WriteUInt32(UnkUInt1); - data.WriteInt32(UnkInt1); - data.WriteUInt32(UnkUInt2); - data.WriteFloat(Radius); - data.WriteFloat(BlendFromRadius); - data.WriteFloat(InitialAngel); - data.WriteFloat(ZOffset); - - if (AreaTriggerUnkGUID.HasValue) - data.WritePackedGuid(AreaTriggerUnkGUID.Value); - - if (Center.HasValue) - data.WriteVector3(Center.Value); - } - - public Optional AreaTriggerUnkGUID; - public Optional Center; - public bool UnkBit1; - public bool UnkBit2; - public uint UnkUInt1; - public int UnkInt1; - public uint UnkUInt2; - public float Radius; - public float BlendFromRadius; - public float InitialAngel; - public float ZOffset; - } } diff --git a/sql/updates/world/master/2018_08_05_00_world.sql b/sql/updates/world/master/2018_08_05_00_world.sql new file mode 100644 index 000000000..eabd469a8 --- /dev/null +++ b/sql/updates/world/master/2018_08_05_00_world.sql @@ -0,0 +1,13 @@ +DROP TABLE IF EXISTS `spell_areatrigger_circular`; +CREATE TABLE `spell_areatrigger_circular`( + `SpellMiscId` INT(10) UNSIGNED NOT NULL, + `StartDelay` INT(10) UNSIGNED NOT NULL DEFAULT 0, + `CircleRadius` FLOAT NOT NULL DEFAULT 0, + `BlendFromRadius` FLOAT NOT NULL DEFAULT 0, + `InitialAngle` FLOAT NOT NULL DEFAULT 0, + `ZOffset` FLOAT NOT NULL DEFAULT 0, + `CounterClockwise` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0, + `CanLoop` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0, + `VerifiedBuild` INT(10) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`SpellMiscId`) +);