2021-12-15 03:31:57 +01:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
|
|
using GTA;
|
|
|
|
|
using GTA.Native;
|
|
|
|
|
using GTA.Math;
|
|
|
|
|
|
2021-12-27 10:15:52 +01:00
|
|
|
|
namespace CoopClient.Entities.NPC
|
2021-12-15 03:31:57 +01:00
|
|
|
|
{
|
2021-12-27 10:15:52 +01:00
|
|
|
|
internal partial class EntitiesNPC
|
2021-12-15 03:31:57 +01:00
|
|
|
|
{
|
|
|
|
|
#region -- ON FOOT --
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The latest character rotation (may not have been applied yet)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Vector3 Rotation { get; internal set; }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The latest character velocity (may not have been applied yet)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Vector3 Velocity { get; internal set; }
|
|
|
|
|
internal byte Speed { get; set; }
|
|
|
|
|
private bool LastIsJumping = false;
|
|
|
|
|
internal bool IsJumping { get; set; }
|
|
|
|
|
internal bool IsRagdoll { get; set; }
|
|
|
|
|
internal bool IsOnFire { get; set; }
|
|
|
|
|
internal bool IsAiming { get; set; }
|
|
|
|
|
internal bool IsShooting { get; set; }
|
|
|
|
|
internal bool IsReloading { get; set; }
|
|
|
|
|
internal uint CurrentWeaponHash { get; set; }
|
|
|
|
|
private Dictionary<uint, bool> LastWeaponComponents = null;
|
|
|
|
|
internal Dictionary<uint, bool> WeaponComponents { get; set; } = null;
|
|
|
|
|
private int LastWeaponObj = 0;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
private void DisplayOnFoot()
|
|
|
|
|
{
|
|
|
|
|
if (Character.IsInVehicle())
|
|
|
|
|
{
|
|
|
|
|
Character.Task.LeaveVehicle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (MainVehicle != null)
|
|
|
|
|
{
|
|
|
|
|
MainVehicle = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsOnFire && !Character.IsOnFire)
|
|
|
|
|
{
|
|
|
|
|
Character.IsInvincible = false;
|
|
|
|
|
|
|
|
|
|
Function.Call(Hash.START_ENTITY_FIRE, Character.Handle);
|
|
|
|
|
}
|
|
|
|
|
else if (!IsOnFire && Character.IsOnFire)
|
|
|
|
|
{
|
|
|
|
|
Function.Call(Hash.STOP_ENTITY_FIRE, Character.Handle);
|
|
|
|
|
|
|
|
|
|
Character.IsInvincible = true;
|
|
|
|
|
|
|
|
|
|
if (Character.IsDead)
|
|
|
|
|
{
|
|
|
|
|
Character.Resurrect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsJumping && !LastIsJumping)
|
|
|
|
|
{
|
|
|
|
|
Character.Task.Jump();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LastIsJumping = IsJumping;
|
|
|
|
|
|
2021-12-24 06:33:00 +01:00
|
|
|
|
if (IsRagdoll)
|
2021-12-15 03:31:57 +01:00
|
|
|
|
{
|
2021-12-24 06:33:00 +01:00
|
|
|
|
if (!Character.IsRagdoll)
|
|
|
|
|
{
|
|
|
|
|
// CanRagdoll = true, inside this function
|
|
|
|
|
Character.Ragdoll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
2021-12-15 03:31:57 +01:00
|
|
|
|
}
|
|
|
|
|
else if (!IsRagdoll && Character.IsRagdoll)
|
|
|
|
|
{
|
|
|
|
|
Character.CanRagdoll = false;
|
2021-12-24 06:33:00 +01:00
|
|
|
|
Character.Task.ClearAllImmediately();
|
|
|
|
|
|
|
|
|
|
return;
|
2021-12-15 03:31:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-24 06:33:00 +01:00
|
|
|
|
if (IsJumping || IsOnFire)
|
2021-12-15 03:31:57 +01:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-27 10:15:52 +01:00
|
|
|
|
if (IsReloading && Character.IsInRange(Position, 0.5f))
|
2021-12-15 03:31:57 +01:00
|
|
|
|
{
|
|
|
|
|
if (!Character.IsReloading)
|
|
|
|
|
{
|
|
|
|
|
Character.Task.ClearAll();
|
|
|
|
|
Character.Task.ReloadWeapon();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-24 03:16:16 +01:00
|
|
|
|
return;
|
2021-12-15 03:31:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-16 22:26:45 +01:00
|
|
|
|
if (Character.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(LastWeaponComponents))
|
2021-12-15 03:31:57 +01:00
|
|
|
|
{
|
|
|
|
|
Character.Weapons.RemoveAll();
|
|
|
|
|
|
|
|
|
|
if (CurrentWeaponHash != (uint)WeaponHash.Unarmed)
|
|
|
|
|
{
|
|
|
|
|
if (WeaponComponents == null || WeaponComponents.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
Character.Weapons.Give((WeaponHash)CurrentWeaponHash, -1, true, true);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LastWeaponObj = Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0);
|
|
|
|
|
|
|
|
|
|
foreach (KeyValuePair<uint, bool> comp in WeaponComponents)
|
|
|
|
|
{
|
|
|
|
|
if (comp.Value)
|
|
|
|
|
{
|
|
|
|
|
Function.Call(Hash.GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, LastWeaponObj, comp.Key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-27 17:26:16 +01:00
|
|
|
|
Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, LastWeaponObj, Character.Handle);
|
2021-12-15 03:31:57 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LastWeaponComponents = WeaponComponents;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsShooting)
|
|
|
|
|
{
|
|
|
|
|
if (!Character.IsInRange(Position, 0.5f))
|
|
|
|
|
{
|
|
|
|
|
Function.Call(Hash.TASK_GO_TO_COORD_WHILE_AIMING_AT_COORD, Character.Handle, Position.X, Position.Y,
|
2021-12-27 17:26:16 +01:00
|
|
|
|
Position.Z, AimCoords.X, AimCoords.Y, AimCoords.Z, 3f, true, 0x3F000000, 0x40800000, false, 0, false,
|
2021-12-15 03:31:57 +01:00
|
|
|
|
unchecked((int)FiringPattern.FullAuto));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Function.Call(Hash.TASK_SHOOT_AT_COORD, Character.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z, 1500, unchecked((int)FiringPattern.FullAuto));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (IsAiming)
|
|
|
|
|
{
|
|
|
|
|
if (!Character.IsInRange(Position, 0.5f))
|
|
|
|
|
{
|
|
|
|
|
Function.Call(Hash.TASK_GO_TO_COORD_WHILE_AIMING_AT_COORD, Character.Handle, Position.X, Position.Y,
|
2021-12-27 17:26:16 +01:00
|
|
|
|
Position.Z, AimCoords.X, AimCoords.Y, AimCoords.Z, 3f, false, 0x3F000000, 0x40800000, false, 512, false,
|
2021-12-15 03:31:57 +01:00
|
|
|
|
unchecked((int)FiringPattern.FullAuto));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Character.Task.AimAt(AimCoords, 100);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
WalkTo();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool LastMoving;
|
|
|
|
|
private void WalkTo()
|
|
|
|
|
{
|
2021-12-27 17:26:16 +01:00
|
|
|
|
if (!Character.IsInRange(Position, 6.0f))
|
|
|
|
|
{
|
|
|
|
|
Character.PositionNoOffset = Position;
|
|
|
|
|
Character.Rotation = Rotation;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-24 03:16:16 +01:00
|
|
|
|
Vector3 predictPosition = Position + (Position - Character.Position) + Velocity;
|
|
|
|
|
float range = predictPosition.DistanceToSquared(Character.Position);
|
|
|
|
|
|
|
|
|
|
switch (Speed)
|
2021-12-15 03:31:57 +01:00
|
|
|
|
{
|
2021-12-24 03:16:16 +01:00
|
|
|
|
case 1:
|
|
|
|
|
if (!Character.IsWalking || range > 0.25f)
|
|
|
|
|
{
|
|
|
|
|
float nrange = range * 2;
|
|
|
|
|
if (nrange > 1.0f)
|
|
|
|
|
{
|
|
|
|
|
nrange = 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Character.Task.GoStraightTo(predictPosition);
|
|
|
|
|
Function.Call(Hash.SET_PED_DESIRED_MOVE_BLEND_RATIO, Character.Handle, nrange);
|
|
|
|
|
}
|
|
|
|
|
LastMoving = true;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
if (!Character.IsRunning || range > 0.50f)
|
|
|
|
|
{
|
|
|
|
|
Character.Task.RunTo(predictPosition, true);
|
|
|
|
|
Function.Call(Hash.SET_PED_DESIRED_MOVE_BLEND_RATIO, Character.Handle, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
LastMoving = true;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
if (!Character.IsSprinting || range > 0.75f)
|
|
|
|
|
{
|
|
|
|
|
Function.Call(Hash.TASK_GO_STRAIGHT_TO_COORD, Character.Handle, predictPosition.X, predictPosition.Y, predictPosition.Z, 3.0f, -1, 0.0f, 0.0f);
|
|
|
|
|
Function.Call(Hash.SET_RUN_SPRINT_MULTIPLIER_FOR_PLAYER, Character.Handle, 1.49f);
|
|
|
|
|
Function.Call(Hash.SET_PED_DESIRED_MOVE_BLEND_RATIO, Character.Handle, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
LastMoving = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2021-12-27 17:26:16 +01:00
|
|
|
|
if (!Character.IsInRange(Position, 0.5f))
|
2021-12-27 10:15:52 +01:00
|
|
|
|
{
|
2021-12-27 17:26:16 +01:00
|
|
|
|
Character.Task.RunTo(Position, true, 500);
|
2021-12-27 10:15:52 +01:00
|
|
|
|
}
|
|
|
|
|
else if (LastMoving)
|
2021-12-24 03:16:16 +01:00
|
|
|
|
{
|
|
|
|
|
Character.Task.StandStill(2000);
|
|
|
|
|
LastMoving = false;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2021-12-15 03:31:57 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|