Core/AreaTrigger: handle AREATRIGGER_FLAG_HAS_CIRCULAR_MOVEMENT

This commit is contained in:
hondacrx
2018-08-26 01:08:24 -04:00
parent 3ab69a9500
commit 90ee61ae2b
10 changed files with 260 additions and 76 deletions
@@ -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));
}
+7 -7
View File
@@ -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
+125 -3
View File
@@ -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>();
+7
View File
@@ -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;
+4 -24
View File
@@ -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)
+1
View File
@@ -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`)
);