diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index 69911536..71915398 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -104,6 +104,7 @@ namespace FFXIVClassic_Map_Server.Actors return subpackets; } + // todo: handle instance areas in derived class? (see virtuals) #region Actor Management public void AddActorToZone(Actor actor) @@ -204,12 +205,12 @@ namespace FFXIVClassic_Map_Server.Actors } } - public List GetActorsAroundPoint(float x, float y, int checkDistance) + public virtual List GetActorsAroundPoint(float x, float y, int checkDistance) where T : Actor { checkDistance /= boundingGridSize; - int gridX = (int)x/boundingGridSize; - int gridY = (int)y/boundingGridSize; + int gridX = (int)x / boundingGridSize; + int gridY = (int)y / boundingGridSize; gridX += halfWidth; gridY += halfHeight; @@ -224,7 +225,7 @@ namespace FFXIVClassic_Map_Server.Actors if (gridY >= numYBlocks) gridY = numYBlocks - 1; - List result = new List(); + List result = new List(); lock (mActorBlock) { @@ -232,7 +233,7 @@ namespace FFXIVClassic_Map_Server.Actors { for (int gy = gridY - checkDistance; gy <= gridY + checkDistance; gy++) { - result.AddRange(mActorBlock[gx, gy]); + result.AddRange(mActorBlock[gx, gy].OfType()); } } } @@ -246,11 +247,20 @@ namespace FFXIVClassic_Map_Server.Actors result.RemoveAt(i); } } - return result; } - public List GetActorsAroundActor(Actor actor, int checkDistance) + public virtual List GetActorsAroundPoint(float x, float y, int checkDistance) + { + return GetActorsAroundPoint(x, y, checkDistance); + } + + public virtual List GetActorsAroundActor(Actor actor, int checkDistance) + { + return GetActorsAroundActor(actor, checkDistance); + } + + public virtual List GetActorsAroundActor(Actor actor, int checkDistance) where T : Actor { checkDistance /= boundingGridSize; @@ -270,7 +280,7 @@ namespace FFXIVClassic_Map_Server.Actors if (gridY >= numYBlocks) gridY = numYBlocks - 1; - List result = new List(); + var result = new List(); lock (mActorBlock) { @@ -278,10 +288,11 @@ namespace FFXIVClassic_Map_Server.Actors { for (int gx = ((gridX - checkDistance) < 0 ? 0 : (gridX - checkDistance)); gx <= ((gridX + checkDistance) >= numXBlocks ? numXBlocks - 1 : (gridX + checkDistance)); gx++) { - result.AddRange(mActorBlock[gx, gy]); + result.AddRange(mActorBlock[gx, gy].OfType()); } } } + //Remove players if isolation zone if (isIsolated) { @@ -327,13 +338,10 @@ namespace FFXIVClassic_Map_Server.Actors { lock (mActorList) { - foreach (Actor a in mActorList.Values) + foreach (Player player in mActorList.Values.OfType()) { - if (a is Player) - { - if (((Player)a).customDisplayName.ToLower().Equals(name.ToLower())) - return (Player)a; - } + if (player.customDisplayName.ToLower().Equals(name.ToLower())) + return player; } return null; } @@ -369,19 +377,22 @@ namespace FFXIVClassic_Map_Server.Actors } // todo: for zones override this to seach contentareas (assuming flag is passed) - public virtual List GetAllActors() + public virtual List GetAllActors() where T : Actor { lock (mActorList) { - List actorList = new List(mActorList.Count); - foreach (var actor in mActorList.Values) - { - actorList.Add(actor); - } + List actorList = new List(mActorList.Count); + actorList.AddRange(mActorList.Values.OfType()); return actorList; } } + + public virtual List GetAllActors() + { + return GetAllActors(); + } + public void BroadcastPacketsAroundActor(Actor actor, List packets) { foreach (SubPacket packet in packets) diff --git a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs index 094e5412..a0dba867 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs @@ -88,6 +88,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.controller = controller; } + public Controller GetController() + { + return controller; + } + public bool CanChangeState() { return states.Count == 0 || states.Peek().CanInterrupt(); diff --git a/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs b/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs index 06752fe2..a476e7c3 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs @@ -5,6 +5,8 @@ using System.Text; using System.Threading.Tasks; using FFXIVClassic_Map_Server.Actors; using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.actors.chara.ai; +using FFXIVClassic_Map_Server.actors.chara.ai.controllers; // port of dsp's ai code https://github.com/DarkstarProject/darkstar/blob/master/src/map/ai/ @@ -67,6 +69,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai { private Character owner; private Character target; + private Character masterTarget; // if target is a pet, this is the owner private TargetFindCharacterType findType; private TargetFindFlags findFlags; private TargetFindAOEType aoeType; @@ -128,19 +131,48 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai /// Call SetAOEType before calling this /// Find targets within area set by /// - /// Include pets? - public void FindWithinArea(Character target, TargetFindFlags flags, bool withPet) + public void FindWithinArea(Character target, TargetFindFlags flags) { - // todo: maybe we should keep a snapshot which is only updated on each tick for consistency + findFlags = flags; + // 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.targetPosition = target.GetPosAsVector3(); - this.findFlags = flags; + masterTarget = GetMasterTarget(target); + + // todo: this is stupid + bool withPet = (flags & TargetFindFlags.Pets) != 0 || masterTarget.currentSubState != owner.currentSubState; + + if (IsPlayer(owner)) + { + if (masterTarget is Player) + { + findType = TargetFindCharacterType.PlayerToPlayer; + + // todo: handle player parties + if (masterTarget.currentParty != null) + { + if ((findFlags & TargetFindFlags.Alliance) != 0) + AddAllInAlliance(masterTarget, withPet); + else + AddAllInParty(masterTarget, withPet); + } + else + { + AddTarget(masterTarget, withPet); + } + } + else + { + findType = TargetFindCharacterType.PlayerToBattleNpc; + + } + } if (aoeType == TargetFindAOEType.Box) { FindWithinBox(withPet); @@ -149,6 +181,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai { FindWithinCircle(withPet); } + else if (aoeType == TargetFindAOEType.Cone) + { + FindWithinCone(withPet); + } } /// @@ -163,7 +199,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai // 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 actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 70); var myPos = owner.GetPosAsVector3(); var angle = Vector3.GetAngle(myPos, targetPosition); @@ -174,12 +210,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai var targetCorner = targetPosition.NewHorizontalVector(angle, extents); var targetCorner2 = targetPosition.NewHorizontalVector(angle, -extents); - foreach (Character actor in actors) + foreach (Character actor in actors.OfType()) { // dont wanna add static actors if (actor is Player || actor is BattleNpc) { - if (actor.GetPosAsVector3().IsWithinBox(myCorner2, targetCorner)) + if (actor.GetPosAsVector3().IsWithinBox(targetCorner2, myCorner)) { if (CanTarget(actor)) AddTarget(actor, withPet); @@ -196,18 +232,23 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai /// private void FindWithinCircle(bool withPet) { - var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 30); + var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 70); - foreach (Character target in actors) + foreach (Character actor in actors) { - if (target is Player || target is BattleNpc) + if (actor is Player || actor is BattleNpc) { - if (target.GetPosAsVector3().IsWithinCircle(targetPosition, extents)) + if (actor.GetPosAsVector3().IsWithinCircle(targetPosition, extents)) AddTarget(target, withPet); } } } + private void FindWithinCone(bool withPet) + { + + } + private void AddTarget(Character target, bool withPet) { if (CanTarget(target)) @@ -220,11 +261,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private void AddAllInParty(Character target, bool withPet) { // todo: + AddTarget(target, withPet); } private void AddAllInAlliance(Character target, bool withPet) { // todo: + AddTarget(target, withPet); } public bool CanTarget(Character target) @@ -241,8 +284,32 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (target is Player && ((Player)target).playerSession.isUpdatesLocked) return false; + + return true; } + private bool IsPlayer(Character target) + { + if (target is Player) + return true; + + // treat player owned pets as players too + return GetMasterTarget(target) is Player; + } + + private Character GetMasterTarget(Character target) + { + // if character is a player owned pet, treat as a player + if (target.aiContainer != null) + { + var controller = target.aiContainer.GetController(); + if (controller != null && controller is PetController) + { + return ((PetController)controller).GetPetMaster(); + } + } + return target; + } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/PetController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/PetController.cs index dbe7e67f..54411f7f 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/PetController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/PetController.cs @@ -3,10 +3,65 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using FFXIVClassic_Map_Server.Actors; namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { - class PetController + class PetController : Controller { + private Character petMaster; + + public PetController(Character owner) + { + this.owner = owner; + this.lastUpdate = Program.Tick; + } + + public override void Update(DateTime tick) + { + // todo: handle player stuff on tick + } + + public override void ChangeTarget(Character target) + { + base.ChangeTarget(target); + } + + public override bool Engage(Character target) + { + // todo: check distance, last swing time, status effects + return true; + } + + public override bool Disengage() + { + // todo: + return true; + } + + public override void Cast(Character target, uint spellId) + { + + } + + public override void Ability(Character target, uint abilityId) + { + + } + + public override void RangedAttack(Character target) + { + + } + + public Character GetPetMaster() + { + return petMaster; + } + + public void SetPetMaster(Character master) + { + petMaster = master; + } } }