Core/AreaTrigger: handle AREATRIGGER_FLAG_HAS_CIRCULAR_MOVEMENT
This commit is contained in:
@@ -29,7 +29,7 @@ namespace Framework.Constants
|
||||
Unk2 = 0x080,
|
||||
Unk3 = 0x100,
|
||||
Unk4 = 0x200,
|
||||
Unk5 = 0x400
|
||||
HasCircularMovement = 0x400
|
||||
}
|
||||
|
||||
public enum AreaTriggerTypes
|
||||
|
||||
@@ -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<uint>(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<uint>(1);
|
||||
circularMovementInfo.Radius = circularMovementInfos.Read<float>(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<float>(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<float>(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<float>(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<bool>(6);
|
||||
circularMovementInfo.CanLoop = circularMovementInfos.Read<bool>(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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
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<AreaTriggerCircularMovementInfo> GetCircularMovementInfo() { return _circularMovementInfo; }
|
||||
|
||||
ObjectGuid _targetGuid;
|
||||
|
||||
AuraEffect _aurEff;
|
||||
@@ -761,6 +881,8 @@ namespace Game.Entities
|
||||
int _lastSplineIndex;
|
||||
uint _movementTime;
|
||||
|
||||
Optional<AreaTriggerCircularMovementInfo> _circularMovementInfo;
|
||||
|
||||
AreaTriggerMiscTemplate _areaTriggerMiscTemplate;
|
||||
List<ObjectGuid> _insideUnits = new List<ObjectGuid>();
|
||||
|
||||
|
||||
@@ -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<ObjectGuid> TargetGUID;
|
||||
public Optional<Vector3> 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<Vector3> SplinePoints = new List<Vector3>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1284,6 +1284,7 @@ namespace Game.Maps
|
||||
{
|
||||
// update pos
|
||||
at.Relocate(at._newPosition);
|
||||
at.UpdateShape();
|
||||
at.UpdateObjectVisibility(false);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -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<AreaTriggerSplineInfo> AreaTriggerSpline;
|
||||
public Optional<AreaTriggerUnkTypeInfo> AreaTriggerUnkType;
|
||||
public Optional<AreaTriggerCircularMovementInfo> AreaTriggerCircularMovement;
|
||||
public ObjectGuid TriggerGUID;
|
||||
}
|
||||
|
||||
@@ -119,41 +119,4 @@ namespace Game.Network.Packets
|
||||
public uint ElapsedTimeForMovement;
|
||||
public List<Vector3> Points = new List<Vector3>();
|
||||
}
|
||||
|
||||
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<ObjectGuid> AreaTriggerUnkGUID;
|
||||
public Optional<Vector3> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`)
|
||||
);
|
||||
Reference in New Issue
Block a user