mirror of
https://github.com/GTA-ASM/SanAndreasUnity
synced 2024-12-05 02:09:27 +00:00
203 lines
No EOL
7.6 KiB
C#
203 lines
No EOL
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using SanAndreasUnity.Importing.Items.Definitions;
|
|
using SanAndreasUnity.Importing.Paths;
|
|
using UnityEngine;
|
|
using SanAndreasUnity.Utilities;
|
|
using SanAndreasUnity.Net;
|
|
|
|
namespace SanAndreasUnity.Behaviours
|
|
{
|
|
public enum PedAction
|
|
{
|
|
WalkingAround,
|
|
Chasing,
|
|
Escaping
|
|
}
|
|
|
|
public class PedAI : MonoBehaviour
|
|
{
|
|
private static readonly List<PedAI> s_allPedAIs = new List<PedAI>();
|
|
public static IReadOnlyList<PedAI> AllPedAIs => s_allPedAIs;
|
|
|
|
private static bool s_subscribedToPedOnDamageEvent = false;
|
|
|
|
[SerializeField] private Vector3 currentNodePos;
|
|
[SerializeField] private Vector3 targetNodePos;
|
|
[SerializeField] private Vector2 targetNodeOffset; // Adding random offset to prevent peds to have the exact destination
|
|
|
|
public PedAction Action;
|
|
|
|
/// <summary>
|
|
/// The node where the Ped starts
|
|
/// </summary>
|
|
public PathNode CurrentNode;
|
|
|
|
/// <summary>
|
|
/// The node the Ped is targeting
|
|
/// </summary>
|
|
public PathNode TargetNode;
|
|
|
|
/// <summary>
|
|
/// The ped the Ped is chasing
|
|
/// </summary>
|
|
public Ped TargetPed;
|
|
|
|
public Ped MyPed { get; private set; }
|
|
|
|
|
|
private void Awake()
|
|
{
|
|
this.MyPed = this.GetComponentOrThrow<Ped>();
|
|
|
|
if (!s_subscribedToPedOnDamageEvent)
|
|
{
|
|
s_subscribedToPedOnDamageEvent = true;
|
|
Ped.onDamaged += OnPedDamaged;
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
s_allPedAIs.Add(this);
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
s_allPedAIs.Remove(this);
|
|
}
|
|
|
|
private static void OnPedDamaged(Ped hitPed, DamageInfo dmgInfo, Ped.DamageResult dmgResult)
|
|
{
|
|
var hitPedAi = hitPed.GetComponent<PedAI>();
|
|
if (null == hitPedAi)
|
|
return;
|
|
|
|
if (hitPed.PedDef != null &&
|
|
(hitPed.PedDef.DefaultType == PedestrianType.Criminal ||
|
|
hitPed.PedDef.DefaultType == PedestrianType.Cop ||
|
|
hitPed.PedDef.DefaultType.IsGangMember()))
|
|
{
|
|
hitPedAi.TargetPed = dmgInfo.GetAttackerPed();
|
|
hitPedAi.Action = PedAction.Chasing;
|
|
}
|
|
else
|
|
hitPedAi.Action = PedAction.Escaping;
|
|
}
|
|
|
|
// Update is called once per frame
|
|
void Update()
|
|
{
|
|
this.MyPed.ResetInput();
|
|
if (NetStatus.IsServer)
|
|
{
|
|
switch (this.Action)
|
|
{
|
|
case PedAction.WalkingAround:
|
|
currentNodePos = CurrentNode.Position;
|
|
targetNodePos = TargetNode.Position;
|
|
if (Vector2.Distance(new Vector2(this.gameObject.transform.position.x, this.gameObject.transform.position.z), new Vector2(targetNodePos.x, targetNodePos.z)) < 3)
|
|
{
|
|
// arrived at target node
|
|
PathNode previousNode = CurrentNode;
|
|
CurrentNode = TargetNode;
|
|
TargetNode = GetNextPathNode(previousNode, CurrentNode);
|
|
targetNodeOffset = new Vector2(UnityEngine.Random.Range(-2, 2), UnityEngine.Random.Range(-2, 2));
|
|
}
|
|
this.MyPed.IsWalkOn = true;
|
|
Vector3 dest = targetNodePos + new Vector3(targetNodeOffset.x, 0, targetNodeOffset.y);
|
|
this.MyPed.Movement = (dest - this.MyPed.transform.position).normalized;
|
|
this.MyPed.Heading = this.MyPed.Movement;
|
|
break;
|
|
case PedAction.Chasing:
|
|
if (this.TargetPed != null)
|
|
{
|
|
Vector3 diff = GetHeadOrTransform(this.TargetPed).position - GetHeadOrTransform(this.MyPed).position;
|
|
Vector3 dir = diff.normalized;
|
|
if (diff.magnitude < 10f)
|
|
{
|
|
this.MyPed.Heading = dir;
|
|
this.MyPed.AimDirection = dir;
|
|
this.MyPed.IsAimOn = true;
|
|
this.MyPed.IsFireOn = true;
|
|
}
|
|
else
|
|
{
|
|
this.MyPed.IsRunOn = true;
|
|
this.MyPed.Movement = dir;
|
|
this.MyPed.Heading = dir;
|
|
}
|
|
}
|
|
else // The target is dead/disconnected
|
|
{
|
|
this.Action = PedAction.WalkingAround;
|
|
}
|
|
break;
|
|
case PedAction.Escaping:
|
|
currentNodePos = CurrentNode.Position;
|
|
targetNodePos = TargetNode.Position;
|
|
if (Vector2.Distance(new Vector2(this.gameObject.transform.position.x, this.gameObject.transform.position.z), new Vector2(TargetNode.Position.x, TargetNode.Position.z)) < 1f)
|
|
{
|
|
// arrived at target node
|
|
PathNode previousNode = CurrentNode;
|
|
CurrentNode = TargetNode;
|
|
TargetNode = GetNextPathNode(previousNode, CurrentNode);
|
|
}
|
|
this.MyPed.IsSprintOn = true;
|
|
this.MyPed.Movement = (TargetNode.Position - this.MyPed.transform.position).normalized;
|
|
this.MyPed.Heading = this.MyPed.Movement;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Transform GetHeadOrTransform(Ped ped)
|
|
{
|
|
return ped.PlayerModel.Head != null ? ped.PlayerModel.Head : ped.transform;
|
|
}
|
|
|
|
private static PathNode GetNextPathNode(PathNode previousNode, PathNode currentNode)
|
|
{
|
|
var possibilities = new List<PathNode>(
|
|
NodeReader.GetAllAdjacentNodes(currentNode)
|
|
.Where(_ => !_.Equals(previousNode)));
|
|
|
|
if (possibilities.Count > 0)
|
|
{
|
|
return possibilities.RandomElement();
|
|
}
|
|
else
|
|
{
|
|
//No possibilities found, returning to previous node
|
|
return previousNode;
|
|
}
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
Gizmos.color = Color.green;
|
|
Gizmos.DrawLine(CurrentNode.Position, TargetNode.Position);
|
|
Gizmos.DrawWireSphere(CurrentNode.Position, CurrentNode.PathWidth / 2f);
|
|
Gizmos.DrawWireSphere(TargetNode.Position, TargetNode.PathWidth / 2f);
|
|
|
|
Gizmos.color = Color.yellow;
|
|
|
|
NodeReader.GetAllAdjacentNodes(TargetNode)
|
|
.Except(new[] {CurrentNode})
|
|
.ForEach(node =>
|
|
{
|
|
Gizmos.DrawLine(TargetNode.Position, node.Position);
|
|
Gizmos.DrawWireSphere(node.Position, node.PathWidth / 2f);
|
|
});
|
|
NodeReader.GetAllAdjacentNodes(CurrentNode)
|
|
.Except(new[] {TargetNode})
|
|
.ForEach(node =>
|
|
{
|
|
Gizmos.DrawLine(CurrentNode.Position, node.Position);
|
|
Gizmos.DrawWireSphere(node.Position, node.PathWidth / 2f);
|
|
});
|
|
}
|
|
}
|
|
|
|
} |