2021-07-07 13:36:25 +02:00
using System ;
using System.Drawing ;
using System.Collections.Generic ;
using GTA ;
using GTA.Native ;
using GTA.Math ;
using LemonUI.Elements ;
2021-11-28 23:35:37 +01:00
namespace CoopClient.Entities
2021-07-07 13:36:25 +02:00
{
2021-11-28 23:35:37 +01:00
/// <summary>
2021-12-15 00:25:31 +01:00
/// ?
2021-11-28 23:35:37 +01:00
/// </summary>
2021-12-15 03:31:57 +01:00
public partial class EntitiesPed
2021-07-07 13:36:25 +02:00
{
2021-12-18 08:04:17 +01:00
// If this NPC is in a vehicle, we can find the handle of this vehicle in Main.NPCsVehicles[NPCVehHandle] and prevent multiple vehicles from being created
2021-12-15 00:25:31 +01:00
internal long NPCVehHandle { get ; set ; } = 0 ;
2021-12-12 19:31:06 +01:00
/// <summary>
/// 0 = Nothing
/// 1 = Character
/// 2 = Vehicle
/// </summary>
2021-12-12 22:00:06 +01:00
private byte ModelNotFound = 0 ;
2021-07-07 13:36:25 +02:00
private bool AllDataAvailable = false ;
2021-12-15 00:25:31 +01:00
internal bool LastSyncWasFull { get ; set ; } = false ;
2021-11-28 23:35:37 +01:00
/// <summary>
2021-12-15 00:25:31 +01:00
/// Get the last update = TickCount64()
2021-11-28 23:35:37 +01:00
/// </summary>
2021-12-14 19:33:52 +01:00
public ulong LastUpdateReceived { get ; internal set ; }
2021-11-28 23:35:37 +01:00
/// <summary>
2021-12-15 00:25:31 +01:00
/// Get the player latency
2021-11-28 23:35:37 +01:00
/// </summary>
2021-12-14 19:33:52 +01:00
public float Latency { get ; internal set ; }
2021-07-07 13:36:25 +02:00
2021-11-28 23:35:37 +01:00
/// <summary>
/// ?
/// </summary>
2021-12-14 19:33:52 +01:00
public Ped Character { get ; internal set ; }
2021-11-28 23:35:37 +01:00
/// <summary>
2021-12-15 00:25:31 +01:00
/// The latest character health (may not have been applied yet)
2021-11-28 23:35:37 +01:00
/// </summary>
2021-12-14 19:33:52 +01:00
public int Health { get ; internal set ; }
2021-07-07 13:36:25 +02:00
private int LastModelHash = 0 ;
2021-12-12 19:31:06 +01:00
private int CurrentModelHash = 0 ;
2021-11-28 23:35:37 +01:00
/// <summary>
2021-12-15 00:25:31 +01:00
/// The latest character model hash (may not have been applied yet)
2021-11-28 23:35:37 +01:00
/// </summary>
2021-12-12 19:31:06 +01:00
public int ModelHash
{
2021-12-14 19:33:52 +01:00
get = > CurrentModelHash ;
internal set
2021-12-12 19:31:06 +01:00
{
LastModelHash = LastModelHash = = 0 ? value : CurrentModelHash ;
CurrentModelHash = value ;
}
}
2021-12-16 22:05:40 +01:00
private Dictionary < byte , short > LastClothes = null ;
internal Dictionary < byte , short > Clothes { get ; set ; }
2021-11-28 23:35:37 +01:00
/// <summary>
2021-12-15 00:25:31 +01:00
/// The latest character position (may not have been applied yet)
2021-11-28 23:35:37 +01:00
/// </summary>
2021-12-14 19:33:52 +01:00
public Vector3 Position { get ; internal set ; }
2021-12-15 00:25:31 +01:00
internal Blip PedBlip = null ;
2021-12-15 03:31:57 +01:00
internal Vector3 AimCoords { get ; set ; }
2021-07-10 09:41:17 +02:00
2021-11-28 23:35:37 +01:00
internal void DisplayLocally ( string username )
2021-07-07 13:36:25 +02:00
{
/ *
* username : string
* string : null
* ped : npc
* string : value
* ped : player
* /
// Check beforehand whether ped has all the required data
if ( ! AllDataAvailable )
{
if ( ! LastSyncWasFull )
{
2021-12-15 18:24:33 +01:00
if ( Position ! = default )
2021-08-26 17:01:32 +02:00
{
if ( PedBlip ! = null & & PedBlip . Exists ( ) )
{
PedBlip . Position = Position ;
}
else
{
PedBlip = World . CreateBlip ( Position ) ;
PedBlip . Color = BlipColor . White ;
PedBlip . Scale = 0.8f ;
PedBlip . Name = username ;
}
}
2021-07-07 13:36:25 +02:00
return ;
}
AllDataAvailable = true ;
}
2021-12-13 11:51:03 +01:00
if ( ModelNotFound ! = 0 )
{
if ( ModelNotFound = = 1 )
{
if ( CurrentModelHash ! = LastModelHash )
{
ModelNotFound = 0 ;
}
}
else
{
if ( CurrentVehicleModelHash ! = LastVehicleModelHash )
{
ModelNotFound = 0 ;
}
}
}
2021-09-26 21:19:26 +02:00
#region NOT_IN_RANGE
2021-12-13 11:51:03 +01:00
if ( ModelNotFound ! = 0 | | ! Game . Player . Character . IsInRange ( Position , 500f ) )
2021-07-07 13:36:25 +02:00
{
2021-09-26 22:27:49 +02:00
if ( Character ! = null & & Character . Exists ( ) )
{
Character . Kill ( ) ;
Character . MarkAsNoLongerNeeded ( ) ;
Character . Delete ( ) ;
Character = null ;
}
2021-09-26 21:19:26 +02:00
if ( MainVehicle ! = null & & MainVehicle . Exists ( ) & & MainVehicle . IsSeatFree ( VehicleSeat . Driver ) & & MainVehicle . PassengerCount = = 0 )
2021-07-10 09:41:17 +02:00
{
2021-09-26 21:19:26 +02:00
MainVehicle . MarkAsNoLongerNeeded ( ) ;
2021-07-10 09:41:17 +02:00
MainVehicle . Delete ( ) ;
MainVehicle = null ;
}
2021-07-07 13:36:25 +02:00
if ( username ! = null )
{
2021-08-26 17:01:32 +02:00
if ( PedBlip ! = null & & PedBlip . Exists ( ) )
{
PedBlip . Position = Position ;
}
else
2021-07-07 13:36:25 +02:00
{
PedBlip = World . CreateBlip ( Position ) ;
PedBlip . Color = BlipColor . White ;
PedBlip . Scale = 0.8f ;
PedBlip . Name = username ;
}
}
return ;
}
#endregion
#region IS_IN_RANGE
bool characterExist = Character ! = null & & Character . Exists ( ) ;
if ( ! characterExist )
{
2021-08-18 11:47:59 +02:00
if ( ! CreateCharacter ( username ) )
{
return ;
}
2021-07-07 13:36:25 +02:00
}
else if ( LastSyncWasFull )
{
2021-12-12 19:31:06 +01:00
if ( CurrentModelHash ! = LastModelHash )
2021-07-07 13:36:25 +02:00
{
2021-08-18 11:47:59 +02:00
Character . Kill ( ) ;
Character . Delete ( ) ;
if ( ! CreateCharacter ( username ) )
2021-07-07 13:36:25 +02:00
{
2021-08-18 11:47:59 +02:00
return ;
2021-07-07 13:36:25 +02:00
}
}
2021-12-16 22:26:45 +01:00
else if ( ! Clothes . Compare ( LastClothes ) )
2021-07-07 13:36:25 +02:00
{
2021-12-16 22:05:40 +01:00
foreach ( KeyValuePair < byte , short > cloth in Clothes )
2021-07-07 13:36:25 +02:00
{
2021-12-16 22:05:40 +01:00
Function . Call ( Hash . SET_PED_COMPONENT_VARIATION , Character . Handle , cloth . Key , cloth . Value , 0 , 0 ) ;
2021-07-07 13:36:25 +02:00
}
2021-12-16 22:05:40 +01:00
LastClothes = Clothes ;
2021-07-07 13:36:25 +02:00
}
}
2021-07-10 09:41:17 +02:00
if ( username ! = null & & Character . IsVisible & & Character . IsInRange ( Game . Player . Character . Position , 20f ) )
2021-07-07 13:36:25 +02:00
{
float sizeOffset ;
if ( GameplayCamera . IsFirstPersonAimCamActive )
{
Vector3 targetPos = Character . Bones [ Bone . IKHead ] . Position + new Vector3 ( 0 , 0 , 0.10f ) + ( Character . Velocity / Game . FPS ) ;
Function . Call ( Hash . SET_DRAW_ORIGIN , targetPos . X , targetPos . Y , targetPos . Z , 0 ) ;
sizeOffset = Math . Max ( 1f - ( ( GameplayCamera . Position - Character . Position ) . Length ( ) / 30f ) , 0.30f ) ;
}
else
{
Vector3 targetPos = Character . Bones [ Bone . IKHead ] . Position + new Vector3 ( 0 , 0 , 0.35f ) + ( Character . Velocity / Game . FPS ) ;
Function . Call ( Hash . SET_DRAW_ORIGIN , targetPos . X , targetPos . Y , targetPos . Z , 0 ) ;
sizeOffset = Math . Max ( 1f - ( ( GameplayCamera . Position - Character . Position ) . Length ( ) / 25f ) , 0.25f ) ;
}
new ScaledText ( new PointF ( 0 , 0 ) , username , 0.4f * sizeOffset , GTA . UI . Font . ChaletLondon )
{
Outline = true ,
Alignment = GTA . UI . Alignment . Center
} . Draw ( ) ;
Function . Call ( Hash . CLEAR_DRAW_ORIGIN ) ;
}
if ( Character . IsDead )
{
if ( Health < = 0 )
{
return ;
}
Character . IsInvincible = true ;
Character . Resurrect ( ) ;
}
else if ( Character . Health ! = Health )
{
Character . Health = Health ;
if ( Health < = 0 & & ! Character . IsDead )
{
Character . IsInvincible = false ;
Character . Kill ( ) ;
return ;
}
}
2021-07-10 09:41:17 +02:00
if ( IsInVehicle )
{
DisplayInVehicle ( ) ;
}
else
{
DisplayOnFoot ( ) ;
}
#endregion
}
2021-08-18 11:47:59 +02:00
private bool CreateCharacter ( string username )
2021-07-07 13:36:25 +02:00
{
2021-08-26 17:01:32 +02:00
if ( PedBlip ! = null & & PedBlip . Exists ( ) )
{
PedBlip . Delete ( ) ;
PedBlip = null ;
}
2021-12-12 19:31:06 +01:00
Model characterModel = CurrentModelHash . ModelRequest ( ) ;
2021-08-18 11:47:59 +02:00
if ( characterModel = = null )
{
2021-12-12 22:00:06 +01:00
//GTA.UI.Notification.Show($"~r~(Character)Model ({CurrentModelHash}) cannot be loaded!");
2021-12-12 19:31:06 +01:00
ModelNotFound = 1 ;
2021-08-18 11:47:59 +02:00
return false ;
}
Character = World . CreatePed ( characterModel , Position , Rotation . Z ) ;
2021-12-12 22:52:57 +01:00
characterModel . MarkAsNoLongerNeeded ( ) ;
2021-12-15 01:25:16 +01:00
// ?
2021-07-07 13:36:25 +02:00
Character . RelationshipGroup = Main . RelationshipGroup ;
2021-12-15 01:25:16 +01:00
2021-07-10 09:41:17 +02:00
if ( IsInVehicle )
{
Character . IsVisible = false ;
}
2021-07-07 13:36:25 +02:00
Character . BlockPermanentEvents = true ;
Character . CanRagdoll = false ;
Character . IsInvincible = true ;
Character . Health = Health ;
2021-12-15 01:25:16 +01:00
Character . CanBeTargetted = true ;
Function . Call ( Hash . SET_PED_CAN_BE_TARGETTED_BY_PLAYER , Character . Handle , Game . Player , true ) ;
2021-07-07 13:36:25 +02:00
if ( username ! = null )
{
// Add a new blip for the ped
Character . AddBlip ( ) ;
Character . AttachedBlip . Color = BlipColor . White ;
Character . AttachedBlip . Scale = 0.8f ;
Character . AttachedBlip . Name = username ;
2021-07-09 00:20:09 +02:00
2021-07-09 03:28:58 +02:00
Function . Call ( Hash . SET_PED_CAN_EVASIVE_DIVE , Character . Handle , false ) ;
Function . Call ( Hash . SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE , Character . Handle , false ) ;
2021-07-07 13:36:25 +02:00
}
2021-12-16 22:05:40 +01:00
foreach ( KeyValuePair < byte , short > cloth in Clothes )
2021-07-07 13:36:25 +02:00
{
2021-12-16 22:05:40 +01:00
Function . Call ( Hash . SET_PED_COMPONENT_VARIATION , Character . Handle , cloth . Key , cloth . Value , 0 , 0 ) ;
2021-07-07 13:36:25 +02:00
}
2021-08-18 11:47:59 +02:00
return true ;
2021-07-07 13:36:25 +02:00
}
}
}