2023-04-18 06:29:51 +02:00
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using UnityEngine;
|
|
|
|
using UnityEngine.AI;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class SkinlessMonsterComponent : MonoBehaviour
|
|
|
|
{
|
|
|
|
[SerializeField]
|
|
|
|
private NavMeshAgent agent;
|
|
|
|
[SerializeField]
|
|
|
|
private SkinlessMonsterAnimator animator;
|
2023-04-21 09:30:43 +02:00
|
|
|
private bool inDamageRange = false;
|
|
|
|
private bool inDamageMargin = false;
|
2023-04-18 06:29:51 +02:00
|
|
|
private bool atTarget = false;
|
|
|
|
[SerializeField]
|
2023-04-21 09:30:43 +02:00
|
|
|
private float atTargetDistance = 2;
|
2023-04-18 06:29:51 +02:00
|
|
|
private TargetInformation target;
|
|
|
|
|
2023-04-21 09:30:43 +02:00
|
|
|
[SerializeField]
|
|
|
|
private float health = 2f;
|
|
|
|
|
2023-04-18 06:29:51 +02:00
|
|
|
|
2023-04-21 09:30:43 +02:00
|
|
|
private bool isAlive = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Vector3 oppositeVector;
|
|
|
|
|
|
|
|
private PlayerComponent player;
|
|
|
|
private float distanceToPlayer;
|
|
|
|
private FlareRegister flareRegister;
|
|
|
|
[SerializeField]
|
|
|
|
[Tooltip("This is the angle of visibility the enemy has")]
|
|
|
|
private float visibilityConeLimit = 90f;
|
|
|
|
[SerializeField]
|
|
|
|
private float bulletSoundRange = 15f;
|
|
|
|
|
|
|
|
private GameObject targetObject;
|
|
|
|
[SerializeField]
|
|
|
|
private float newTargetCooldown = 5f;
|
|
|
|
private float timeSinceTarget = 0f;
|
|
|
|
|
2023-04-22 09:18:21 +02:00
|
|
|
private InGameManager manager;
|
|
|
|
private bool prePauseStoppedState = false;
|
2023-04-21 09:30:43 +02:00
|
|
|
|
|
|
|
private void Awake()
|
2023-04-18 06:29:51 +02:00
|
|
|
{
|
2023-04-21 09:30:43 +02:00
|
|
|
//Find active player rn.
|
|
|
|
PlayerComponent[] players = GameObject.FindObjectsOfType<PlayerComponent>();
|
|
|
|
foreach (PlayerComponent p in players)
|
|
|
|
{
|
|
|
|
if (p.isActiveAndEnabled)
|
|
|
|
{
|
|
|
|
player = p;
|
|
|
|
}
|
|
|
|
}
|
2023-04-18 06:29:51 +02:00
|
|
|
|
2023-04-22 09:18:21 +02:00
|
|
|
manager = GameObject.FindObjectOfType<InGameManager>();
|
2023-04-21 09:30:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void Start()
|
|
|
|
{
|
|
|
|
flareRegister = GameObject.FindObjectOfType<FlareRegister>();
|
|
|
|
if (targetObject == null)
|
|
|
|
targetObject = new GameObject();
|
|
|
|
targetObject.name = "Enemy Target";
|
|
|
|
|
2023-04-22 09:18:21 +02:00
|
|
|
if(player == null)
|
|
|
|
{
|
|
|
|
player = GameObject.FindObjectOfType<PlayerComponent>();
|
|
|
|
}
|
2023-04-21 09:30:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void HandleTargetOperations()
|
|
|
|
{
|
|
|
|
//Update booleans for movement
|
|
|
|
float distToTarget = Vector3.Distance(target.target.transform.position, agent.transform.position);
|
|
|
|
atTarget = atTargetDistance >= distToTarget;
|
|
|
|
if (target.hasDamageRange)
|
|
|
|
{
|
|
|
|
inDamageRange = target.damageRange <= distToTarget;
|
|
|
|
inDamageMargin = target.damageMargin + target.damageRange <= distToTarget;
|
|
|
|
}
|
|
|
|
else
|
2023-04-18 06:29:51 +02:00
|
|
|
{
|
2023-04-21 09:30:43 +02:00
|
|
|
inDamageRange = false;
|
|
|
|
inDamageMargin = false;
|
|
|
|
}
|
|
|
|
//Perform actions.
|
|
|
|
if (target.runTowards)
|
|
|
|
{
|
|
|
|
if (inDamageRange)
|
2023-04-18 06:29:51 +02:00
|
|
|
{
|
2023-04-21 09:30:43 +02:00
|
|
|
//Damage is being dealt
|
|
|
|
animator.InLight();
|
|
|
|
//Deal Damage
|
|
|
|
health -= Time.deltaTime;
|
|
|
|
if (health < 0 && this.isAlive)
|
|
|
|
{
|
|
|
|
this.Kill();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (inDamageMargin)
|
|
|
|
{
|
|
|
|
//Effective stop
|
|
|
|
agent.SetDestination(transform.position);
|
|
|
|
animator.StopMoving();
|
|
|
|
|
2023-04-18 06:29:51 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-21 09:30:43 +02:00
|
|
|
if (atTarget)
|
|
|
|
{
|
|
|
|
//Effective stop
|
|
|
|
agent.SetDestination(transform.position);
|
|
|
|
animator.StopMoving();
|
|
|
|
if (target.isHostile)
|
|
|
|
{
|
|
|
|
|
|
|
|
animator.Attack();
|
|
|
|
animator.SetAttackType(Random.Range(0, 0));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
agent.SetDestination(target.target.transform.position);
|
|
|
|
animator.StartMoving();
|
|
|
|
animator.NotInLight();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Run away logic
|
|
|
|
if (atTarget || distToTarget < 3)
|
|
|
|
{
|
|
|
|
animator.StopMoving();
|
2023-04-18 06:29:51 +02:00
|
|
|
}
|
2023-04-21 09:30:43 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Ray r = new Ray();
|
|
|
|
r.origin = transform.position;
|
|
|
|
r.direction = oppositeVector;
|
|
|
|
RaycastHit hit;
|
|
|
|
if (Physics.Raycast(r, out hit))
|
|
|
|
{
|
2023-04-18 06:29:51 +02:00
|
|
|
|
2023-04-21 09:30:43 +02:00
|
|
|
animator.StartMoving();
|
|
|
|
agent.SetDestination(hit.point);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
agent.SetDestination(transform.position + oppositeVector * 100);
|
|
|
|
agent.isStopped = false;
|
|
|
|
}
|
|
|
|
}
|
2023-04-18 06:29:51 +02:00
|
|
|
|
|
|
|
}
|
2023-04-21 09:30:43 +02:00
|
|
|
|
|
|
|
if (atTarget&&target.isHostile)
|
|
|
|
{
|
|
|
|
SlowLookAt(target.target.transform.position-transform.position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void SlowLookAt(Vector3 targetVector,float animatedRotationSpeed=1f)
|
|
|
|
{
|
|
|
|
Vector3 relativePos = targetVector;
|
|
|
|
Quaternion toRotation = Quaternion.LookRotation(relativePos);
|
|
|
|
|
|
|
|
transform.rotation = Quaternion.Lerp(transform.rotation, toRotation, animatedRotationSpeed * Time.deltaTime);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckForAOERanges()
|
|
|
|
{
|
|
|
|
foreach(BulletComponent bullet in flareRegister.bullets)
|
|
|
|
{
|
|
|
|
float dist = Vector3.Distance(bullet.transform.position, transform.position);
|
|
|
|
if (dist <= bullet.DamageRange)
|
|
|
|
{
|
|
|
|
this.health = 0;
|
|
|
|
this.Kill(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach(FlareBeacon beacon in flareRegister.beacons)
|
|
|
|
{
|
|
|
|
float dist = Vector3.Distance(beacon.transform.position, transform.position);
|
|
|
|
if (dist <= beacon.Range)
|
|
|
|
{
|
|
|
|
this.health = 0f;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (this.health <= 0)
|
|
|
|
{
|
|
|
|
this.Kill();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool isPlayerVisible(bool withAngle)
|
|
|
|
{
|
|
|
|
Ray r = new Ray();
|
|
|
|
r.origin = transform.position;
|
|
|
|
r.direction = player.transform.position - transform.position;
|
|
|
|
Vector3 toPosition = (player.transform.position - transform.position).normalized;
|
|
|
|
float angleToPosition = Vector3.Angle(transform.forward, toPosition);
|
|
|
|
|
|
|
|
|
|
|
|
RaycastHit hit;
|
|
|
|
if(Physics.Raycast(r, out hit))
|
|
|
|
{
|
|
|
|
|
|
|
|
GameObject hitObject = hit.transform.gameObject;
|
|
|
|
|
|
|
|
if (hitObject.GetComponent<PlayerComponent>() != null)
|
|
|
|
{
|
|
|
|
//hit player
|
|
|
|
return angleToPosition <= this.visibilityConeLimit||!withAngle;
|
|
|
|
|
|
|
|
}else if (hitObject.GetComponentInParent<PlayerComponent>() != null)
|
|
|
|
{
|
|
|
|
//also hit player
|
|
|
|
return angleToPosition <= this.visibilityConeLimit||!withAngle;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetLiveTargeting()
|
|
|
|
{
|
|
|
|
if (!isAlive)
|
|
|
|
{
|
|
|
|
//this.targetObject.transform.position = transform.position;
|
|
|
|
this.Stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
distanceToPlayer = Vector3.Distance(this.transform.position, player.transform.position);
|
|
|
|
//print("Dist Comparison "+distanceToPlayer.ToString()+" Noise:"+player.NoiseManager.NoiseDistance);
|
|
|
|
bool isPlayerVisible = this.isPlayerVisible(true);
|
|
|
|
//check if player is heard or player line of sight
|
|
|
|
if (distanceToPlayer <= player.NoiseManager.NoiseDistance||isPlayerVisible)
|
|
|
|
{
|
|
|
|
//check that nothing in between
|
|
|
|
if (this.isPlayerVisible(false))
|
|
|
|
{
|
|
|
|
this.Target(player.gameObject, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (this.timeSinceTarget < this.newTargetCooldown)
|
|
|
|
{
|
|
|
|
//no further targeting
|
|
|
|
//Stop();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BulletComponent closestBullet = null;
|
|
|
|
float closestDistance = Mathf.Infinity;
|
|
|
|
foreach(BulletComponent bullet in this.flareRegister.bullets)
|
|
|
|
{
|
|
|
|
float bDist = Vector3.Distance(bullet.transform.position, transform.position);
|
|
|
|
if (closestBullet==null||(bDist< bulletSoundRange&&bDist<closestDistance))
|
|
|
|
{
|
|
|
|
closestBullet = bullet;
|
|
|
|
closestDistance = bDist;
|
|
|
|
}
|
|
|
|
}
|
2023-04-22 09:18:21 +02:00
|
|
|
if (closestBullet != null&&closestBullet.DamageRange==0)
|
2023-04-21 09:30:43 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
targetObject.transform.position= closestBullet.transform.position;
|
|
|
|
this.Target(targetObject,false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
targetObject.transform.position = transform.position;
|
|
|
|
this.Target(targetObject,false);
|
|
|
|
this.Stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void Update()
|
|
|
|
{
|
|
|
|
if (!player.IsAlive)
|
|
|
|
{
|
|
|
|
Stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target != null)
|
|
|
|
{
|
|
|
|
HandleTargetOperations();
|
|
|
|
}
|
|
|
|
CheckForAOERanges();
|
|
|
|
if(isAlive&&player.IsAlive)
|
|
|
|
SetLiveTargeting();
|
|
|
|
timeSinceTarget += Time.deltaTime;
|
|
|
|
|
2023-04-22 09:18:21 +02:00
|
|
|
|
|
|
|
|
2023-04-21 09:30:43 +02:00
|
|
|
/*
|
|
|
|
AI Behavior:
|
|
|
|
A. If not targeting player
|
|
|
|
1. Get distance to player
|
|
|
|
- if distance < player.NoiseDistance then target player.
|
|
|
|
2. Raycast to player to test line of sight
|
|
|
|
- if in line of sight and angle between LOS vector and forward vector is < LOF margin, then
|
|
|
|
target player.
|
|
|
|
B. If targeting player
|
|
|
|
1. If out of sound range and no line of sight then lose target.
|
|
|
|
- stop()
|
|
|
|
2. If beacon placed down and in range
|
|
|
|
- remove agent
|
|
|
|
- kill()
|
|
|
|
3. If beacon placed down and in margin:
|
|
|
|
- stop() and wait for player to leave AOE (enforced by navigation).
|
|
|
|
*/
|
2023-04-18 06:29:51 +02:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
STANDARD BEHAVIOR:
|
|
|
|
- OnSeeing/Hearing Player:
|
|
|
|
- Run towards Player
|
|
|
|
- OnHearing Bolt:
|
|
|
|
- Run towards bolt
|
|
|
|
- OnSeeing Flare:
|
|
|
|
- Run towards flare if not within (flareRange + flareMargin).
|
|
|
|
- This acts like if you are far enough away that you don't feel the pain of the flare
|
|
|
|
- OnSeeing FlareBeacon:
|
|
|
|
- Run away if within (flareBeaconRange + flareBeaconMargin).
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
//Runs towards target. If its meant to be hostile, it will melee attack once in range.
|
|
|
|
public void Target(GameObject obj,bool hostile=false)
|
|
|
|
{
|
2023-04-21 09:30:43 +02:00
|
|
|
if(target == null || target.target!= obj)
|
2023-04-18 06:29:51 +02:00
|
|
|
{
|
|
|
|
//target = new TargetInformation(obj,hostile,false,runTowards:true);
|
2023-04-21 09:30:43 +02:00
|
|
|
target = new TargetInformation();
|
|
|
|
target.target = obj;
|
|
|
|
target.isHostile = hostile;
|
|
|
|
target.hasDamageRange = false;
|
|
|
|
target.runTowards = true;
|
|
|
|
timeSinceTarget = 0;
|
|
|
|
|
2023-04-18 06:29:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
//Runs towards flare such that it stops randomly within margin before range.
|
|
|
|
public void TargetFlare(GameObject obj,float range,float margin)
|
|
|
|
{
|
2023-04-21 09:30:43 +02:00
|
|
|
if(target == null || target.target != obj)
|
2023-04-18 06:29:51 +02:00
|
|
|
{
|
|
|
|
//target = new TargetInformation(obj, false, true, range, margin,runTowards:true);
|
2023-04-21 09:30:43 +02:00
|
|
|
target = new TargetInformation();
|
|
|
|
target.target = obj;
|
|
|
|
target.isHostile = false;
|
|
|
|
target.hasDamageRange = true;
|
|
|
|
target.damageRange = range;
|
|
|
|
target.damageMargin = margin;
|
|
|
|
target.runTowards = true;
|
|
|
|
timeSinceTarget = 0;
|
2023-04-18 06:29:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
|
|
/// Runs away from object. A minimum range is specified where it no longer takes damage, and a minimum margin is
|
|
|
|
/// speicifed where it can stop running.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="obj"></param>
|
|
|
|
/// <param name="minRange"></param>
|
|
|
|
/// <param name="minMargin"></param>
|
|
|
|
public void RunAwayFrom(GameObject obj,float minRange,float minMargin) {
|
2023-04-21 09:30:43 +02:00
|
|
|
if(target== null ||target.target != obj)
|
2023-04-18 06:29:51 +02:00
|
|
|
{
|
|
|
|
//target = new TargetInformation(obj, false, true, minRange, minMargin, false);
|
2023-04-21 09:30:43 +02:00
|
|
|
target = new TargetInformation();
|
|
|
|
target.target = obj;
|
|
|
|
target.isHostile = false;
|
|
|
|
target.hasDamageRange = true;
|
|
|
|
target.damageRange = minRange;
|
|
|
|
target.damageMargin = minMargin;
|
|
|
|
target.runTowards = false;
|
|
|
|
oppositeVector = -(target.target.transform.position - transform.position).normalized;
|
|
|
|
timeSinceTarget = 0;
|
2023-04-18 06:29:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-21 09:30:43 +02:00
|
|
|
public void Stop()
|
|
|
|
{
|
|
|
|
target = new TargetInformation();
|
|
|
|
target.target = this.gameObject;
|
|
|
|
target.isHostile = false;
|
|
|
|
target.hasDamageRange = false;
|
|
|
|
target.runTowards = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Kill(bool fast=false)
|
|
|
|
{
|
|
|
|
if (this.isAlive)
|
|
|
|
{
|
|
|
|
this.animator.Kill(fast);
|
|
|
|
this.isAlive = false;
|
|
|
|
agent.isStopped = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
private void OnTriggerEnter(Collider other)
|
|
|
|
{
|
|
|
|
if (other.gameObject.GetComponent<BulletComponent>() != null)
|
|
|
|
{
|
|
|
|
|
|
|
|
this.health -= other.gameObject.GetComponent<BulletComponent>().DamageMagnitude;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-04-18 06:29:51 +02:00
|
|
|
}
|
|
|
|
class TargetInformation
|
|
|
|
{
|
|
|
|
|
|
|
|
public GameObject target;
|
|
|
|
public bool isHostile;
|
|
|
|
public bool hasDamageRange;
|
|
|
|
public float damageRange;
|
|
|
|
public float damageMargin;
|
|
|
|
public bool runTowards;
|
|
|
|
|
|
|
|
}
|