SanAndreasUnity/Assets/Scripts/Behaviours/Ped/PedAI.cs
2021-09-11 17:08:29 +02:00

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);
});
}
}
}