Newer
Older
BlackoutClient / Assets / Gameplay / Virus.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.AI;

public class Virus : MonoBehaviour
{
    private Item item;
    private NavMeshAgent nav;
    void Awake()
    {
        item = GetComponentInChildren<Item>();
        nav = GetComponentInChildren<NavMeshAgent>();
        nav.isStopped = true;
        repathTimer = new SimpleTimer();
        attackTween = new SimpleTween();
        attackTimer = new SimpleTimer();
    }

    public GameObject MeshRoot;
        
    // Start is called before the first frame update
    void Start()
    {
        
    }

    void StartLocal()
    {
        PathToPlayer();
    }

    private float pathTime;
    private Player targetPlayer;
    private SimpleTimer repathTimer;
    private SimpleTween attackTween;
    private SimpleTimer attackTimer;

    private void PathToPlayer()
    {
        List<Player> players = new List<Player>();
        List<float> weights = new List<float>();

        foreach (Player p in FindObjectsOfType<Player>())
        {
            players.Add(p);

            float dist = (p.transform.position - transform.position).magnitude;
            weights.Add(30 - dist);
        }

        if (players.Count == 0)
        {
            repathTimer.Start(3, PathToPlayer);
        }

        float total = weights.Sum();

        targetPlayer = null;

        float rand = UnityEngine.Random.Range(0, total);
        float sum = 0;
        for (int i = 0; i < players.Count; i++)
        {
            sum += weights[i];
            if (rand < sum || i == players.Count - 1)
            {
                targetPlayer = players[i];
                break;
            }
        }

        if (targetPlayer != null)
        {
            nav.stoppingDistance = AttackMaxDist * 0.8f;
            pathTime = Time.realtimeSinceStartup;
            nav.isStopped = false;
            nav.destination = TargetBasePos;
        }
    }

    public void StopPathing()
    {
        nav.isStopped = true;
    }

    public bool IsPathFinished()
    {
        if (!nav.pathPending)
        {
            if (nav.remainingDistance <= nav.stoppingDistance)
            {
                if (!nav.hasPath || nav.velocity.sqrMagnitude == 0f)
                {
                    return true;
                }
            }
        }

        return false;
    }

    public Vector3 TargetBasePos
    {
        get
        {
            if (targetPlayer == null) return new Vector3();
            return targetPlayer.transform.position + Vector3.down * targetPlayer.BottomOffset;
        }
    }

    public float DistToTarget
    {
        get
        {
            if (targetPlayer == null) return 99999;
            return (TargetBasePos - transform.position).magnitude;
        }
    }

    public float AttackMaxDist = 1.3f;

    public float RepathTime = 1.3f;

    // Update is called once per frame
    void Update()
    {
        if (item != null && item.IsLocal && !nav.isStopped)
        {
            if (IsPathFinished())
            {
                if (DistToTarget > AttackMaxDist)
                {
                    nav.destination = TargetBasePos;
                }
                else
                {
                    StopPathing();
                    AttackTarget();
                }
            }
            else if (targetPlayer == null)
            {
                StopPathing();
                targetPlayer = null;
                PathToPlayer();
            }
            else if (Time.realtimeSinceStartup > pathTime + RepathTime && DistToTarget > 2)
            {
                nav.destination = TargetBasePos;
            }
        }
    }

    void TakeDamage(object detailsObj)
    {
        Dictionary<string, object> details = detailsObj as Dictionary<string, object>;

        if (details != null && details.ContainsKey("damage"))
        {
            item.DestroyItem();
        }
    }

    void OnDestroy()
    {
        attacking = false;
        damaged.Clear();
        attackTween.Stop();
        repathTimer.Stop();
        attackTimer.Stop();
    }

    public float AttackTime = 0.2f;
    public float AttackDistMult = 1.3f;

    public float Damage = 0.4f;

    private void AttackTarget()
    {
        Vector3 startPos = transform.position;
        Vector3 delta = TargetBasePos - startPos;

        delta *= AttackDistMult;
        if (delta.magnitude < AttackMaxDist * 0.4f)
        {
            delta = delta.normalized * AttackMaxDist * 0.4f;
        }

        attacking = true;
        damaged.Clear();

        attackTween.Init(
            v =>
            {
                transform.position = startPos + delta * v;
                MeshRoot.transform.localScale = new Vector3(1, 1, 1) * (v * 0.3f + 1.0f);
            }, SimpleTween.QuadEaseOut, 0, 1, AttackTime);
        attackTween.OnFinish = FinishedAttack;

        //attackTimer.Start(AttackTime / AttackDistMult, DamageTarget);
        
    }

    public float AttackWait = 0.8f;

    private bool attacking = false;
    private List<Player> damaged = new List<Player>();
    private void FinishedAttack()
    {
        attacking = false;
        MeshRoot.transform.localScale = new Vector3(1, 1, 1);

        if (damagedAny)
        {
            item.DestroyItem();
        }

        if (UnityEngine.Random.Range(0, 1) < 0.4f)
        {
            attackTimer.Start(0.3f, PathToPlayer);
            targetPlayer = null;
            return;
        }


        if (targetPlayer != null && DistToTarget <= AttackMaxDist)
        {
            attackTimer.Start(AttackWait, CheckAttackTarget);
        }
        else
        {
            targetPlayer = null;
            PathToPlayer();
        }
    }

    private void CheckAttackTarget()
    {
        if (targetPlayer != null && DistToTarget <= AttackMaxDist)
        {
            AttackTarget();
        }
        else
        {
            targetPlayer = null;
            PathToPlayer();
        }
    }
    private bool damagedAny = false;
    void OnTriggerEnter(Collider other)
    {
        Player p = other.GetComponentInParent<Player>();
        if (p != null && !damaged.Contains(p) && attacking)
        {
            DamagePlayer(p);
        }
    }

    public float MinDamageDist = 0.6f;
    private void DamagePlayer(Player p)
    {
        //if (targetPlayer != null && DistToTarget < MinDamageDist)
        //{
        damaged.Add(p);
        Item playerItem = p.GetComponentInChildren<Item>();
        if (playerItem != null && item != null && item.GetNetHost() != null)
        {
            item.GetNetHost().SendItemCommand(playerItem.Key, "TakeDamage", new Dictionary<string, object> { { "damage", Damage } });
            damagedAny = true;
        }
        //}
    }
}