2021-01-17 10:57:00 +01:00
using System.Collections ;
using System.Collections.Generic ;
using UnityEngine ;
using UnityEngine.AI ;
//Define the system managing the events. (Singleton)
2021-01-17 17:56:17 +01:00
//TODO: Switch to a registering approach for events
2021-01-17 10:57:00 +01:00
public sealed class EventManager : MonoBehaviour
{
2021-01-19 10:46:07 +01:00
public static string EventManager_path = "/GameSystem/EventManager" ;
2021-01-17 10:57:00 +01:00
//Singleton
private static EventManager _instance = null ;
public static EventManager Instance { get
{
2021-01-19 10:46:07 +01:00
if ( _instance is null ) //Force Awakening if needed
GameObject . Find ( EventManager_path ) . GetComponent < EventManager > ( ) . Awake ( ) ;
2021-01-17 10:57:00 +01:00
return _instance ;
}
}
[HideInInspector]
public bool ready = false ; //Wether the ClientManager is initialized
[SerializeField]
2021-01-17 17:56:17 +01:00
float SpawnRange = 1.0f ; //Range of an event spawn from its origin (real max distance = 2*range)
2021-01-17 10:57:00 +01:00
[SerializeField]
2021-01-22 17:34:44 +01:00
float spawnChance = 100.0f ; //Probability of an event to spawn (%)
2021-01-17 10:57:00 +01:00
[SerializeField]
2021-01-22 17:34:44 +01:00
int maxSoftObs = 1 , maxHardObs = 1 ; //Maximum active events
2021-01-17 17:56:17 +01:00
// [SerializeField]
// float eventSpawnTimer = 0.5f; //Intial time before first spawn (pseudo-random after that)
// [SerializeField]
// float maxTimeNewEvents = 2.0f; //Longest waiting time for new clients
// bool eventSpawnReady = false;
2021-01-17 10:57:00 +01:00
[SerializeField]
2021-01-22 17:34:44 +01:00
string SoftObsRessourceFolder = "Events/SoftObstacles" , HardObsRessourceFolder = "Events/HardObstacles" ; //Ressource folder w/ events prefabs
private Dictionary < string , Object [ ] > eventPrefabs = new Dictionary < string , Object [ ] > ( ) ;
2021-01-17 10:57:00 +01:00
GameObject EventContainer = null ;
2021-01-22 17:34:44 +01:00
//List of active event ID
List < GameObject > softObsList = new List < GameObject > ( ) ;
List < GameObject > hardObsList = new List < GameObject > ( ) ;
[SerializeField]
float coroutineRefreshRate = 1.0f ; //Time (s) before refreshing a coroutine
2021-01-17 17:56:17 +01:00
private Dictionary < int , IEnumerator > coroutines = new Dictionary < int , IEnumerator > ( ) ; //Dict of EventManager coroutines associated to each client ID
2021-01-17 10:57:00 +01:00
2021-01-17 17:56:17 +01:00
//Spawn an event near position with a probability of spawnChance%
2021-01-22 17:34:44 +01:00
public void spawnSoftObs ( Vector2 position , float spawnChance = 100.0f )
2021-01-17 10:57:00 +01:00
{
Vector3 spawnPoint ;
2021-01-22 17:34:44 +01:00
if ( Random . Range ( 0.0f , 99.9f ) < spawnChance & & softObsList . Count < maxSoftObs & & RandomPoint ( position , SpawnRange , out spawnPoint ) )
2021-01-17 10:57:00 +01:00
{
2021-01-17 17:56:17 +01:00
// Debug.DrawRay(spawnPoint, Vector3.up, Color.blue, 2.0f);
2021-01-22 17:34:44 +01:00
int prefabChoice = Random . Range ( 0 , eventPrefabs [ "soft" ] . Length ) ;
GameObject newEvent = Instantiate ( ( GameObject ) eventPrefabs [ "soft" ] [ prefabChoice ] , spawnPoint , Quaternion . identity , EventContainer . transform ) ; //Instantiate new event inside ClientManager
softObsList . Add ( newEvent ) ; //Save event to list
newEvent . name = newEvent . name . Split ( '(' ) [ 0 ] + newEvent . GetInstanceID ( ) ; //Rename new event
2021-01-17 10:57:00 +01:00
2021-01-17 17:56:17 +01:00
// eventSpawnTimer=Random.Range(1.0f, maxTimeNewEvents); //Need more random ?
// eventSpawnReady=false;
2021-01-17 10:57:00 +01:00
}
}
2021-01-22 17:34:44 +01:00
public void spawnHardObs ( List < Client_controller > targetClients , Vector2 position , float spawnChance = 100.0f )
{
//TODO: Orienté client vers event + prefab
Vector3 spawnPoint ;
if ( Random . Range ( 0.0f , 99.9f ) < spawnChance & & hardObsList . Count < maxHardObs & & RandomPoint ( position , SpawnRange , out spawnPoint ) )
{
// Debug.DrawRay(spawnPoint, Vector3.up, Color.blue, 2.0f);
int prefabChoice = Random . Range ( 0 , eventPrefabs [ "hard" ] . Length ) ;
GameObject newEvent = Instantiate ( ( GameObject ) eventPrefabs [ "hard" ] [ prefabChoice ] , spawnPoint , Quaternion . identity , EventContainer . transform ) ; //Instantiate new event inside ClientManager
hardObsList . Add ( newEvent ) ; //Save event to list
newEvent . name = newEvent . name . Split ( '(' ) [ 0 ] + newEvent . GetInstanceID ( ) ; //Rename new event
foreach ( Client_controller client in targetClients )
client . assignToEvent ( spawnPoint ) ;
}
}
2021-01-17 17:56:17 +01:00
//Remove an event from the EventManager
public void destroyEvent ( GameObject eventObj )
{
2021-01-22 17:34:44 +01:00
softObsList . Remove ( eventObj ) ;
hardObsList . Remove ( eventObj ) ;
2021-01-17 17:56:17 +01:00
}
//Start an event coroutine for client
public void startCoroutine ( GameObject client )
{
2021-01-22 17:34:44 +01:00
// Debug.Log("EventManager: Start coroutine "+client.name);
2021-01-17 17:56:17 +01:00
int clientID = client . GetInstanceID ( ) ;
if ( coroutines . ContainsKey ( clientID ) ) //Stop previous coroutine of the client
StopCoroutine ( coroutines [ clientID ] ) ;
2021-01-22 17:34:44 +01:00
IEnumerator newCoroutine = clientCoroutine ( client ) ;
2021-01-17 17:56:17 +01:00
coroutines [ clientID ] = newCoroutine ;
StartCoroutine ( newCoroutine ) ;
}
//Stop the event coroutine for client
public void stopCoroutine ( GameObject client )
{
int clientID = client . GetInstanceID ( ) ;
if ( coroutines . ContainsKey ( clientID ) )
{
2021-01-22 17:34:44 +01:00
// Debug.Log("EventManager: Stop coroutine "+client.name);
2021-01-17 17:56:17 +01:00
StopCoroutine ( coroutines [ clientID ] ) ;
}
}
//InvokeRepeating() is another option
//Coroutine to be started in a parallel process.
2021-01-22 17:34:44 +01:00
private IEnumerator clientCoroutine ( GameObject clientObj )
2021-01-17 17:56:17 +01:00
{
2021-01-22 17:34:44 +01:00
Client_controller client = clientObj . GetComponent < Client_controller > ( ) ;
2021-01-17 17:56:17 +01:00
while ( EventManager . Instance ! = null ) {
if ( GameSystem . Instance . serviceOpen )
2021-01-22 17:34:44 +01:00
{
//Try to spawn softObs or hardObs randomly
if ( Random . value < 0.5 & & client . status = = "consuming" )
EventManager . Instance . spawnSoftObs ( clientObj . transform . position , spawnChance ) ;
else
{
List < GameObject > otherClients = findNearClients ( clientObj , 1.0f ) ;
if ( otherClients . Count > 0 )
{
foreach ( GameObject ocl in otherClients )
Debug . DrawLine ( clientObj . transform . position , ocl . transform . position , Color . red , coroutineRefreshRate ) ;
// Debug.Log("Clients near");
GameObject tgtClient = otherClients [ Random . Range ( 0 , otherClients . Count ) ] ;
//TODO : Compute spawnChance w/ clients happiness
Vector2 eventPos = ( clientObj . transform . position + tgtClient . transform . position ) / 2 ; //Event pos between clients
List < Client_controller > targetClients = new List < Client_controller > ( ) { client , tgtClient . GetComponent < Client_controller > ( ) } ;
EventManager . Instance . spawnHardObs ( targetClients , eventPos , spawnChance ) ;
}
}
}
yield return new WaitForSeconds ( coroutineRefreshRate ) ; //Called every coroutineRefreshRate second
}
}
//Return the list of other clients in range of client. The client in event status are ignored.
List < GameObject > findNearClients ( GameObject client , float range )
{
List < GameObject > res = new List < GameObject > ( ) ;
List < GameObject > clientList = ClientManager . Instance . clientList ;
Vector3 originPos = client . transform . position ;
Vector3 otherPos ;
foreach ( GameObject cl in clientList )
{
otherPos = cl . transform . position ;
if ( Vector2 . Distance ( originPos , otherPos ) < range & & originPos ! = otherPos )
if ( cl . GetComponent < Client_controller > ( ) . status ! = "event" ) //Ignore clients already in event status
res . Add ( cl ) ;
}
return res ;
}
//Try to find a random point on NavMesh inside a range circle. A result would at a maximum distance of 2*range.
bool RandomPoint ( Vector3 center , float range , out Vector3 result )
{
for ( int i = 0 ; i < 50 ; i + + )
{
Vector3 randomPoint = center + ( Vector3 ) Random . insideUnitCircle * range ;
NavMeshHit hit ;
if ( NavMesh . SamplePosition ( randomPoint , out hit , range , NavMesh . AllAreas ) )
{
result = hit . position ;
return true ;
}
2021-01-17 17:56:17 +01:00
}
2021-01-22 17:34:44 +01:00
result = Vector3 . zero ;
return false ;
2021-01-17 17:56:17 +01:00
}
2021-01-17 10:57:00 +01:00
//Awake is called when the script instance is being loaded.
void Awake ( )
{
//Singleton
if ( _instance ! = null & & _instance ! = this )
Destroy ( this . gameObject ) ;
else
_instance = this ;
if ( ! ready )
{
2021-01-19 10:46:07 +01:00
EventContainer = GameObject . Find ( EventManager_path ) ;
2021-01-17 10:57:00 +01:00
if ( EventContainer is null )
throw new System . Exception ( "No EventManager found under GameSystem" ) ;
2021-01-22 17:34:44 +01:00
eventPrefabs [ "soft" ] = Resources . LoadAll ( SoftObsRessourceFolder ) ;
eventPrefabs [ "hard" ] = Resources . LoadAll ( HardObsRessourceFolder ) ;
if ( eventPrefabs [ "soft" ] . Length = = 0 )
2021-01-17 10:57:00 +01:00
{
2021-01-22 17:34:44 +01:00
Debug . LogWarning ( "EventManager didn't find events prefab in ressource folder : " + SoftObsRessourceFolder ) ;
}
if ( eventPrefabs [ "hard" ] . Length = = 0 )
{
Debug . LogWarning ( "EventManager didn't find events prefab in ressource folder : " + HardObsRessourceFolder ) ;
2021-01-17 10:57:00 +01:00
}
ready = true ;
}
}
// Update is called once per frame
void Update ( )
{
2021-01-17 17:56:17 +01:00
// if(!eventSpawnReady)
// {
// eventSpawnTimer-= Time.deltaTime;
// if(eventSpawnTimer<=0)
// eventSpawnReady=true;
// }
}
void OnDisable ( )
{
StopAllCoroutines ( ) ;
}
void OnDestroy ( )
{
StopAllCoroutines ( ) ;
}
2021-01-17 10:57:00 +01:00
}