mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-05-20 08:26:59 -04:00
added pool/spawn/genus mod loading
- moved ai helper classes to own folder
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FFXIVClassic_Map_Server.Actors;
|
||||
using MoonSharp;
|
||||
using MoonSharp.Interpreter;
|
||||
using FFXIVClassic_Map_Server.lua;
|
||||
|
||||
namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
{
|
||||
class Action
|
||||
{
|
||||
public DateTime startTime;
|
||||
public uint durationMs;
|
||||
public bool checkState;
|
||||
// todo: lua function
|
||||
LuaScript script;
|
||||
}
|
||||
|
||||
class ActionQueue
|
||||
{
|
||||
private Character owner;
|
||||
private Queue<Action> actionQueue;
|
||||
private Queue<Action> timerQueue;
|
||||
|
||||
public bool IsEmpty { get { return actionQueue.Count > 0 || timerQueue.Count > 0; } }
|
||||
|
||||
public ActionQueue(Character owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
actionQueue = new Queue<Action>();
|
||||
timerQueue = new Queue<Action>();
|
||||
}
|
||||
|
||||
public void PushAction(Action action)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Update(DateTime tick)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void HandleAction(Action action)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void CheckAction(DateTime tick)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
205
FFXIVClassic Map Server/actors/chara/ai/helpers/PathFind.cs
Normal file
205
FFXIVClassic Map Server/actors/chara/ai/helpers/PathFind.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FFXIVClassic_Map_Server.Actors;
|
||||
using FFXIVClassic_Map_Server;
|
||||
using FFXIVClassic_Map_Server.utils;
|
||||
using FFXIVClassic.Common;
|
||||
using FFXIVClassic_Map_Server.actors.area;
|
||||
using FFXIVClassic_Map_Server.packets.send.actor;
|
||||
|
||||
// port of https://github.com/DarkstarProject/darkstar/blob/master/src/map/ai/helpers/pathfind.h
|
||||
|
||||
namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
{
|
||||
// todo: check for obstacles, los, etc
|
||||
public enum PathFindFlags
|
||||
{
|
||||
None,
|
||||
Scripted = 0x01,
|
||||
IgnoreNav = 0x02,
|
||||
}
|
||||
class PathFind
|
||||
{
|
||||
private Character owner;
|
||||
private List<Vector3> path;
|
||||
private bool canFollowPath;
|
||||
float distanceFromPoint;
|
||||
|
||||
private PathFindFlags pathFlags;
|
||||
|
||||
public PathFind(Character owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public void PreparePath(Vector3 dest, float stepSize = 1.25f, int maxPath = 40, float polyRadius = 0.0f)
|
||||
{
|
||||
PreparePath(dest.X, dest.Y, dest.Z, stepSize, maxPath, polyRadius);
|
||||
}
|
||||
|
||||
public void PreparePath(float x, float y, float z, float stepSize = 1.25f, int maxPath = 40, float polyRadius = 0.0f)
|
||||
{
|
||||
var pos = new Vector3(owner.positionX, owner.positionY, owner.positionZ);
|
||||
var dest = new Vector3(x, y, z);
|
||||
var zone = (Zone)owner.GetZone();
|
||||
var sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
if ((pathFlags & PathFindFlags.IgnoreNav) != 0)
|
||||
path = new List<Vector3>(1) { new Vector3(x, y, z) };
|
||||
else
|
||||
path = NavmeshUtils.GetPath(zone, pos, dest, stepSize, maxPath, polyRadius);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
if (owner.oldPositionX == 0.0f && owner.oldPositionY == 0.0f && owner.oldPositionZ == 0.0f)
|
||||
{
|
||||
owner.oldPositionX = owner.positionX;
|
||||
owner.oldPositionY = owner.positionY;
|
||||
owner.oldPositionZ = owner.positionZ;
|
||||
}
|
||||
|
||||
// todo: something went wrong
|
||||
if (path.Count == 0)
|
||||
{
|
||||
owner.positionX = owner.oldPositionX;
|
||||
owner.positionY = owner.oldPositionY;
|
||||
owner.positionZ = owner.oldPositionZ;
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
zone.pathCalls++;
|
||||
zone.pathCallTime += sw.ElapsedMilliseconds;
|
||||
|
||||
//if (path.Count == 1)
|
||||
// Program.Log.Info($"mypos: {owner.positionX} {owner.positionY} {owner.positionZ} | targetPos: {x} {y} {z} | step {stepSize} | maxPath {maxPath} | polyRadius {polyRadius}");
|
||||
|
||||
//Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", owner.actorId, owner.actorName, path.Count, sw.ElapsedMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
public void PathInRange(Vector3 dest, float minRange, float maxRange)
|
||||
{
|
||||
PathInRange(dest.X, dest.Y, dest.Z, minRange, maxRange);
|
||||
}
|
||||
|
||||
public void PathInRange(float x, float y, float z, float minRange, float maxRange = 5.0f)
|
||||
{
|
||||
var dest = owner.FindRandomPoint(x, y, z, minRange, maxRange);
|
||||
// todo: this is dumb..
|
||||
distanceFromPoint = owner.GetAttackRange();
|
||||
PreparePath(dest.X, dest.Y, dest.Z);
|
||||
}
|
||||
|
||||
|
||||
public void SetPathFlags(PathFindFlags flags)
|
||||
{
|
||||
this.pathFlags = flags;
|
||||
}
|
||||
|
||||
public bool IsFollowingPath()
|
||||
{
|
||||
return path?.Count > 0;
|
||||
}
|
||||
|
||||
public bool IsFollowingScriptedPath()
|
||||
{
|
||||
return (pathFlags & PathFindFlags.Scripted) != 0;
|
||||
}
|
||||
|
||||
public void FollowPath()
|
||||
{
|
||||
if (path?.Count > 0)
|
||||
{
|
||||
var point = path[0];
|
||||
|
||||
StepTo(point);
|
||||
|
||||
if (AtPoint(point))
|
||||
{
|
||||
path.Remove(point);
|
||||
owner.OnPath(point);
|
||||
//Program.Log.Error($"{owner.actorName} arrived at point {point.X} {point.Y} {point.Z}");
|
||||
}
|
||||
|
||||
if (path.Count == 0 && owner.target != null)
|
||||
owner.LookAt(owner.target);
|
||||
}
|
||||
}
|
||||
|
||||
public bool AtPoint(Vector3 point = null)
|
||||
{
|
||||
if (point == null && path != null && path.Count > 0)
|
||||
{
|
||||
point = path[path.Count - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
distanceFromPoint = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (distanceFromPoint == 0)
|
||||
return owner.positionX == point.X && owner.positionZ == point.Z;
|
||||
else
|
||||
return Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, point.X, point.Y, point.Z) <= (distanceFromPoint + 4.5f);
|
||||
}
|
||||
|
||||
public void StepTo(Vector3 point, bool run = false)
|
||||
{
|
||||
float speed = GetSpeed();
|
||||
|
||||
float stepDistance = speed / 3;
|
||||
float distanceTo = Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, point.X, point.Y, point.Z);
|
||||
|
||||
owner.LookAt(point);
|
||||
|
||||
if (distanceTo <= distanceFromPoint + stepDistance)
|
||||
{
|
||||
if (distanceFromPoint <= owner.GetAttackRange())
|
||||
{
|
||||
owner.QueuePositionUpdate(point);
|
||||
}
|
||||
else
|
||||
{
|
||||
float x = owner.positionX - (float)Math.Cos(owner.rotation + (float)(Math.PI / 2)) * (distanceTo - distanceFromPoint);
|
||||
float z = owner.positionZ + (float)Math.Sin(owner.rotation + (float)(Math.PI / 2)) * (distanceTo - distanceFromPoint);
|
||||
|
||||
owner.QueuePositionUpdate(x, owner.positionY, z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float x = owner.positionX - (float)Math.Cos(owner.rotation + (float)(Math.PI / 2)) * (distanceTo - distanceFromPoint);
|
||||
float z = owner.positionZ + (float)Math.Sin(owner.rotation + (float)(Math.PI / 2)) * (distanceTo - distanceFromPoint);
|
||||
|
||||
owner.QueuePositionUpdate(x, owner.positionY, z);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
path?.Clear();
|
||||
pathFlags = PathFindFlags.None;
|
||||
distanceFromPoint = 0.0f;
|
||||
}
|
||||
|
||||
private float GetSpeed()
|
||||
{
|
||||
float baseSpeed = owner.GetSpeed();
|
||||
|
||||
if (!(owner is Player))
|
||||
{
|
||||
if (owner is BattleNpc)
|
||||
{
|
||||
//owner.ChangeSpeed(0.0f, SetActorSpeedPacket.DEFAULT_WALK - 2.0f, SetActorSpeedPacket.DEFAULT_RUN - 2.0f, SetActorSpeedPacket.DEFAULT_ACTIVE - 2.0f);
|
||||
}
|
||||
// baseSpeed += ConfigConstants.NPC_SPEED_MOD;
|
||||
}
|
||||
return baseSpeed;
|
||||
}
|
||||
}
|
||||
}
|
444
FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs
Normal file
444
FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs
Normal file
@@ -0,0 +1,444 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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;
|
||||
using FFXIVClassic_Map_Server.actors.group;
|
||||
using FFXIVClassic_Map_Server.packets.send.actor;
|
||||
|
||||
// port of dsp's ai code https://github.com/DarkstarProject/darkstar/blob/master/src/map/ai/
|
||||
|
||||
namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
{
|
||||
// https://github.com/Windower/POLUtils/blob/master/PlayOnline.FFXI/Enums.cs
|
||||
[Flags]
|
||||
public enum ValidTarget : ushort
|
||||
{
|
||||
None = 0x00,
|
||||
Self = 0x01,
|
||||
Player = 0x02,
|
||||
PartyMember = 0x04,
|
||||
Ally = 0x08,
|
||||
NPC = 0x10,
|
||||
Enemy = 0x20,
|
||||
Unknown = 0x40,
|
||||
Object = 0x60,
|
||||
CorpseOnly = 0x80,
|
||||
Corpse = 0x9D // CorpseOnly + NPC + Ally + Partymember + Self
|
||||
}
|
||||
|
||||
/// <summary> Targeting from/to different entity types </summary>
|
||||
enum TargetFindCharacterType : byte
|
||||
{
|
||||
None,
|
||||
/// <summary> Player can target all <see cref="Player">s in party </summary>
|
||||
PlayerToPlayer,
|
||||
/// <summary> Player can target all <see cref="BattleNpc"/>s (excluding player owned <see cref="Pet"/>s) </summary>
|
||||
PlayerToBattleNpc,
|
||||
/// <summary> BattleNpc can target other <see cref="BattleNpc"/>s </summary>
|
||||
BattleNpcToBattleNpc,
|
||||
/// <summary> BattleNpc can target <see cref="Player"/>s and their <see cref="Pet"/>s </summary>
|
||||
BattleNpcToPlayer,
|
||||
}
|
||||
|
||||
/// <summary> Type of AOE region to create </summary>
|
||||
enum TargetFindAOEType : byte
|
||||
{
|
||||
None,
|
||||
/// <summary> Really a cylinder, uses maxDistance parameter in SetAOEType </summary>
|
||||
Circle,
|
||||
/// <summary> Create a cone with param in radians </summary>
|
||||
Cone,
|
||||
/// <summary> Box using self/target coords and </summary>
|
||||
Box
|
||||
}
|
||||
|
||||
/// <summary> Set AOE around self or target </summary>
|
||||
enum TargetFindAOETarget : byte
|
||||
{
|
||||
/// <summary> Set AOE's origin at target's position </summary>
|
||||
Target,
|
||||
/// <summary> Set AOE's origin to own position. </summary>
|
||||
Self
|
||||
}
|
||||
|
||||
/// <summary> Target finding helper class </summary>
|
||||
class TargetFind
|
||||
{
|
||||
private Character owner;
|
||||
private Character masterTarget; // if target is a pet, this is the owner
|
||||
private TargetFindCharacterType findType;
|
||||
private ValidTarget validTarget;
|
||||
private TargetFindAOETarget aoeTarget;
|
||||
private TargetFindAOEType aoeType;
|
||||
private Vector3 targetPosition;
|
||||
private float maxDistance;
|
||||
private float param;
|
||||
private List<Character> targets;
|
||||
|
||||
public TargetFind(Character owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
this.findType = TargetFindCharacterType.None;
|
||||
this.validTarget = ValidTarget.Enemy;
|
||||
this.aoeType = TargetFindAOEType.None;
|
||||
this.aoeTarget = TargetFindAOETarget.Target;
|
||||
this.targetPosition = null;
|
||||
this.maxDistance = 0.0f;
|
||||
this.param = 0.0f;
|
||||
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>
|
||||
/// Call this before <see cref="FindWithinArea"/> <para/>
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">
|
||||
/// <see cref="TargetFindAOEType.Circle"/> - radius of circle <para/>
|
||||
/// <see cref="TargetFindAOEType.Cone"/> - height of cone <para/>
|
||||
/// <see cref="TargetFindAOEType.Box"/> - width of box / 2 (todo: set box length not just between user and target)
|
||||
/// </param>
|
||||
/// <param name="param"> param in degrees of cone (todo: probably use radians and forget converting at runtime) </param>
|
||||
public void SetAOEType(ValidTarget validTarget, TargetFindAOEType aoeType, TargetFindAOETarget aoeTarget, float maxDistance = -1.0f, float param = -1.0f)
|
||||
{
|
||||
this.validTarget = validTarget;
|
||||
this.aoeType = aoeType;
|
||||
this.maxDistance = maxDistance != -1.0f ? maxDistance : 0.0f;
|
||||
this.param = param != -1.0f ? param : 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this to prepare Box AOE
|
||||
/// </summary>
|
||||
/// <param name="validTarget"></param>
|
||||
/// <param name="aoeTarget"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <param name="width"></param>
|
||||
public void SetAOEBox(ValidTarget validTarget, TargetFindAOETarget aoeTarget, float length, float width)
|
||||
{
|
||||
this.validTarget = validTarget;
|
||||
this.aoeType = TargetFindAOEType.Box;
|
||||
this.aoeTarget = aoeTarget;
|
||||
float x = owner.positionX - (float)Math.Cos(owner.rotation + (float)(Math.PI / 2)) * (length);
|
||||
float z = owner.positionZ + (float)Math.Sin(owner.rotation + (float)(Math.PI / 2)) * (length);
|
||||
this.targetPosition = new Vector3(x, owner.positionY, z);
|
||||
this.maxDistance = width;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find and try to add a single target to target list
|
||||
/// </summary>
|
||||
public void FindTarget(Character target, ValidTarget flags)
|
||||
{
|
||||
validTarget = flags;
|
||||
// todo: maybe this should only be set if successfully added?
|
||||
AddTarget(target, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para> Call SetAOEType before calling this </para>
|
||||
/// Find targets within area set by <see cref="SetAOEType"/>
|
||||
/// </summary>
|
||||
public void FindWithinArea(Character target, ValidTarget flags, TargetFindAOETarget aoeTarget)
|
||||
{
|
||||
validTarget = flags;
|
||||
// are we creating aoe circles around target or self
|
||||
if (aoeTarget == TargetFindAOETarget.Self)
|
||||
this.targetPosition = owner.GetPosAsVector3();
|
||||
else
|
||||
this.targetPosition = target.GetPosAsVector3();
|
||||
|
||||
masterTarget = TryGetMasterTarget(target) ?? target;
|
||||
|
||||
// todo: this is stupid
|
||||
bool withPet = (flags & ValidTarget.Ally) != 0 || masterTarget.allegiance != owner.allegiance;
|
||||
|
||||
if (masterTarget != null)
|
||||
targets.Add(masterTarget);
|
||||
|
||||
if (IsPlayer(owner))
|
||||
{
|
||||
if (masterTarget is Player)
|
||||
{
|
||||
findType = TargetFindCharacterType.PlayerToPlayer;
|
||||
|
||||
if (masterTarget.currentParty != null)
|
||||
{
|
||||
if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0)
|
||||
AddAllInAlliance(masterTarget, withPet);
|
||||
else
|
||||
AddAllInParty(masterTarget, withPet);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddTarget(masterTarget, withPet);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
findType = TargetFindCharacterType.PlayerToBattleNpc;
|
||||
AddAllBattleNpcs(masterTarget, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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 & ValidTarget.HitAll) != 0)
|
||||
//{
|
||||
// AddAllInZone(masterTarget, withPet);
|
||||
//}
|
||||
|
||||
AddAllInAlliance(target, withPet);
|
||||
|
||||
if (findType == TargetFindCharacterType.BattleNpcToPlayer)
|
||||
{
|
||||
if (owner.allegiance == CharacterTargetingAllegiance.Player)
|
||||
AddAllInZone(masterTarget, withPet);
|
||||
else
|
||||
AddAllInHateList();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find targets within a box using owner's coordinates and target's coordinates as length
|
||||
/// with corners being `maxDistance` yalms to either side of self and target
|
||||
/// </summary>
|
||||
private bool IsWithinBox(Character target, bool withPet)
|
||||
{
|
||||
if (aoeTarget == TargetFindAOETarget.Self)
|
||||
targetPosition = owner.GetPosAsVector3();
|
||||
else
|
||||
targetPosition = target.GetPosAsVector3();
|
||||
|
||||
var myPos = owner.GetPosAsVector3();
|
||||
var angle = Vector3.GetAngle(myPos, targetPosition);
|
||||
|
||||
// todo: actually check this works..
|
||||
var myCorner = myPos.NewHorizontalVector(angle, maxDistance);
|
||||
var myCorner2 = myPos.NewHorizontalVector(angle, -maxDistance);
|
||||
|
||||
var targetCorner = targetPosition.NewHorizontalVector(angle, maxDistance);
|
||||
var targetCorner2 = targetPosition.NewHorizontalVector(angle, -maxDistance);
|
||||
|
||||
return target.GetPosAsVector3().IsWithinBox(targetCorner2, myCorner);
|
||||
}
|
||||
|
||||
private bool IsWithinCone(Character target, bool withPet)
|
||||
{
|
||||
// todo: make this actual cone
|
||||
return owner.IsFacing(target, param) && Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) < maxDistance;
|
||||
}
|
||||
|
||||
private void AddTarget(Character target, bool withPet)
|
||||
{
|
||||
if (CanTarget(target))
|
||||
{
|
||||
// todo: add pets too
|
||||
targets.Add(target);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllInParty(Character target, bool withPet)
|
||||
{
|
||||
var party = target.currentParty as Party;
|
||||
if (party != null)
|
||||
{
|
||||
foreach (var actorId in party.members)
|
||||
{
|
||||
AddTarget(owner.zone.FindActorInArea<Character>(actorId), withPet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllInAlliance(Character target, bool withPet)
|
||||
{
|
||||
// todo:
|
||||
AddAllInParty(target, withPet);
|
||||
}
|
||||
|
||||
private void AddAllBattleNpcs(Character target, bool withPet)
|
||||
{
|
||||
var actors = owner.zone.GetActorsAroundActor<BattleNpc>(owner, 50);
|
||||
|
||||
foreach (BattleNpc actor in actors)
|
||||
{
|
||||
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");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var hateEntry in ((BattleNpc)owner).hateContainer.GetHateList())
|
||||
{
|
||||
AddTarget(hateEntry.Value.actor, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanTarget(Character target, bool withPet = false, bool retarget = false, bool ignoreAOE = false)
|
||||
{
|
||||
// already targeted, dont target again
|
||||
if (target == null || !retarget && targets.Contains(target))
|
||||
return false;
|
||||
|
||||
// cant target dead
|
||||
if ((validTarget & ValidTarget.Corpse) == 0 && target.IsDead())
|
||||
return false;
|
||||
|
||||
if ((validTarget & ValidTarget.Ally) != 0 && target.allegiance != owner.allegiance)
|
||||
return false;
|
||||
|
||||
if ((validTarget & ValidTarget.Enemy) != 0 && target.allegiance == owner.allegiance)
|
||||
return false;
|
||||
|
||||
if ((validTarget & ValidTarget.NPC) != 0 && target.isStatic)
|
||||
return false;
|
||||
|
||||
// todo: why is player always zoning?
|
||||
// cant target if zoning
|
||||
if (target is Player && ((Player)target).playerSession.isUpdatesLocked)
|
||||
{
|
||||
owner.aiContainer.ChangeTarget(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (/*target.isZoning || owner.isZoning || */target.zone != owner.zone)
|
||||
return false;
|
||||
|
||||
if (validTarget == ValidTarget.Self && aoeType == TargetFindAOEType.None && owner != target)
|
||||
return false;
|
||||
|
||||
// this is fuckin retarded, think of a better way l8r
|
||||
if (!ignoreAOE)
|
||||
{
|
||||
// hit everything within zone or within aoe region
|
||||
if (maxDistance == -1.0f || aoeType == TargetFindAOEType.Circle && !IsWithinCircle(target, maxDistance))
|
||||
return false;
|
||||
|
||||
if (aoeType == TargetFindAOEType.Cone && !IsWithinCone(target, withPet))
|
||||
return false;
|
||||
|
||||
if (aoeType == TargetFindAOEType.Box && !IsWithinBox(target, withPet))
|
||||
return false;
|
||||
|
||||
if (aoeType == TargetFindAOEType.None && targets.Count != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsWithinCircle(Character target, float maxDistance)
|
||||
{
|
||||
// todo: make y diff modifiable?
|
||||
if (Math.Abs(owner.positionX - target.positionY) > 6.0f)
|
||||
return false;
|
||||
|
||||
if (this.targetPosition == null)
|
||||
this.targetPosition = aoeTarget == TargetFindAOETarget.Self ? owner.GetPosAsVector3() : masterTarget.GetPosAsVector3();
|
||||
return target.GetPosAsVector3().IsWithinCircle(targetPosition, param);
|
||||
}
|
||||
|
||||
private bool IsPlayer(Character target)
|
||||
{
|
||||
if (target is Player)
|
||||
return true;
|
||||
|
||||
// treat player owned pets as players too
|
||||
return TryGetMasterTarget(target) is Player;
|
||||
}
|
||||
|
||||
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<PetController>();
|
||||
if (controller != null)
|
||||
return controller.GetPetMaster();
|
||||
}
|
||||
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
|
||||
if (owner is BattleNpc && ((BattleNpc)owner).hateContainer.GetMostHatedTarget() != target)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Character GetValidTarget(Character target, ValidTarget findFlags)
|
||||
{
|
||||
if (target == null || target is Player && ((Player)target).playerSession.isUpdatesLocked)
|
||||
return null;
|
||||
|
||||
if ((findFlags & ValidTarget.Ally) != 0)
|
||||
{
|
||||
return owner.pet;
|
||||
}
|
||||
|
||||
// todo: this is beyond retarded
|
||||
var oldFlags = this.validTarget;
|
||||
this.validTarget = findFlags;
|
||||
if (CanTarget(target, false, true))
|
||||
{
|
||||
this.validTarget = oldFlags;
|
||||
return target;
|
||||
}
|
||||
this.validTarget = oldFlags;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user