mirror of
https://github.com/GTA-ASM/SanAndreasUnity
synced 2024-11-29 23:40:23 +00:00
227 lines
No EOL
7.4 KiB
C#
227 lines
No EOL
7.4 KiB
C#
using System.Linq;
|
|
using SanAndreasUnity.Utilities;
|
|
using UnityEngine;
|
|
|
|
namespace SanAndreasUnity.Behaviours.Peds.AI
|
|
{
|
|
public class UpdateAttackParams
|
|
{
|
|
public bool wasInRange = false;
|
|
public float timeWhenAddedFireOffset = 0f;
|
|
public float timeUntilOffsetChanges = 1f;
|
|
public Vector3 newFireOffset = Vector3.zero;
|
|
|
|
public void Cleanup()
|
|
{
|
|
this.wasInRange = false;
|
|
this.timeWhenAddedFireOffset = 0f;
|
|
this.timeUntilOffsetChanges = 1f;
|
|
this.newFireOffset = Vector3.zero;
|
|
}
|
|
}
|
|
|
|
public class ChaseState : BaseState
|
|
{
|
|
public Ped TargetPed { get; private set; }
|
|
|
|
private readonly UpdateAttackParams _updateAttackParams = new UpdateAttackParams();
|
|
|
|
private static int[] s_weaponSlotsOrdered = new[]
|
|
{
|
|
WeaponSlot.Machine,
|
|
WeaponSlot.Submachine,
|
|
WeaponSlot.Rifle,
|
|
WeaponSlot.Heavy,
|
|
WeaponSlot.Shotgun,
|
|
WeaponSlot.Pistol,
|
|
};
|
|
|
|
|
|
public override void OnBecameActive()
|
|
{
|
|
base.OnBecameActive();
|
|
|
|
_updateAttackParams.Cleanup();
|
|
|
|
this.TargetPed = this.ParameterForEnteringState as Ped;
|
|
if (this.TargetPed != null)
|
|
_enemyPeds.AddIfNotPresent(this.TargetPed);
|
|
}
|
|
|
|
public override void UpdateState2Seconds()
|
|
{
|
|
this.ChooseBestWeapon();
|
|
|
|
if (null == _ped.CurrentWeapon)
|
|
{
|
|
// we have no weapon to attack with, or no ammo
|
|
_pedAI.StartWalkingAround();
|
|
return;
|
|
}
|
|
|
|
if (null == this.TargetPed)
|
|
{
|
|
this.TargetPed = this.GetNextPedToAttack();
|
|
return;
|
|
}
|
|
|
|
if (this.IsInRange(this.TargetPed))
|
|
return; // current target is in range, continue attacking it
|
|
|
|
this.TargetPed = this.GetNextPedToAttack();
|
|
|
|
/*Ped nextPedToAttack = this.GetNextPedToAttack();
|
|
if (null == nextPedToAttack || nextPedToAttack == this.TargetPed)
|
|
return;
|
|
|
|
// check if we should switch to next target
|
|
|
|
Vector3 myPosition = _ped.transform.position;
|
|
float currentDistance = Vector3.Distance(this.TargetPed.transform.position, myPosition);
|
|
float nextDistance = Vector3.Distance(nextPedToAttack.transform.position, myPosition);
|
|
|
|
if (currentDistance - nextDistance > 6f)
|
|
{
|
|
// next target is closer by some delta value - switch to it
|
|
this.TargetPed = nextPedToAttack;
|
|
return;
|
|
}*/
|
|
}
|
|
|
|
public override void UpdateState()
|
|
{
|
|
if (null == this.TargetPed)
|
|
this.TargetPed = this.GetNextPedToAttack();
|
|
|
|
if (null == this.TargetPed)
|
|
{
|
|
// we finished attacking all enemies, now start walking
|
|
_pedAI.StartWalkingAround();
|
|
return;
|
|
}
|
|
|
|
if (_ped.IsInVehicle)
|
|
{
|
|
_ped.OnSubmitPressed();
|
|
return;
|
|
}
|
|
|
|
this.UpdateAttackOnPed(this.TargetPed, _updateAttackParams);
|
|
}
|
|
|
|
public Ped GetNextPedToAttack()
|
|
{
|
|
_enemyPeds.RemoveDeadObjectsIfNotEmpty();
|
|
if (_enemyPeds.Count == 0)
|
|
return null;
|
|
|
|
Vector3 myPosition = _ped.transform.position;
|
|
|
|
Ped closestPed = _enemyPeds.MinBy(p => Vector3.Distance(p.transform.position, myPosition), null);
|
|
|
|
return closestPed;
|
|
}
|
|
|
|
public bool IsInRange(Ped ped)
|
|
{
|
|
return Vector3.Distance(ped.transform.position, _ped.transform.position) < 10f;
|
|
}
|
|
|
|
public void UpdateAttackOnPed(Ped ped, UpdateAttackParams updateAttackParams)
|
|
{
|
|
if (Time.time - updateAttackParams.timeWhenAddedFireOffset > updateAttackParams.timeUntilOffsetChanges)
|
|
{
|
|
updateAttackParams.timeWhenAddedFireOffset = Time.time;
|
|
updateAttackParams.newFireOffset = Random.onUnitSphere * 0.2f;
|
|
}
|
|
|
|
Vector3 myHeadPos = GetHeadOrTransform(this.MyPed).position;
|
|
Vector3 targetHeadPos = GetHeadOrTransform(ped).position;
|
|
Vector3 targetChestPos = GetChestPosition(ped) + updateAttackParams.newFireOffset;
|
|
Vector3 firePos = this.MyPed.IsAiming ? this.MyPed.FirePosition : myHeadPos;
|
|
|
|
Vector3 diff = targetHeadPos - myHeadPos;
|
|
Vector3 dir = diff.normalized;
|
|
this.MyPed.Heading = dir;
|
|
|
|
Vector3 aimDir = (targetChestPos - firePos).normalized;
|
|
|
|
// fix for stuttering which happens when target ped is too close:
|
|
// we assign AimDir here, which changes skeleton and therefore changes fire position, which then
|
|
// changes AimDir in next frame
|
|
if (diff.ToVec2WithXAndZ().magnitude < 2.5f)
|
|
aimDir = (ped.transform.position + updateAttackParams.newFireOffset - _ped.transform.position).normalized;
|
|
|
|
if (this.MyPed.IsInVehicle)
|
|
{
|
|
if (diff.magnitude < 10f)
|
|
{
|
|
this.MyPed.AimDirection = aimDir;
|
|
if (!this.MyPed.IsAiming)
|
|
this.MyPed.OnAimButtonPressed();
|
|
this.MyPed.IsFireOn = true;
|
|
}
|
|
else
|
|
{
|
|
if (this.MyPed.IsAiming)
|
|
this.MyPed.OnAimButtonPressed();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float rangeRequired = updateAttackParams.wasInRange ? 10f : 8f;
|
|
|
|
updateAttackParams.wasInRange = false;
|
|
|
|
if (diff.magnitude < rangeRequired)
|
|
{
|
|
updateAttackParams.wasInRange = true;
|
|
this.MyPed.AimDirection = aimDir;
|
|
this.MyPed.IsAimOn = true;
|
|
this.MyPed.IsFireOn = true;
|
|
}
|
|
else if (Vector2.Distance(ped.transform.position.ToVec2WithXAndZ(), this.MyPed.transform.position.ToVec2WithXAndZ()) > 3f)
|
|
{
|
|
this.MyPed.IsRunOn = true;
|
|
this.MyPed.Movement = dir;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Transform GetHeadOrTransform(Ped ped)
|
|
{
|
|
return ped.PlayerModel.Head != null ? ped.PlayerModel.Head : ped.transform;
|
|
}
|
|
|
|
private static Vector3 GetChestPosition(Ped ped)
|
|
{
|
|
return ped.PlayerModel.UpperSpine != null
|
|
? ped.PlayerModel.UpperSpine.position
|
|
: ped.transform.position + new Vector3(0f, 0.3f, 0f);
|
|
}
|
|
|
|
public void ChooseBestWeapon()
|
|
{
|
|
_ped.WeaponHolder.SwitchWeapon(this.GetBestWeaponSlot());
|
|
}
|
|
|
|
public int GetBestWeaponSlot()
|
|
{
|
|
for (int i = 0; i < s_weaponSlotsOrdered.Length; i++)
|
|
{
|
|
int slot = s_weaponSlotsOrdered[i];
|
|
Weapon weapon = _ped.WeaponHolder.GetWeaponAtSlot(slot);
|
|
if (weapon != null && weapon.TotalAmmo > 0)
|
|
return slot;
|
|
}
|
|
|
|
return WeaponSlot.Hand;
|
|
}
|
|
|
|
public bool CanStartChasing()
|
|
{
|
|
int slot = this.GetBestWeaponSlot();
|
|
return slot != WeaponSlot.Hand;
|
|
}
|
|
}
|
|
} |