mirror of
https://github.com/GTA-ASM/SanAndreasUnity
synced 2024-11-22 03:53:04 +00:00
* Added Ped AI with debug info * Improved ped AI + auto spawn for each players * Disabling Ped Spawner script for clients * Added flee script for peds and reworked peds AI * Optimized Ped spawning script * Removed unused ped command * Convert class to struct in NodeFile.cs * Revert style on Ped_Spawning * Fixed respawning ped issue on multiplayer * Improved ped's AI and spawns * Added FindYCoordWithXZ method * Fixed peds spawn point on bridges + added Ped tag * Removed Ped tag to improve performances * Fixed ped trying to spawn when player is in interior
This commit is contained in:
parent
7b491d49ae
commit
5862efba4f
12 changed files with 840 additions and 9 deletions
|
@ -38,7 +38,7 @@ RenderSettings:
|
|||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1}
|
||||
m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641258, b: 0.5748172, a: 1}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
|
@ -149,7 +149,7 @@ PrefabInstance:
|
|||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4141056491348262, guid: 4fdeb0b43fda44cd48a2a49fa62c0245, type: 3}
|
||||
propertyPath: m_RootOrder
|
||||
value: 5
|
||||
value: 4
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 4fdeb0b43fda44cd48a2a49fa62c0245, type: 3}
|
||||
|
@ -413,6 +413,16 @@ PrefabInstance:
|
|||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2436511926335041943, guid: dd5c14626ef595d439de799ec08c2b16,
|
||||
type: 3}
|
||||
propertyPath: m_isOpenedByDefaultInPauseMenu
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2440717351265352695, guid: dd5c14626ef595d439de799ec08c2b16,
|
||||
type: 3}
|
||||
propertyPath: m_isOpenedByDefaultInPauseMenu
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: dd5c14626ef595d439de799ec08c2b16, type: 3}
|
||||
--- !u!224 &603823381 stripped
|
||||
|
@ -512,6 +522,163 @@ Transform:
|
|||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1468615409 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 1297494511425690, guid: 9678826ffd3ed244997780ccbdec99b7,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 508769729}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &1468615410
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1468615409}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8d1123faa382c4a4798a13a6c6b7ec4f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!114 &1468615437
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1468615409}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b50435cf5b8851246ae5943f42f6f790, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
file: {fileID: 4900000, guid: d860ab37164548b4eae5c4439ffb31c2, type: 3}
|
||||
--- !u!1 &1585510437
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1585510439}
|
||||
- component: {fileID: 1585510438}
|
||||
m_Layer: 0
|
||||
m_Name: GameObject
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!120 &1585510438
|
||||
LineRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1585510437}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_MotionVectors: 0
|
||||
m_LightProbeUsage: 0
|
||||
m_ReflectionProbeUsage: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 0}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 0
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_Positions:
|
||||
- {x: 0, y: 0, z: 0}
|
||||
- {x: 0, y: 0, z: 1}
|
||||
m_Parameters:
|
||||
serializedVersion: 3
|
||||
widthMultiplier: 1
|
||||
widthCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
colorGradient:
|
||||
serializedVersion: 2
|
||||
key0: {r: 1, g: 1, b: 1, a: 1}
|
||||
key1: {r: 1, g: 1, b: 1, a: 1}
|
||||
key2: {r: 0, g: 0, b: 0, a: 0}
|
||||
key3: {r: 0, g: 0, b: 0, a: 0}
|
||||
key4: {r: 0, g: 0, b: 0, a: 0}
|
||||
key5: {r: 0, g: 0, b: 0, a: 0}
|
||||
key6: {r: 0, g: 0, b: 0, a: 0}
|
||||
key7: {r: 0, g: 0, b: 0, a: 0}
|
||||
ctime0: 0
|
||||
ctime1: 65535
|
||||
ctime2: 0
|
||||
ctime3: 0
|
||||
ctime4: 0
|
||||
ctime5: 0
|
||||
ctime6: 0
|
||||
ctime7: 0
|
||||
atime0: 0
|
||||
atime1: 65535
|
||||
atime2: 0
|
||||
atime3: 0
|
||||
atime4: 0
|
||||
atime5: 0
|
||||
atime6: 0
|
||||
atime7: 0
|
||||
m_Mode: 0
|
||||
m_NumColorKeys: 2
|
||||
m_NumAlphaKeys: 2
|
||||
numCornerVertices: 0
|
||||
numCapVertices: 0
|
||||
alignment: 0
|
||||
textureMode: 0
|
||||
shadowBias: 0.5
|
||||
generateLightingData: 0
|
||||
m_UseWorldSpace: 1
|
||||
m_Loop: 0
|
||||
--- !u!4 &1585510439
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1585510437}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -128.6073, y: -83.42614, z: -111.87694}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 8
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1001 &1683889267
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
|
@ -562,7 +729,7 @@ PrefabInstance:
|
|||
- target: {fileID: 434938348018962339, guid: 7f1167f19fe2f0303abfd5803ceac47a,
|
||||
type: 3}
|
||||
propertyPath: m_RootOrder
|
||||
value: 6
|
||||
value: 5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 434938348018962339, guid: 7f1167f19fe2f0303abfd5803ceac47a,
|
||||
type: 3}
|
||||
|
@ -631,7 +798,7 @@ PrefabInstance:
|
|||
- target: {fileID: 474485661921074690, guid: deb0bfae1782f054d89601976a1ff30e,
|
||||
type: 3}
|
||||
propertyPath: m_RootOrder
|
||||
value: 8
|
||||
value: 7
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 474485661921074690, guid: deb0bfae1782f054d89601976a1ff30e,
|
||||
type: 3}
|
||||
|
@ -700,7 +867,7 @@ PrefabInstance:
|
|||
- target: {fileID: 4375384607915833815, guid: d9bcfeb4379e265eb85390741183fa3c,
|
||||
type: 3}
|
||||
propertyPath: m_RootOrder
|
||||
value: 7
|
||||
value: 6
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4375384607915833815, guid: d9bcfeb4379e265eb85390741183fa3c,
|
||||
type: 3}
|
||||
|
|
|
@ -102,6 +102,7 @@ namespace SanAndreasUnity.Behaviours
|
|||
new LoadingStep ( StepLoadMap, "Loading map", 2.1f ),
|
||||
new LoadingStep ( StepLoadSpecialTextures, "Loading special textures", 0.01f ),
|
||||
// new LoadingStep ( StepLoadGXT, "Loading GXT", 0.15f),
|
||||
new LoadingStep ( StepLoadPaths, "Loading paths"),
|
||||
};
|
||||
|
||||
|
||||
|
@ -427,9 +428,13 @@ namespace SanAndreasUnity.Behaviours
|
|||
GXT.Load();
|
||||
}
|
||||
|
||||
private static void StepLoadPaths()
|
||||
{
|
||||
Assets.Scripts.Importing.Paths.NodeReader.StepLoadPaths();
|
||||
}
|
||||
|
||||
|
||||
public static float GetProgressPerc ()
|
||||
public static float GetProgressPerc ()
|
||||
{
|
||||
if (m_currentStepIndex <= 0)
|
||||
return 0f;
|
||||
|
|
160
Assets/Scripts/Behaviours/PathsManager.cs
Normal file
160
Assets/Scripts/Behaviours/PathsManager.cs
Normal file
|
@ -0,0 +1,160 @@
|
|||
using Assets.Scripts.Importing.Paths;
|
||||
using SanAndreasUnity.Importing.Items.Definitions;
|
||||
using SanAndreasUnity.Net;
|
||||
using SanAndreasUnity.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SanAndreasUnity.Behaviours
|
||||
{
|
||||
public class PathsManager : MonoBehaviour
|
||||
{
|
||||
public const float MaxNPCDistance = 100.0f; // Max distance from each players before delete
|
||||
public const float MinNPCCreateDistance = 50.0f; // Min distance from each players to spawn ped
|
||||
public const float RefreshRate = 2f; // Number of seconds between each refresh
|
||||
public const int MaxNumberOfNPCAtSpawnPoint = 25;
|
||||
public int NumberOfPeds; // Debug data only
|
||||
|
||||
private float lastUpdateTime;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
lastUpdateTime = Time.time;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (NetStatus.IsServer)
|
||||
{
|
||||
if (Time.time > lastUpdateTime + RefreshRate)
|
||||
{
|
||||
List<Ped> npcs = Ped.AllPeds.Where(ped => ped.PlayerOwner == null).ToList();
|
||||
List<Ped> players = Ped.AllPeds.Where(ped => ped.PlayerOwner != null).ToList();
|
||||
|
||||
List<Vector3> playersPos = new List<Vector3>();
|
||||
|
||||
foreach (Ped player in players)
|
||||
{
|
||||
playersPos.Add(player.transform.position);
|
||||
}
|
||||
|
||||
bool isNearPlayer; // If false, delete NPC
|
||||
foreach (Ped npc in npcs)
|
||||
{
|
||||
isNearPlayer = false;
|
||||
foreach (Ped player in players)
|
||||
{
|
||||
if (Vector3.Distance(npc.transform.position, player.transform.position) < MaxNPCDistance)
|
||||
isNearPlayer = true;
|
||||
}
|
||||
if(!isNearPlayer)
|
||||
Destroy(npc.gameObject);
|
||||
}
|
||||
int nbrOfNPCInZone;
|
||||
foreach (Ped player in players)
|
||||
{
|
||||
nbrOfNPCInZone = 0;
|
||||
foreach (Ped npc in npcs)
|
||||
{
|
||||
if (Vector3.Distance(npc.transform.position, player.transform.position) < MaxNPCDistance)
|
||||
nbrOfNPCInZone++;
|
||||
}
|
||||
if (nbrOfNPCInZone < 5)
|
||||
{
|
||||
Vector3 targetZone = player.transform.position + player.Heading * MinNPCCreateDistance;
|
||||
StartCoroutine(SpawnPedWithAI(targetZone));
|
||||
}
|
||||
}
|
||||
NumberOfPeds = GameObject.FindObjectsOfType<Ped_AI>().Count();
|
||||
lastUpdateTime = Time.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static System.Collections.IEnumerator SpawnPedWithAI(Vector3 targetZone)
|
||||
{
|
||||
if (NetStatus.IsServer)
|
||||
{
|
||||
int currentArea = NodeFile.GetAreaFromPosition(targetZone);
|
||||
List<int> nearAreas = NodeFile.GetAreaNeighborhood(currentArea);
|
||||
List<Ped> pedList = new List<Ped>();
|
||||
|
||||
foreach (NodeFile file in NodeReader.Nodes.Where(f => nearAreas.Contains(f.Id) || f.Id == currentArea))
|
||||
{
|
||||
foreach (PathNode node in file.PathNodes.Where(pn => pn.NodeType > 2
|
||||
&& Vector3.Distance(pn.Position, targetZone) < MaxNPCDistance
|
||||
&& Vector3.Distance(pn.Position, targetZone) > MinNPCCreateDistance))
|
||||
{
|
||||
if (UnityEngine.Random.Range(0, 255) > node.Flags.SpawnProbability)
|
||||
{
|
||||
PathNode pedNode = node;
|
||||
Vector3 spawnPos = new Vector3(pedNode.Position.x, pedNode.Position.y, pedNode.Position.z);
|
||||
|
||||
Ped newPed = Ped.SpawnPed(Ped.RandomPedId, spawnPos + new Vector3(0, 1, 0), Quaternion.identity, true);
|
||||
|
||||
Ped_AI ai = newPed.gameObject.AddComponent<Ped_AI>();
|
||||
ai.CurrentNode = pedNode;
|
||||
ai.TargetNode = pedNode;
|
||||
|
||||
pedList.Add(newPed);
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
if (GameObject.FindObjectsOfType<Ped_AI>().Where(p => Math.Abs(Vector3.Distance(p.transform.position, targetZone)) < MaxNPCDistance).Count() > MaxNumberOfNPCAtSpawnPoint)
|
||||
break;
|
||||
}
|
||||
}
|
||||
yield return new WaitForEndOfFrame();
|
||||
|
||||
foreach (Ped ped in pedList)
|
||||
{
|
||||
Weapon weapon = null;
|
||||
switch (ped.PedDef.DefaultType)
|
||||
{
|
||||
case PedestrianType.Cop:
|
||||
weapon = ped.WeaponHolder.SetWeaponAtSlot(346, 0);
|
||||
break;
|
||||
case PedestrianType.Criminal:
|
||||
weapon = ped.WeaponHolder.SetWeaponAtSlot(347, 0);
|
||||
break;
|
||||
case PedestrianType.GangMember:
|
||||
weapon = ped.WeaponHolder.SetWeaponAtSlot(352, 0);
|
||||
break;
|
||||
}
|
||||
if (weapon != null)
|
||||
{
|
||||
ped.WeaponHolder.SwitchWeapon(weapon.SlotIndex);
|
||||
WeaponHolder.AddRandomAmmoAmountToWeapon(weapon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static PathNode GetNextPathNode(PathNode origin, PathNode current)
|
||||
{
|
||||
List<int> areas = NodeFile.GetAreaNeighborhood(origin.AreaID);
|
||||
NodeFile file = NodeReader.Nodes.Where(f => f.Id == origin.AreaID).First();
|
||||
List<PathNode> possibilities = new List<PathNode>();
|
||||
for (int i = 0; i < current.LinkCount; i++)
|
||||
{
|
||||
int linkArrayIndex = current.BaseLinkID + i;
|
||||
NodeFile nf = NodeReader.Nodes.Single(nf2 => nf2.Id == file.NodeLinks[linkArrayIndex].AreaID);
|
||||
PathNode target = nf.PathNodes.ElementAt(file.NodeLinks[linkArrayIndex].NodeID);
|
||||
if (!target.Equals(origin))
|
||||
possibilities.Add(target);
|
||||
}
|
||||
|
||||
if (possibilities.Count > 0)
|
||||
{
|
||||
return possibilities.ElementAt(UnityEngine.Random.Range(0, possibilities.Count - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
//No possibilites found, returning to origin
|
||||
return origin;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/Behaviours/PathsManager.cs.meta
Normal file
11
Assets/Scripts/Behaviours/PathsManager.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8d1123faa382c4a4798a13a6c6b7ec4f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -199,8 +199,11 @@ namespace SanAndreasUnity.Behaviours
|
|||
}
|
||||
}
|
||||
|
||||
F.InvokeEventExceptionSafe(onStart, this);
|
||||
if(this.PlayerOwner != null)
|
||||
StartCoroutine(PathsManager.SpawnPedWithAI(this.transform.position));
|
||||
|
||||
F.InvokeEventExceptionSafe(onStart, this);
|
||||
|
||||
}
|
||||
|
||||
void OnEnable ()
|
||||
|
@ -369,8 +372,8 @@ namespace SanAndreasUnity.Behaviours
|
|||
this.transform.position = hit.point + Vector3.up * (characterController.height + 0.1f);
|
||||
this.Velocity = Vector3.zero;
|
||||
|
||||
Debug.LogFormat ("Found ground at {0}, distance {1}, object name {2}, num attempts {3}, {4}, ped {5}", hit.point, hit.distance,
|
||||
hit.transform.name, numAttempts, customMessage, this.DescriptionForLogging);
|
||||
//Debug.LogFormat ("Found ground at {0}, distance {1}, object name {2}, num attempts {3}, {4}, ped {5}", hit.point, hit.distance,
|
||||
// hit.transform.name, numAttempts, customMessage, this.DescriptionForLogging);
|
||||
|
||||
}
|
||||
|
||||
|
|
124
Assets/Scripts/Behaviours/Ped/Ped_AI.cs
Normal file
124
Assets/Scripts/Behaviours/Ped/Ped_AI.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
using Assets.Scripts.Importing.Paths;
|
||||
using UnityEngine;
|
||||
using SanAndreasUnity.Utilities;
|
||||
using SanAndreasUnity.Net;
|
||||
|
||||
namespace SanAndreasUnity.Behaviours
|
||||
{
|
||||
public enum PedAction
|
||||
{
|
||||
WalkingAround,
|
||||
Chasing,
|
||||
Escaping
|
||||
}
|
||||
public class Ped_AI : MonoBehaviour
|
||||
{
|
||||
[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; }
|
||||
|
||||
// Use this for initialization
|
||||
void Start()
|
||||
{
|
||||
this.MyPed = this.GetComponentOrLogError<Ped>();
|
||||
Ped.onDamaged += Ped_onDamaged;
|
||||
}
|
||||
|
||||
private void Ped_onDamaged(Ped hitPed, DamageInfo dmgInfo, Ped.DamageResult dmgResult)
|
||||
{
|
||||
if(hitPed.Equals(this.MyPed))
|
||||
{
|
||||
if (this.MyPed.PedDef.DefaultType == Importing.Items.Definitions.PedestrianType.Criminal ||
|
||||
this.MyPed.PedDef.DefaultType == Importing.Items.Definitions.PedestrianType.Cop ||
|
||||
this.MyPed.PedDef.DefaultType == Importing.Items.Definitions.PedestrianType.GangMember)
|
||||
{
|
||||
TargetPed = (Ped)dmgInfo.attacker;
|
||||
this.Action = PedAction.Chasing;
|
||||
}
|
||||
else
|
||||
this.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)
|
||||
{
|
||||
PathNode previousNode = CurrentNode;
|
||||
CurrentNode = TargetNode;
|
||||
TargetNode = PathsManager.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)
|
||||
{
|
||||
if (Vector3.Distance(TargetPed.transform.position, this.MyPed.transform.position) < 10f)
|
||||
{
|
||||
this.MyPed.AimDirection = (TargetPed.transform.position - this.MyPed.transform.position).normalized;
|
||||
this.MyPed.IsAimOn = true;
|
||||
this.MyPed.IsFireOn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MyPed.IsRunOn = true;
|
||||
this.MyPed.Movement = (TargetPed.transform.position - this.MyPed.transform.position).normalized;
|
||||
this.MyPed.Heading = this.MyPed.Movement;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
PathNode previousNode = CurrentNode;
|
||||
CurrentNode = TargetNode;
|
||||
TargetNode = PathsManager.GetNextPathNode(CurrentNode, previousNode);
|
||||
}
|
||||
this.MyPed.IsSprintOn = true;
|
||||
this.MyPed.Movement = (TargetNode.Position - this.MyPed.transform.position).normalized;
|
||||
this.MyPed.Heading = this.MyPed.Movement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
11
Assets/Scripts/Behaviours/Ped/Ped_AI.cs.meta
Normal file
11
Assets/Scripts/Behaviours/Ped/Ped_AI.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f5833d381af6045439639283b05d13fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Scripts/Importing/Paths.meta
Normal file
8
Assets/Scripts/Importing/Paths.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f7097cb8583e5174f9d5c41a115618f1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
315
Assets/Scripts/Importing/Paths/NodeFile.cs
Normal file
315
Assets/Scripts/Importing/Paths/NodeFile.cs
Normal file
|
@ -0,0 +1,315 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Script created by RobinS-S
|
||||
/// </summary>
|
||||
|
||||
namespace Assets.Scripts.Importing.Paths
|
||||
{
|
||||
public enum PathNodeTrafficLevel
|
||||
{
|
||||
Full = 0,
|
||||
High = 1,
|
||||
Medium = 2,
|
||||
Low = 3
|
||||
}
|
||||
public struct PathNodeFlag
|
||||
{
|
||||
public PathNodeTrafficLevel TrafficLevel;
|
||||
public bool RoadBlocks;
|
||||
public bool IsWater;
|
||||
public bool EmergencyOnly;
|
||||
public bool IsHighway;
|
||||
public int SpawnProbability;
|
||||
public override string ToString()
|
||||
{
|
||||
return "TrafficLevel=" + TrafficLevel + ",Roadblocks=" + RoadBlocks + ",IsWater=" + IsWater + "\r\n" +
|
||||
"EmergencyOnly=" + EmergencyOnly + ",IsHighway=" + IsHighway + ",SpawnProbability=" + SpawnProbability;
|
||||
}
|
||||
}
|
||||
public struct PathNode
|
||||
{
|
||||
public Vector3 Position { get; set; }
|
||||
public int BaseLinkID { get; set; }
|
||||
public int AreaID { get; set; }
|
||||
public int NodeID { get; set; }
|
||||
public float PathWidth { get; set; }
|
||||
public int NodeType { get; set; } // enum
|
||||
public int LinkCount { get; set; }
|
||||
public PathNodeFlag Flags;
|
||||
}
|
||||
public struct NavNode
|
||||
{
|
||||
public Vector2 Position { get; set; }
|
||||
public int TargetAreaID { get; set; }
|
||||
public int TargetNodeID { get; set; }
|
||||
public Vector2 Direction { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int NumLeftLanes { get; set; }
|
||||
public int NumRightLanes { get; set; }
|
||||
public int TrafficLightDirection { get; set; }
|
||||
public int TrafficLightBehavior { get; set; }
|
||||
public int IsTrainCrossing { get; set; }
|
||||
public byte Flags { get; set; }
|
||||
}
|
||||
public struct NodeLink
|
||||
{
|
||||
public int AreaID { get; set; }
|
||||
public int NodeID { get; set; }
|
||||
public int Length { get; set; }
|
||||
}
|
||||
public struct PathIntersectionFlags
|
||||
{
|
||||
public bool IsRoadCross { get; set; }
|
||||
public bool IsTrafficLight { get; set; }
|
||||
}
|
||||
public struct NavNodeLink
|
||||
{
|
||||
public int NodeLink { get; set; }
|
||||
public int AreaID { get; set; }
|
||||
}
|
||||
public class NodeReader
|
||||
{
|
||||
public static List<NodeFile> Nodes { get; set; }
|
||||
public static float[][] Borders { get; private set; }
|
||||
public static void StepLoadPaths()
|
||||
{
|
||||
int row;
|
||||
int col;
|
||||
Borders = new float[64][];
|
||||
//TODO: according to https://gtamods.com/wiki/Paths_%28GTA_SA%29 only the active area and those surrounding it should be loaded at a time
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
using (Stream node = SanAndreasUnity.Importing.Archive.ArchiveManager.ReadFile("nodes" + i + ".dat"))
|
||||
{
|
||||
NodeFile nf = new NodeFile(i, node);
|
||||
AddNode(nf);
|
||||
}
|
||||
row = (int)(i % 8);
|
||||
col = (int)(i / 8);
|
||||
Borders[i] = new float[] { -3000 + (750 * row), -3000 + (750 * col) };
|
||||
}
|
||||
}
|
||||
public static void AddNode(NodeFile node)
|
||||
{
|
||||
if (Nodes == null) Nodes = new List<NodeFile>();
|
||||
Nodes.Add(node);
|
||||
}
|
||||
}
|
||||
public class NodeFile
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int NumOfNodes { get; set; }
|
||||
public int NumOfVehNodes { get; set; }
|
||||
public int NumOfPedNodes { get; set; }
|
||||
public int NumOfNavNodes { get; set; }
|
||||
public int NumOfLinks { get; set; }
|
||||
public List<PathNode> PathNodes { get; set; }
|
||||
public List<NavNode> NavNodes { get; set; }
|
||||
public List<NodeLink> NodeLinks { get; set; }
|
||||
public List<NavNodeLink> NavNodeLinks { get; set; }
|
||||
public List<PathIntersectionFlags> PathIntersections { get; set; }
|
||||
|
||||
|
||||
public NodeFile(int id, Stream stream)
|
||||
{
|
||||
Id = id;
|
||||
PathNodes = new List<PathNode>();
|
||||
NavNodes = new List<NavNode>();
|
||||
NodeLinks = new List<NodeLink>();
|
||||
NavNodeLinks = new List<NavNodeLink>();
|
||||
PathIntersections = new List<PathIntersectionFlags>();
|
||||
using (BinaryReader reader = new BinaryReader(stream))
|
||||
{
|
||||
ReadHeader(reader);
|
||||
Debug.Log(NumOfNodes);
|
||||
ReadNodes(reader);
|
||||
ReadNavNodes(reader);
|
||||
Debug.Log(NumOfNavNodes);
|
||||
ReadLinks(reader);
|
||||
reader.ReadBytes(768);
|
||||
ReadNavLinks(reader);
|
||||
ReadLinkLengths(reader);
|
||||
ReadPathIntersectionFlags(reader);
|
||||
}
|
||||
//Debug.Log($"Read paths. Nodes {NumOfNodes} VehNodes {NumOfVehNodes} PedNodes {NumOfPedNodes} NavNodes {NumOfNavNodes} Links {NumOfLinks}");
|
||||
}
|
||||
|
||||
private void ReadNodes(BinaryReader reader)
|
||||
{
|
||||
for (int i = 0; i < NumOfNodes; i++)
|
||||
{
|
||||
PathNode node = new PathNode();
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
float x = (float)reader.ReadInt16() / 8;
|
||||
float z = (float)reader.ReadInt16() / 8;
|
||||
float y = (float)reader.ReadInt16() / 8;
|
||||
node.Position = new Vector3(x, y, z);
|
||||
short heuristic = reader.ReadInt16();
|
||||
if (heuristic != 0x7FFE) UnityEngine.Debug.Log("corrupted path node?");
|
||||
node.BaseLinkID = reader.ReadUInt16();
|
||||
node.AreaID = reader.ReadUInt16();
|
||||
node.NodeID = reader.ReadUInt16();
|
||||
node.PathWidth = (float)reader.ReadByte() / 8;
|
||||
node.NodeType = reader.ReadByte();
|
||||
|
||||
int flag = reader.ReadInt32();
|
||||
node.LinkCount = flag & 15;
|
||||
node.Flags.RoadBlocks = Convert.ToBoolean(flag & 0xF);
|
||||
node.Flags.IsWater = Convert.ToBoolean(flag & 0x80);
|
||||
node.Flags.EmergencyOnly = Convert.ToBoolean(flag & 0x100);
|
||||
node.Flags.IsHighway = !Convert.ToBoolean(flag & 0x1000);
|
||||
node.Flags.SpawnProbability = (flag & 0xF0000) >> 16;
|
||||
|
||||
PathNodes.Add(node);
|
||||
//UnityEngine.Debug.Log($"Node {i}: POS [{node.Position.x} {node.Position.y} {node.Position.z}] LinkID {node.LinkID} LinkCount {node.LinkCount} AreaID {node.AreaID} NodeID {node.NodeID} PathWidth {node.PathWidth} NodeType {node.NodeType} Flags {node.Flags}");
|
||||
}
|
||||
}
|
||||
private void ReadNavNodes(BinaryReader reader)
|
||||
{
|
||||
for (int i = 0; i < NumOfNavNodes; i++)
|
||||
{
|
||||
NavNode node = new NavNode();
|
||||
node.Position = new Vector2(reader.ReadInt16(), reader.ReadInt16());
|
||||
node.TargetAreaID = reader.ReadUInt16();
|
||||
node.TargetNodeID = reader.ReadUInt16();
|
||||
node.Direction = new Vector2(reader.ReadSByte(), reader.ReadSByte());
|
||||
node.Width = reader.ReadByte() / 8;
|
||||
|
||||
byte flags = reader.ReadByte();
|
||||
node.NumLeftLanes = flags & 7;
|
||||
node.NumRightLanes = (flags >> 3) & 7;
|
||||
node.TrafficLightDirection = (flags >> 4) & 1;
|
||||
|
||||
flags = reader.ReadByte();
|
||||
node.TrafficLightBehavior = flags & 3;
|
||||
node.IsTrainCrossing = (flags >> 2) & 1;
|
||||
node.Flags = reader.ReadByte();
|
||||
|
||||
NavNodes.Add(node);
|
||||
//UnityEngine.Debug.Log($"NavNode {i}: {node.Position.x} {node.Position.y} AreaID {node.AreaID} NodeID {node.NodeID} Direction {node.Direction.x} {node.Direction.y} Flags {node.Flags}");
|
||||
}
|
||||
}
|
||||
private void ReadLinks(BinaryReader reader)
|
||||
{
|
||||
for (int i = 0; i < NumOfLinks; i++)
|
||||
{
|
||||
NodeLink link = new NodeLink();
|
||||
link.AreaID = reader.ReadUInt16();
|
||||
link.NodeID = reader.ReadUInt16();
|
||||
NodeLinks.Add(link);
|
||||
//UnityEngine.Debug.Log($"NodeLink {i}: AreaID {link.AreaID} NodeID {link.NodeID}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadNavLinks(BinaryReader reader)
|
||||
{
|
||||
for (int i = 0; i < NumOfNavNodes; i++)
|
||||
{
|
||||
ushort bytes = reader.ReadUInt16();
|
||||
NavNodeLink link = new NavNodeLink();
|
||||
link.NodeLink = bytes & 1023;
|
||||
link.AreaID = bytes >> 10;
|
||||
NavNodeLinks.Add(link);
|
||||
//UnityEngine.Debug.Log($"NavLink {i} area ID {link.AreaID} NaviNodeID {link.NaviNodeID}");
|
||||
}
|
||||
}
|
||||
private void ReadLinkLengths(BinaryReader reader)
|
||||
{
|
||||
for (int i = 0; i < NumOfLinks; i++)
|
||||
{
|
||||
ushort length = reader.ReadByte();
|
||||
NodeLink tmp = NodeLinks[i];
|
||||
tmp.Length = length;
|
||||
NodeLinks[i] = tmp;
|
||||
//UnityEngine.Debug.Log($"Link length {i}: {length}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadPathIntersectionFlags(BinaryReader reader)
|
||||
{
|
||||
for (int i = 0; i < NumOfLinks; i++)
|
||||
{
|
||||
byte roadCross = reader.ReadByte();
|
||||
//byte pedTrafficLight = reader.ReadByte();
|
||||
/*
|
||||
PathIntersectionFlags pif = new PathIntersectionFlags()
|
||||
{
|
||||
IsRoadCross = (roadCross & 1) ? true : false,
|
||||
IsTrafficLight = (roadCross & 1) ? true : false
|
||||
};*/
|
||||
//UnityEngine.Debug.Log($"PathIntersectionFlags {i}: roadCross {roadCross} pedTrafficLight {pedTrafficLight}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadHeader(BinaryReader reader)
|
||||
{
|
||||
NumOfNodes = (int)reader.ReadUInt32();
|
||||
NumOfVehNodes = (int)reader.ReadUInt32();
|
||||
NumOfPedNodes = (int)reader.ReadUInt32();
|
||||
NumOfNavNodes = (int)reader.ReadUInt32();
|
||||
NumOfLinks = (int)reader.ReadUInt32();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the areas ID around the given areaID
|
||||
*/
|
||||
public static List<int> GetAreaNeighborhood(int areaID)
|
||||
{
|
||||
List<int> result = new List<int>();
|
||||
int indexX = (areaID + 1) % 8;
|
||||
int indexY = Convert.ToInt32(Math.Truncate(Convert.ToDecimal((areaID + 1) / 8)));
|
||||
if (indexY == 8) indexY = 7;
|
||||
|
||||
int aW, aNW, aN, aNE, aE, aSE, aS, aSW;
|
||||
aW = (indexX == 1) ? -1 : (areaID - 1);
|
||||
aNW = (indexX == 1) ? -1 : ((indexY == 7) ? -1 : (areaID + 7));
|
||||
aN = (indexY == 7) ? -1 : (areaID + 8);
|
||||
aNE = (indexX == 0) ? -1 : ((indexY == 7) ? -1 : (areaID + 9));
|
||||
aE = (indexX == 0) ? -1 : (areaID + 1);
|
||||
aSE = (indexX == 0) ? -1 : ((indexY == 0) ? -1 : (areaID - 7));
|
||||
aS = (indexY == 0) ? -1 : (areaID - 8);
|
||||
aSW = (indexX == 1) ? -1 : ((indexY == 0) ? -1 : (areaID - 9));
|
||||
|
||||
result.Add(aW);
|
||||
result.Add(aNW);
|
||||
result.Add(aN);
|
||||
result.Add(aNE);
|
||||
result.Add(aE);
|
||||
result.Add(aSE);
|
||||
result.Add(aS);
|
||||
result.Add(aSW);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int GetAreaFromPosition(Vector3 position)
|
||||
{
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (position.x > NodeReader.Borders[i][0] && position.x < (NodeReader.Borders[i][0] + 750)
|
||||
&& position.z > NodeReader.Borders[i][1] && position.z < (NodeReader.Borders[i][1] + 750))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Debug.Log("NodeReader.Borders is null");
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int GetAreaFromPosition(Vector2 position)
|
||||
{
|
||||
return GetAreaFromPosition(new Vector3(position.x, 0.0f, position.y));
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/Importing/Paths/NodeFile.cs.meta
Normal file
11
Assets/Scripts/Importing/Paths/NodeFile.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0454936199047de4193bc73c8e693f58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -971,5 +971,20 @@ namespace SanAndreasUnity.Utilities
|
|||
return clip.samples * sizeof(float);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ground position using X and Z position
|
||||
/// </summary>
|
||||
/// <param name="x">The X position</param>
|
||||
/// <param name="z">The Z position</param>
|
||||
/// <returns>The ground level</returns>
|
||||
public static float FindYCoordWithXZ(float x, float z)
|
||||
{
|
||||
if (Physics.Raycast(new Vector3(x, 3000, z), Vector3.down, out RaycastHit hit))
|
||||
{
|
||||
return hit.point.y;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ TagManager:
|
|||
- Minimap
|
||||
- UI
|
||||
- Fire
|
||||
- Ped
|
||||
layers:
|
||||
- Default
|
||||
- TransparentFX
|
||||
|
|
Loading…
Reference in a new issue