From 1637bba1677fae330ccd034e4c644f521740f25d Mon Sep 17 00:00:00 2001 From: Tahir Akhlaq Date: Mon, 10 Jul 2017 02:31:37 +0100 Subject: [PATCH] added target finding within box (thanks kjLotus!) - added function to return position as Vector3 to Actor (todo: maybe we should just use the class instead of 3 separate floats?) - added function to return all actors in Area - actually added documentation to TargetFind stuff (kill me pls) - todo: actually test this.. --- FFXIVClassic Common Class Lib/Vector3.cs | 36 ++++ FFXIVClassic Map Server/actors/Actor.cs | 5 + FFXIVClassic Map Server/actors/area/Area.cs | 14 ++ .../actors/chara/ai/TargetFind.cs | 167 +++++++++++++++--- 4 files changed, 196 insertions(+), 26 deletions(-) diff --git a/FFXIVClassic Common Class Lib/Vector3.cs b/FFXIVClassic Common Class Lib/Vector3.cs index 04bf296c..c6cc3d07 100644 --- a/FFXIVClassic Common Class Lib/Vector3.cs +++ b/FFXIVClassic Common Class Lib/Vector3.cs @@ -70,5 +70,41 @@ namespace FFXIVClassic.Common { return (lhs.X * rhs.X) + (lhs.Y * rhs.Y) + (lhs.Z * rhs.Z); } + + public static float GetAngle(Vector3 lhs, Vector3 rhs) + { + var angle = (float)Math.Atan((rhs.Z - lhs.Z) / (rhs.X - lhs.X)); + return lhs.X > rhs.X ? angle + (float)Math.PI : angle; + } + + public Vector3 NewHorizontalVector(float angle, float extents) + { + var newVec = new Vector3(); + newVec.Y = this.Y; + newVec.X = this.X + (float)Math.Cos(angle) * extents; + newVec.Z = this.Z + (float)Math.Sin(angle) * extents; + + return newVec; + } + + public bool IsWithinCircle(Vector3 centre, float radius) + { + float diffX = centre.X - this.X; + float diffZ = centre.Z - this.Z; + + float distance = (float)Math.Sqrt((diffX * diffX) + (diffZ * diffZ)); + + return distance < radius; + } + + public bool IsWithinBox(Vector3 upperLeftCorner, Vector3 lowerRightCorner) + { + return upperLeftCorner.X <= this.X && + upperLeftCorner.Y <= this.Y && + upperLeftCorner.Z <= this.Z && + lowerRightCorner.X >= this.X && + lowerRightCorner.Y >= this.Y && + lowerRightCorner.Z >= this.Z; + } } } diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index d150b74a..283884cf 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -544,6 +544,11 @@ namespace FFXIVClassic_Map_Server.Actors return pos; } + public Vector3 GetPosAsVector3() + { + return new Vector3(positionX, positionY, positionZ); + } + public void SetPos(float x, float y, float z, float rot = 0, uint zoneId = 0) { oldPositionX = positionX; diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index b990df67..69911536 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -368,6 +368,20 @@ namespace FFXIVClassic_Map_Server.Actors } } + // todo: for zones override this to seach contentareas (assuming flag is passed) + public virtual List GetAllActors() + { + lock (mActorList) + { + List actorList = new List(mActorList.Count); + foreach (var actor in mActorList.Values) + { + actorList.Add(actor); + } + return actorList; + } + } + public void BroadcastPacketsAroundActor(Actor actor, List packets) { foreach (SubPacket packet in packets) diff --git a/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs b/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs index f33b36ff..06752fe2 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs @@ -10,40 +10,59 @@ using FFXIVClassic.Common; namespace FFXIVClassic_Map_Server.actors.chara.ai { + /// todo: what even do i summarise this as? [Flags] - enum TargetFindFlags : ushort + enum TargetFindFlags { None, - All, // able to target players who arent in party - Alliance, // alliance - Pet, // allow targeting pets - ZoneWide, // - Dead, // allow targeting of dead players + /// Able to target s even if not in target's party + All, + /// Able to target all s in target's party/alliance + Alliance, + /// Able to target any in target's party/alliance + Pets, + /// Target all in zone, regardless of distance + ZoneWide, + /// Able to target dead s + Dead, } + /// Targeting from/to different entity types enum TargetFindCharacterType { None, - PlayerToPlayer, // player can target all players in party - PlayerToBattleNpc, // player can target all battlenpc (excluding player owned pets) - BattleNpcToBattleNpc, // battlenpc can target other battlenpcs - BattleNpcToPlayer, // battlenpc can target players + /// Player can target all s in party + PlayerToPlayer, + /// Player can target all s (excluding player owned s) + PlayerToBattleNpc, + /// BattleNpc can target other s + BattleNpcToBattleNpc, + /// BattleNpc can target s and their s + BattleNpcToPlayer, } + /// Type of AOE region to create enum TargetFindAOEType { None, + /// Really a cylinder, uses extents parameter in SetAOEType Circle, + /// Create a cone with angle in radians Cone, + /// Box using self/target coords and Box } + /// Set AOE around self or target enum TargetFindAOERadiusType { + /// Set AOE's origin at target's position Target, + /// Set AOE's origin to own position. Self } + /// Target finding helper class class TargetFind { private Character owner; @@ -51,8 +70,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private TargetFindCharacterType findType; private TargetFindFlags findFlags; private TargetFindAOEType aoeType; + private TargetFindAOERadiusType radiusType; private Vector3 targetPosition; - private float range; + private float extents; private float angle; private List targets; @@ -67,48 +87,144 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.target = null; this.findType = TargetFindCharacterType.None; this.findFlags = TargetFindFlags.None; + this.aoeType = TargetFindAOEType.None; + this.radiusType = TargetFindAOERadiusType.Self; this.targetPosition = null; - this.range = 0.0f; + this.extents = 0.0f; this.angle = 0.0f; + this.targets = new List(); } - public void SetAOEType(TargetFindAOEType type, float range = -1.0f, float angle = -1.0f) + /// + /// Call this before + /// + /// + /// - radius of circle + /// - height of cone + /// - width of box / 2 + /// + /// Angle in radians of cone + public void SetAOEType(TargetFindAOERadiusType radiusType, TargetFindAOEType aoeType, float extents = -1.0f, float angle = -1.0f) { - aoeType = type; - range = range != -1.0f ? range : 0.0f; - angle = angle != -1.0f ? angle : 0.0f; + this.radiusType = TargetFindAOERadiusType.Target; + this.aoeType = aoeType; + this.extents = extents != -1.0f ? extents : 0.0f; + this.angle = angle != -1.0f ? angle : 0.0f; } + /// + /// Find and try to add a single target to target list + /// public void FindTarget(Character target, TargetFindFlags flags) { findFlags = flags; this.target = null; - this.targetPosition = new Vector3(target.positionX, target.positionY, target.positionZ); - + // todo: maybe this should only be set if successfully added? + this.targetPosition = target.GetPosAsVector3(); AddTarget(target, false); } - public void FindWithinArea(Character target, float radius, TargetFindFlags flags) - { + /// + /// Call SetAOEType before calling this + /// Find targets within area set by + /// + /// Include pets? + public void FindWithinArea(Character target, TargetFindFlags flags, bool withPet) + { + // todo: maybe we should keep a snapshot which is only updated on each tick for consistency + + // are we creating aoe circles around target or self + if ((aoeType & TargetFindAOEType.Circle) != 0 && radiusType != TargetFindAOERadiusType.Self) + this.targetPosition = owner.GetPosAsVector3(); + else + this.targetPosition = new Vector3(target.positionX, target.positionY, target.positionZ); + + this.findFlags = flags; + if (aoeType == TargetFindAOEType.Box) + { + FindWithinBox(withPet); + } + else if (aoeType == TargetFindAOEType.Circle) + { + FindWithinCircle(withPet); + } + } + + /// + /// Find targets within a box using owner's coordinates and target's coordinates as length + /// with corners being `extents` yalms to either side of self and target + /// + private void FindWithinBox(bool withPet) + { + // todo: loop over party members + if ((findFlags & TargetFindFlags.All) != 0) + { + // if we have flag set to hit all characters in zone, do it + + // todo: make the distance check modifiable + var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 30); + var myPos = owner.GetPosAsVector3(); + var angle = Vector3.GetAngle(myPos, targetPosition); + + // todo: actually check this works.. + var myCorner = myPos.NewHorizontalVector(angle, extents); + var myCorner2 = myPos.NewHorizontalVector(angle, -extents); + + var targetCorner = targetPosition.NewHorizontalVector(angle, extents); + var targetCorner2 = targetPosition.NewHorizontalVector(angle, -extents); + + foreach (Character actor in actors) + { + // dont wanna add static actors + if (actor is Player || actor is BattleNpc) + { + if (actor.GetPosAsVector3().IsWithinBox(myCorner2, targetCorner)) + { + if (CanTarget(actor)) + AddTarget(actor, withPet); + } + } + } + } + } + + /// + /// Find targets within circle area. + /// As the name implies, it only checks horizontal coords, not vertical - + /// effectively creating cylinder with infinite height + /// + private void FindWithinCircle(bool withPet) + { + var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 30); + + foreach (Character target in actors) + { + if (target is Player || target is BattleNpc) + { + if (target.GetPosAsVector3().IsWithinCircle(targetPosition, extents)) + AddTarget(target, withPet); + } + } } private void AddTarget(Character target, bool withPet) { if (CanTarget(target)) + { + // todo: add pets too targets.Add(target); - - // todo: add pets too + } } private void AddAllInParty(Character target, bool withPet) { - + // todo: } private void AddAllInAlliance(Character target, bool withPet) { - + // todo: } public bool CanTarget(Character target) @@ -125,9 +241,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (target is Player && ((Player)target).playerSession.isUpdatesLocked) return false; - - return true; } + } }