using System; using System.Collections.Generic; using Animators.Leonid_Animator; using Unity.MLAgents; using Unity.MLAgents.Actuators; using Unity.MLAgents.Sensors; using UnityEngine; [RequireComponent(typeof(MovementController),typeof(BufferSensorComponent))] public class NPC : Agent, ICharacter { [HideInInspector] private Character _agentCharacter; private CharacterCondition _condition; private FlagZone _flagZone = null; private INpcBaseState NpcState { get; set; } public INpcBaseBodyState NpcBodyState { get; private set; } public Character GetCharacter => _agentCharacter; private NpcDirectPointState _directState; private NpcInCoverState _coverState; private NpcRunningState _runningState; private NpcStandingState _standingState; private NpcCrouchingState _crouchingState; private MovementController _moveController; private BufferSensorComponent _bufferSensor; private AnimatorHandler _animatorHandler; private AimAssistant _assistant; private Dictionary _navPointIdDict; #region UnityEvents and ML private void Awake() { _directState = new NpcDirectPointState(); _coverState = new NpcInCoverState(); _runningState = new NpcRunningState(); NpcState = _directState; _crouchingState = new NpcCrouchingState(); _standingState = new NpcStandingState(); NpcBodyState = _standingState; _agentCharacter = new Character(); _condition = _agentCharacter.Condition; _moveController = gameObject.GetComponent(); _bufferSensor = gameObject.GetComponent(); _animatorHandler = gameObject.GetComponent(); _assistant = gameObject.GetComponent(); _flagZone = GameObject.FindObjectOfType(); if (_flagZone is null) Debug.LogError("Flag Is Not Set"); _navPointIdDict = MapManager.Instance.IDToNavPoint; if (_navPointIdDict is null) Debug.LogError("Cant Find Nav Point Dictionary"); } private void OnDestroy() { Debug.LogWarning("Pooled object was destroyed"); } public override void OnEpisodeBegin() { if (_navPointIdDict is null) Debug.LogError("Cant Find Nav Point Dictionary"); NpcState = _directState; _flagZone = GameObject.FindObjectOfType(); } public override void CollectObservations(VectorSensor sensor) { // Debug.Log("Collect observations called!"); _navPointIdDict = MapManager.Instance.IDToNavPoint; if (_navPointIdDict is null) Debug.LogError("Cant Find Nav Point Dictionary"); var candidates = _moveController.GetPointsCandidate(); //common sensors sensor.AddObservation(GameManager.IsHaveSeenByEnemy(_agentCharacter.Team.GetOppositeTeam(), NpcBodyState.GetPointToHit(gameObject)).ToInt()); sensor.AddObservation(_agentCharacter.LastTimeHit); sensor.AddObservation((!_flagZone.IsNotOccup).ToInt()); sensor.AddObservation(_condition.GetHealthPointsInQuantile()); sensor.AddObservation(_condition.GetArmourPointsInQuantile()); sensor.AddObservation(candidates.Count); sensor.AddObservation(_moveController.PointStartID); sensor.AddObservation(_moveController.PointEndID); // Debug.Log("Done common!"); //state sensors sensor.AddObservation((int)NpcState.State); sensor.AddObservation((int)NpcBodyState.State); sensor.AddObservation(GameManager.IsEnemyNearby(gameObject.transform.position, _agentCharacter.Team)); sensor.AddObservation(_navPointIdDict[_moveController.PointStartID].DeathAttr); sensor.AddObservation(_navPointIdDict[_moveController.PointEndID].DeathAttr); sensor.AddObservation(_moveController.FlagDistance); // Debug.Log("Done state sensors!"); //point sensors foreach (var point in candidates) { var position = transform.position; _bufferSensor.AppendObservation(new float[] { point.DeathAttr, (int)point.navType, //4 flagEnemyDistance GameManager.IsCloserToFlagFromNextNavPoint(point, position).ToInt(), //5 EnemyVsNavPointDistance GameManager.IsCloserToEnemyThanToNextNavPoint(point, position, _agentCharacter.Team.GetOppositeTeam()).ToInt(), //6 Have been seen by enemy in this point GameManager.IsHaveSeenByEnemy(_agentCharacter.Team.GetOppositeTeam(), point.Position).ToInt() }); } // Debug.Log("Done collect observations!"); } public override void OnActionReceived(ActionBuffers actions) { // Debug.Log("Actions recieved!"); var result = actions.DiscreteActions; // Debug.Log(result[0] + " " + result[1]); if (result[0] == 0) { if (_navPointIdDict[_moveController.PointStartID].navType != NavPointType.Cover) return; NpcState = _coverState; switch (result[1]) { case 0: Peek(); break; case 1: Cover(); break; case 3: Peek(); _moveController.GoToNextNavPoint(_navPointIdDict[result[2]]); break; case 4: NpcState = _directState; break; default: throw new ArgumentException("Undefined Action recieved"); } } // Debug.Log(result[0] == 1); if (result[0] == 1) { // Debug.Log("BEFORE SOme shitty if >:("); if (_navPointIdDict[_moveController.PointStartID].navType != NavPointType.Direction) { // Debug.Log("SOme shitty if >:("); return; } // Debug.Log("FUCK"); switch (result[1]) { case 0: _moveController.GoToNextNavPoint(_navPointIdDict[result[2]]); NpcState = _runningState; Debug.Log("Go to point " + result[2]);break; case 1: NpcState = _directState; break; default: throw new ArgumentException("Undefined Action recieved"); } } if (result[0] == 2) { if (_moveController.PointStartID == _moveController.PointEndID && _moveController.PointEndID != -1) return; switch (result[1]) { case 0: _moveController.StopOnPath(); NpcState = _directState; break; case 1: _moveController.ReturnToStartPoint(); NpcState = _runningState; break; default: throw new ArgumentException("Undefined Action recieved"); } } // Debug.Log("Actions processed!"); } private void UpdateAnimatorValues() { var movementDir = _moveController.Velocity; //Тут может быть косяк, так как я не помню горизонтальное по x или y. _animatorHandler.UpdateAnimatorValues(movementDir.x, movementDir.y, false, NpcBodyState == _crouchingState, _assistant._isFiring); } #endregion public event Action OnChangePosition; private void Peek() { OnChangePosition?.Invoke(global::NpcBodyState.Standing); NpcBodyState = _standingState; } private void Cover() { OnChangePosition?.Invoke(global::NpcBodyState.Crouching); NpcBodyState = _crouchingState; } public event Action OnDeathEvent; public event Action OnDamageRecieved; public void GetDamage(int damage) { _agentCharacter.LastTimeHit = TimeManager.Instance.CurrentTime; _condition.GiveHealth(-Mathf.RoundToInt(damage * (1 - _condition.ArmourPoints * 0.5f))); _condition.GiveArmour(-Mathf.RoundToInt(Mathf.Sqrt(damage) * 5)); OnDamageRecieved?.Invoke(damage, _agentCharacter.Team); if (_condition.HealthPoints < 0) { OnDeathEvent?.Invoke(true); MapManager.AddDeathAttributeToPoints(_moveController.PointStartID, _moveController.PointEndID, _moveController.DistanceToGo, _moveController.RemainingDistance); var pos = gameObject.transform.position; var id = _moveController.PointStartID; CharacterFactory.Instance.ReSpawn(this, ref pos, ref id); } } public void ResetCharacter() { _condition.Reset(); EndEpisode(); } }