mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-04-23 21:27:46 +00:00
cleaned targetfind some
- added character allegiance types
This commit is contained in:
parent
59fab08230
commit
84d5eee1fc
6 changed files with 149 additions and 76 deletions
|
@ -10,7 +10,16 @@ using System;
|
||||||
|
|
||||||
namespace FFXIVClassic_Map_Server.Actors
|
namespace FFXIVClassic_Map_Server.Actors
|
||||||
{
|
{
|
||||||
class Character:Actor
|
/// <summary> Which Character types am I friendly with </summary>
|
||||||
|
enum CharacterTargetingAllegiance
|
||||||
|
{
|
||||||
|
/// <summary> Friendly to Players </summary>
|
||||||
|
Player,
|
||||||
|
/// <summary> Friendly to BattleNpcs </summary>
|
||||||
|
BattleNpcs
|
||||||
|
}
|
||||||
|
|
||||||
|
class Character : Actor
|
||||||
{
|
{
|
||||||
public const int SIZE = 0;
|
public const int SIZE = 0;
|
||||||
public const int COLORINFO = 1;
|
public const int COLORINFO = 1;
|
||||||
|
@ -66,6 +75,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
public AIContainer aiContainer;
|
public AIContainer aiContainer;
|
||||||
public StatusEffects statusEffects;
|
public StatusEffects statusEffects;
|
||||||
|
|
||||||
|
public CharacterTargetingAllegiance allegiance;
|
||||||
|
|
||||||
public Character(uint actorID) : base(actorID)
|
public Character(uint actorID) : base(actorID)
|
||||||
{
|
{
|
||||||
//Init timer array to "notimer"
|
//Init timer array to "notimer"
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
/// <summary> Able to target <see cref="Player"/>s even if not in target's party </summary>
|
/// <summary> Able to target <see cref="Player"/>s even if not in target's party </summary>
|
||||||
All,
|
HitAll,
|
||||||
/// <summary> Able to target all <see cref="Player"/>s in target's party/alliance </summary>
|
/// <summary> Able to target all <see cref="Player"/>s in target's party/alliance </summary>
|
||||||
Alliance,
|
Alliance,
|
||||||
/// <summary> Able to target any <see cref="Pet"/> in target's party/alliance </summary>
|
/// <summary> Able to target any <see cref="Pet"/> in target's party/alliance </summary>
|
||||||
|
@ -98,6 +98,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
this.targets = new List<Character>();
|
this.targets = new List<Character>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<T> GetTargets<T>() where T : Character
|
||||||
|
{
|
||||||
|
return new List<T>(targets.OfType<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Character> GetTargets()
|
||||||
|
{
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Call this before <see cref="FindWithinArea"/> <para/>
|
/// Call this before <see cref="FindWithinArea"/> <para/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -131,11 +141,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
/// <para> Call SetAOEType before calling this </para>
|
/// <para> Call SetAOEType before calling this </para>
|
||||||
/// Find targets within area set by <see cref="SetAOEType"/>
|
/// Find targets within area set by <see cref="SetAOEType"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public void FindWithinArea(Character target, TargetFindFlags flags)
|
public void FindWithinArea(Character target, TargetFindFlags flags)
|
||||||
{
|
{
|
||||||
findFlags = flags;
|
findFlags = flags;
|
||||||
|
|
||||||
// todo: maybe we should keep a snapshot which is only updated on each tick for consistency
|
// 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
|
// are we creating aoe circles around target or self
|
||||||
if ((aoeType & TargetFindAOEType.Circle) != 0 && radiusType != TargetFindAOERadiusType.Self)
|
if ((aoeType & TargetFindAOEType.Circle) != 0 && radiusType != TargetFindAOERadiusType.Self)
|
||||||
|
@ -143,10 +151,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
else
|
else
|
||||||
this.targetPosition = target.GetPosAsVector3();
|
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
|
// 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))
|
if (IsPlayer(owner))
|
||||||
{
|
{
|
||||||
|
@ -170,83 +181,63 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
findType = TargetFindCharacterType.PlayerToBattleNpc;
|
findType = TargetFindCharacterType.PlayerToBattleNpc;
|
||||||
|
AddAllBattleNpcs(masterTarget, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (aoeType == TargetFindAOEType.Box)
|
else
|
||||||
{
|
{
|
||||||
FindWithinBox(withPet);
|
// todo: this needs checking..
|
||||||
}
|
if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player)
|
||||||
else if (aoeType == TargetFindAOEType.Circle)
|
findType = TargetFindCharacterType.BattleNpcToPlayer;
|
||||||
{
|
else
|
||||||
FindWithinCircle(withPet);
|
findType = TargetFindCharacterType.BattleNpcToBattleNpc;
|
||||||
}
|
|
||||||
else if (aoeType == TargetFindAOEType.Cone)
|
// todo: configurable pet aoe buff
|
||||||
{
|
if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null)
|
||||||
FindWithinCone(withPet);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Find targets within a box using owner's coordinates and target's coordinates as length
|
/// 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
|
/// with corners being `extents` yalms to either side of self and target
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void FindWithinBox(bool withPet)
|
private bool IsWithinBox(Character target, bool withPet)
|
||||||
{
|
{
|
||||||
// todo: loop over party members
|
var myPos = owner.GetPosAsVector3();
|
||||||
if ((findFlags & TargetFindFlags.All) != 0)
|
var angle = Vector3.GetAngle(myPos, targetPosition);
|
||||||
{
|
|
||||||
// if we have flag set to hit all characters in zone, do it
|
|
||||||
|
|
||||||
// todo: make the distance check modifiable
|
// todo: actually check this works..
|
||||||
var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors<Character>() : owner.zone.GetActorsAroundActor<Character>(owner, 70);
|
var myCorner = myPos.NewHorizontalVector(angle, extents);
|
||||||
var myPos = owner.GetPosAsVector3();
|
var myCorner2 = myPos.NewHorizontalVector(angle, -extents);
|
||||||
var angle = Vector3.GetAngle(myPos, targetPosition);
|
|
||||||
|
|
||||||
// todo: actually check this works..
|
var targetCorner = targetPosition.NewHorizontalVector(angle, extents);
|
||||||
var myCorner = myPos.NewHorizontalVector(angle, extents);
|
var targetCorner2 = targetPosition.NewHorizontalVector(angle, -extents);
|
||||||
var myCorner2 = myPos.NewHorizontalVector(angle, -extents);
|
|
||||||
|
|
||||||
var targetCorner = targetPosition.NewHorizontalVector(angle, extents);
|
return target.GetPosAsVector3().IsWithinBox(targetCorner2, myCorner);
|
||||||
var targetCorner2 = targetPosition.NewHorizontalVector(angle, -extents);
|
|
||||||
|
|
||||||
foreach (Character actor in actors.OfType<Character>())
|
|
||||||
{
|
|
||||||
// dont wanna add static actors
|
|
||||||
if (actor is Player || actor is BattleNpc)
|
|
||||||
{
|
|
||||||
if (actor.GetPosAsVector3().IsWithinBox(targetCorner2, myCorner))
|
|
||||||
{
|
|
||||||
if (CanTarget(actor))
|
|
||||||
AddTarget(actor, withPet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private bool IsWithinCone(Character target, bool withPet)
|
||||||
/// Find targets within circle area. <para/>
|
|
||||||
/// As the name implies, it only checks horizontal coords, not vertical -
|
|
||||||
/// effectively creating cylinder with infinite height
|
|
||||||
/// </summary>
|
|
||||||
private void FindWithinCircle(bool withPet)
|
|
||||||
{
|
{
|
||||||
var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors<Character>() : owner.zone.GetActorsAroundActor<Character>(owner, 70);
|
// todo:
|
||||||
|
return false;
|
||||||
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)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddTarget(Character target, bool withPet)
|
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)
|
private void AddAllInParty(Character target, bool withPet)
|
||||||
{
|
{
|
||||||
// todo:
|
// todo:
|
||||||
|
/*
|
||||||
|
* foreach (var actor in target.currentParty.GetMembers())
|
||||||
|
* {
|
||||||
|
* AddTarget(actor, withPet);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
AddTarget(target, withPet);
|
AddTarget(target, withPet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddAllInAlliance(Character target, bool withPet)
|
private void AddAllInAlliance(Character target, bool withPet)
|
||||||
{
|
{
|
||||||
// todo:
|
// todo:
|
||||||
|
/*
|
||||||
|
* foreach (var actor in target.currentParty.GetAllianceMembers())
|
||||||
|
* {
|
||||||
|
* AddTarget(actor, withPet);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
AddTarget(target, 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<BattleNpc>() : owner.zone.GetActorsAroundActor<BattleNpc>(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<Character>();
|
||||||
|
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
|
// already targeted, dont target again
|
||||||
if (targets.Contains(target))
|
if (targets.Contains(target))
|
||||||
|
@ -280,13 +316,23 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
if ((findFlags & TargetFindFlags.Dead) == 0 && target.IsDead())
|
if ((findFlags & TargetFindFlags.Dead) == 0 && target.IsDead())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// cant target if player is zoning
|
bool targetingPlayer = target is Player;
|
||||||
if (target is Player && ((Player)target).playerSession.isUpdatesLocked)
|
|
||||||
|
// cant target if zoning
|
||||||
|
if (target.isZoning || owner.isZoning || target.zone != owner.zone || targetingPlayer && ((Player)target).playerSession.isUpdatesLocked)
|
||||||
return false;
|
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)
|
private bool IsPlayer(Character target)
|
||||||
|
@ -295,21 +341,30 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// treat player owned pets as players too
|
// 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 character is a player owned pet, treat as a player
|
||||||
if (target.aiContainer != null)
|
if (target.aiContainer != null)
|
||||||
{
|
{
|
||||||
var controller = target.aiContainer.GetController();
|
var controller = target.aiContainer.GetController();
|
||||||
if (controller != null && controller is PetController)
|
if (controller != null && controller is PetController)
|
||||||
{
|
|
||||||
return ((PetController)controller).GetPetMaster();
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
|
||||||
public void SetPetMaster(Character master)
|
public void SetPetMaster(Character master)
|
||||||
{
|
{
|
||||||
petMaster = master;
|
petMaster = master;
|
||||||
|
|
||||||
|
if (master is Player)
|
||||||
|
owner.allegiance = CharacterTargetingAllegiance.Player;
|
||||||
|
else
|
||||||
|
owner.allegiance = CharacterTargetingAllegiance.BattleNpcs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
this.aiContainer = new AIContainer(this, new BattleNpcController(this), new PathFind(this), new TargetFind(this));
|
this.aiContainer = new AIContainer(this, new BattleNpcController(this), new PathFind(this), new TargetFind(this));
|
||||||
this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER;
|
this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER;
|
||||||
this.hateContainer = new HateContainer(this);
|
this.hateContainer = new HateContainer(this);
|
||||||
|
this.allegiance = CharacterTargetingAllegiance.BattleNpcs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
ushort actorState, uint animationId, string customDisplayName)
|
ushort actorState, uint animationId, string customDisplayName)
|
||||||
: base(actorNumber, actorClass, uniqueId, spawnedArea, posX, posY, posZ, rot, actorState, animationId, 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.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER;
|
||||||
this.hateContainer = new HateContainer(this);
|
this.hateContainer = new HateContainer(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,6 +251,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
lastPlayTimeUpdate = Utils.UnixTimeStampUTC();
|
lastPlayTimeUpdate = Utils.UnixTimeStampUTC();
|
||||||
|
|
||||||
this.aiContainer = new AIContainer(this, new PlayerController(this), null, new TargetFind(this));
|
this.aiContainer = new AIContainer(this, new PlayerController(this), null, new TargetFind(this));
|
||||||
|
allegiance = CharacterTargetingAllegiance.Player;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<SubPacket> Create0x132Packets()
|
public List<SubPacket> Create0x132Packets()
|
||||||
|
|
Loading…
Add table
Reference in a new issue