From 84d5eee1fcc284d252b7953a70aebed60b195ee8 Mon Sep 17 00:00:00 2001 From: Tahir Akhlaq Date: Tue, 11 Jul 2017 01:54:15 +0100 Subject: [PATCH] cleaned targetfind some - added character allegiance types --- .../actors/chara/Character.cs | 13 +- .../actors/chara/ai/TargetFind.cs | 203 +++++++++++------- .../chara/ai/controllers/PetController.cs | 5 + .../actors/chara/npc/BattleNpc.cs | 1 + .../actors/chara/npc/Pet.cs | 2 +- .../actors/chara/player/Player.cs | 1 + 6 files changed, 149 insertions(+), 76 deletions(-) diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 4d0d5c35..761d39c7 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -10,7 +10,16 @@ using System; namespace FFXIVClassic_Map_Server.Actors { - class Character:Actor + /// Which Character types am I friendly with + enum CharacterTargetingAllegiance + { + /// Friendly to Players + Player, + /// Friendly to BattleNpcs + BattleNpcs + } + + class Character : Actor { public const int SIZE = 0; public const int COLORINFO = 1; @@ -66,6 +75,8 @@ namespace FFXIVClassic_Map_Server.Actors public AIContainer aiContainer; public StatusEffects statusEffects; + public CharacterTargetingAllegiance allegiance; + public Character(uint actorID) : base(actorID) { //Init timer array to "notimer" diff --git a/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs b/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs index a476e7c3..49af0b78 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs @@ -18,7 +18,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai { None, /// Able to target s even if not in target's party - All, + HitAll, /// Able to target all s in target's party/alliance Alliance, /// Able to target any in target's party/alliance @@ -98,6 +98,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.targets = new List(); } + public List GetTargets() where T : Character + { + return new List(targets.OfType()); + } + + public List GetTargets() + { + return targets; + } + /// /// Call this before /// @@ -131,11 +141,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai /// Call SetAOEType before calling this /// Find targets within area set by /// - public void FindWithinArea(Character target, TargetFindFlags flags) { 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) @@ -143,10 +151,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai else this.targetPosition = target.GetPosAsVector3(); - masterTarget = GetMasterTarget(target); + masterTarget = TryGetMasterTarget(target) ?? target; + + // todo: should i set this yet or wait til checked if valid target + this.target = target; // todo: this is stupid - bool withPet = (flags & TargetFindFlags.Pets) != 0 || masterTarget.currentSubState != owner.currentSubState; + bool withPet = (flags & TargetFindFlags.Pets) != 0 || masterTarget.allegiance != owner.allegiance; if (IsPlayer(owner)) { @@ -170,83 +181,63 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai else { findType = TargetFindCharacterType.PlayerToBattleNpc; - + AddAllBattleNpcs(masterTarget, false); } } - if (aoeType == TargetFindAOEType.Box) + else { - FindWithinBox(withPet); - } - else if (aoeType == TargetFindAOEType.Circle) - { - FindWithinCircle(withPet); - } - else if (aoeType == TargetFindAOEType.Cone) - { - FindWithinCone(withPet); + // todo: this needs checking.. + if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player) + findType = TargetFindCharacterType.BattleNpcToPlayer; + else + findType = TargetFindCharacterType.BattleNpcToBattleNpc; + + // todo: configurable pet aoe buff + if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null) + withPet = true; + + // todo: does ffxiv have call for help flag? + //if ((findFlags & TargetFindFlags.HitAll) != 0) + //{ + // AddAllInZone(masterTarget, withPet); + //} + + AddAllInAlliance(target, withPet); + + if (findType == TargetFindCharacterType.BattleNpcToPlayer) + { + if (owner.allegiance == CharacterTargetingAllegiance.Player) + AddAllInZone(masterTarget, withPet); + else + AddAllInHateList(); + } } + } /// /// 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) + private bool IsWithinBox(Character target, 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 + var myPos = owner.GetPosAsVector3(); + var angle = Vector3.GetAngle(myPos, targetPosition); - // todo: make the distance check modifiable - var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 70); - 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); - // 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); - var targetCorner = targetPosition.NewHorizontalVector(angle, extents); - var targetCorner2 = targetPosition.NewHorizontalVector(angle, -extents); - - foreach (Character actor in actors.OfType()) - { - // dont wanna add static actors - if (actor is Player || actor is BattleNpc) - { - if (actor.GetPosAsVector3().IsWithinBox(targetCorner2, myCorner)) - { - if (CanTarget(actor)) - AddTarget(actor, withPet); - } - } - } - } + return target.GetPosAsVector3().IsWithinBox(targetCorner2, myCorner); } - /// - /// 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) + private bool IsWithinCone(Character target, bool withPet) { - var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 70); - - foreach (Character actor in actors) - { - if (actor is Player || actor is BattleNpc) - { - if (actor.GetPosAsVector3().IsWithinCircle(targetPosition, extents)) - AddTarget(target, withPet); - } - } - } - - private void FindWithinCone(bool withPet) - { - + // todo: + return false; } private void AddTarget(Character target, bool withPet) @@ -261,16 +252,61 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private void AddAllInParty(Character target, bool withPet) { // todo: + /* + * foreach (var actor in target.currentParty.GetMembers()) + * { + * AddTarget(actor, withPet); + * } + */ AddTarget(target, withPet); } private void AddAllInAlliance(Character target, bool withPet) { // todo: + /* + * foreach (var actor in target.currentParty.GetAllianceMembers()) + * { + * AddTarget(actor, withPet); + * } + */ AddTarget(target, withPet); } - public bool CanTarget(Character target) + private void AddAllBattleNpcs(Character target, bool withPet) + { + // 70 is client render distance so we'll go with that + var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 70); + + // todo: should we look for Characters instead in case player is charmed by BattleNpc + foreach (BattleNpc actor in actors) + { + // todo: + AddTarget(actor, false); + } + } + + private void AddAllInZone(Character target, bool withPet) + { + var actors = owner.zone.GetAllActors(); + foreach (Character actor in actors) + { + AddTarget(actor, withPet); + } + } + + private void AddAllInHateList() + { + if (!(owner is BattleNpc)) + Program.Log.Error($"TargetFind.AddAllInHateList() owner [{owner.actorId}] {owner.customDisplayName} {owner.actorName} is not a BattleNpc"); + + foreach (var hateEntry in ((BattleNpc)owner).hateContainer.GetHateList()) + { + AddTarget(hateEntry.Value.actor, false); + } + } + + public bool CanTarget(Character target, bool withPet = false) { // already targeted, dont target again if (targets.Contains(target)) @@ -280,13 +316,23 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if ((findFlags & TargetFindFlags.Dead) == 0 && target.IsDead()) return false; - // cant target if player is zoning - if (target is Player && ((Player)target).playerSession.isUpdatesLocked) + bool targetingPlayer = target is Player; + + // cant target if zoning + if (target.isZoning || owner.isZoning || target.zone != owner.zone || targetingPlayer && ((Player)target).playerSession.isUpdatesLocked) return false; + // hit everything within zone or within aoe region + if ((findFlags & TargetFindFlags.ZoneWide) != 0 || aoeType == TargetFindAOEType.Circle && target.GetPosAsVector3().IsWithinCircle(targetPosition, extents)) + return true; + if (aoeType == TargetFindAOEType.Cone && IsWithinCone(target, withPet)) + return true; - return true; + if (aoeType == TargetFindAOEType.Box && IsWithinBox(target, withPet)) + return true; + + return false; } private bool IsPlayer(Character target) @@ -295,21 +341,30 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return true; // treat player owned pets as players too - return GetMasterTarget(target) is Player; + return TryGetMasterTarget(target) is Player; } - private Character GetMasterTarget(Character target) + private Character TryGetMasterTarget(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; + return null; + } + + private bool IsBattleNpcOwner(Character target) + { + // i know i copied this from dsp but what even + if (!(owner is Player) || target is Player) + return true; + + // todo: check hate list + + return false; } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/PetController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/PetController.cs index 54411f7f..2ffa3336 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/PetController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/PetController.cs @@ -62,6 +62,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers public void SetPetMaster(Character master) { petMaster = master; + + if (master is Player) + owner.allegiance = CharacterTargetingAllegiance.Player; + else + owner.allegiance = CharacterTargetingAllegiance.BattleNpcs; } } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs index b7895e3d..8adaa964 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs @@ -24,6 +24,7 @@ namespace FFXIVClassic_Map_Server.Actors this.aiContainer = new AIContainer(this, new BattleNpcController(this), new PathFind(this), new TargetFind(this)); this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER; this.hateContainer = new HateContainer(this); + this.allegiance = CharacterTargetingAllegiance.BattleNpcs; } } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/Pet.cs b/FFXIVClassic Map Server/actors/chara/npc/Pet.cs index 1ecaa67f..27e9490e 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Pet.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Pet.cs @@ -17,7 +17,7 @@ namespace FFXIVClassic_Map_Server.Actors ushort actorState, uint animationId, string customDisplayName) : base(actorNumber, actorClass, uniqueId, spawnedArea, posX, posY, posZ, rot, actorState, animationId, customDisplayName) { - this.aiContainer = new AIContainer(this, new BattleNpcController(this), new PathFind(this), new TargetFind(this)); + this.aiContainer = new AIContainer(this, new PetController(this), new PathFind(this), new TargetFind(this)); this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER; this.hateContainer = new HateContainer(this); } diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 0059d68e..a0ddb61a 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -251,6 +251,7 @@ namespace FFXIVClassic_Map_Server.Actors lastPlayTimeUpdate = Utils.UnixTimeStampUTC(); this.aiContainer = new AIContainer(this, new PlayerController(this), null, new TargetFind(this)); + allegiance = CharacterTargetingAllegiance.Player; } public List Create0x132Packets()