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()