Untitled

mail@pastecode.io avatar
unknown
csharp
a year ago
6.6 kB
1
Indexable
Never
using System;
using System.Collections.Generic;
using System.Linq;
using Entitas;
using GameplayModules.Stats;
using GridScene;
using Unity.Mathematics;
using UnityEngine;
using Random = UnityEngine.Random;

public class BaseAttackInstance : MonoBehaviour
{
    [SerializeField] private float _outDuration;
    [SerializeField] private GameObject[] _activateOnOut;
    [SerializeField] private GameObject[] _deactivateOnOut;
    [SerializeField] protected float HitRadius;
    [SerializeField] protected GameObject DebugHitZone;
    
    #region Temporary
    [SerializeField] private float _knockBackMaxForce = 10.0f;
    [SerializeField] private float _knockBackFadeSpeed = 1.0f;
    [SerializeField] private float _knockBackDuration = 1.0f;
    #endregion Temporary

    private GameObject _attackEnemyHitVfx;
    private GameObject _attackObstacleHitVfx;
    private bool _isPaused;
    private bool _isDestroying;
    private Contexts _contexts;
    
    protected GridManager GridManager;
    protected List<uint> EnemyIds = new List<uint>();
    protected Dictionary<EStat, float> StatsLevel = new Dictionary<EStat, float>();
    
    public Action<BaseAttackInstance> OnInstanceDestroyed;
    
    protected bool IsUpdating => !_isDestroying && !_isPaused;
    
    public bool IsPaused
    {
        set => _isPaused = value;
    }

    public virtual void Initialize(Dictionary<EStat, float> statsLevel, GameObject attackEnemyHitVfx, GameObject obstacleHitVfx)
    {
        StatsLevel = statsLevel;
        _attackEnemyHitVfx = attackEnemyHitVfx;
        _attackObstacleHitVfx = obstacleHitVfx;
        GridManager = GridManager.Instance;
        _contexts = Contexts.sharedInstance;
    }

    protected void ApplyDamage(uint[] enemyId)
    {
        float damage = StatsLevel[EStat.Damage];
        float stunDuration = StatsLevel[EStat.StunDuration];
        float slowedDuration = StatsLevel[EStat.SlowDuration];
        float slowedSpeedPenalty = StatsLevel[EStat.SlowSpeedPenalty];
        float criticalChance = StatsLevel[EStat.CriticalChance];
        if (Random.Range(0, 1f) < criticalChance)
        {
            damage *= StatsLevel[EStat.CriticalDamage];
        }

        GameEntity[] entities = Contexts.sharedInstance.game.GetEntities(Matcher<GameEntity>.AllOf(GameMatcher.EnemyId, GameMatcher.Health));
        foreach (var gameEntity in entities)
        {
            if (!enemyId.Contains(gameEntity.enemyId.value)) continue;
            float damageReceived = damage - gameEntity.health.Armor * Mathf.Clamp01(1f - StatsLevel[EStat.ArmorPenetration]);
            gameEntity.health.DamageReceived += damageReceived;
            
            EnemyIds.Add(gameEntity.enemyId.value);
            ApplyEffect(gameEntity, stunDuration, slowedDuration, slowedSpeedPenalty);
            SpawnEnemyHitVfx();
        }
        
        ApplyKnockBack(entities, enemyId);
    }

    private void ApplyKnockBack(GameEntity[] ges, uint[] ids)
    {
        GameEntity impactEntity = _contexts.game.CreateEntity();
        impactEntity.AddImpact(true);
        impactEntity.AddImpactIds(ids);

        float2[] directions = new float2[ges.Length];
        float[] forces = new float[ges.Length];
        float[] durations = new float[ges.Length];
        float[] elapsedTimes = new float[ges.Length];
        float[] fadeSpeeds = new float[ges.Length];
        for (int iEntity = 0; iEntity < ges.Length; iEntity++)
        {
            directions[iEntity] = GetKnockBackDirection(ges[iEntity]);
            forces[iEntity] = _knockBackMaxForce;
            durations[iEntity] = _knockBackDuration;
            elapsedTimes[iEntity] = 0.0f;
            fadeSpeeds[iEntity] = _knockBackFadeSpeed;
        }
        
        impactEntity.AddImpactDirections(directions);
        impactEntity.AddImpactForces(forces);
        impactEntity.AddImpactDurations(durations);
        impactEntity.AddImpactElapsedTimes(elapsedTimes);
        impactEntity.AddImpactFadeSpeeds(fadeSpeeds);
        impactEntity.AddRepulsionForce(0.0f);
    }

    private void ApplyEffect(GameEntity gameEntity, float stunDuration, float slowedDuration, float slowedSpeedPenalty)
    {
        if (slowedDuration > 0f)
        {
            if (slowedSpeedPenalty > gameEntity.enemySpeed.SlowedSpeedPenalty || 
                (Math.Abs(slowedSpeedPenalty - gameEntity.enemySpeed.SlowedSpeedPenalty) < 0.01f && slowedDuration > gameEntity.enemySpeed.SlowedTimer))
            {
                gameEntity.enemySpeed.SlowedTimer = slowedDuration;  
                gameEntity.enemySpeed.SlowedSpeedPenalty = slowedSpeedPenalty;  
            }
        }
        
        if (stunDuration > 0f && stunDuration > gameEntity.enemySpeed.StunTimer)
        {
            gameEntity.enemySpeed.StunTimer = stunDuration;    
        }
    }

    private void SpawnEnemyHitVfx()
    {
        if (_attackEnemyHitVfx == null) return;
        
        Transform attackTransform = transform;
        Instantiate(_attackEnemyHitVfx, attackTransform.position, attackTransform.rotation);
    }

    protected virtual float2 GetKnockBackDirection(GameEntity ge)
    {
        Vector3 pos3 = GridManager.PlayerTransform.position;
        float2 pos2 = new float2(pos3.x, pos3.z);
        return math.normalize(ge.position.value - pos2);
    }

    protected void SpawnObstacleHitVfx(Vector3 obstaclePosition)
    {
        if (_attackObstacleHitVfx == null) return;
        
        Transform attackTransform = transform;
        Instantiate(_attackObstacleHitVfx, obstaclePosition, attackTransform.rotation);
    }

    protected virtual void DetectHit()
    {
        
    }

    public virtual void DestroyInstance()
    {
        _isDestroying = true;
        OnInstanceDestroyed?.Invoke(this);
        if (_outDuration > 0f)
        {
            foreach (var go in _activateOnOut)
            {
                go.SetActive(true);
            }
            
            foreach (var go in _deactivateOnOut)
            {
                go.SetActive(false);
            }
            
            Invoke(nameof(DestroyObject), _outDuration);
        }
        else
        {
            DestroyObject();
        }
    }

    private void DestroyObject()
    {
        if (gameObject != null)
        {
            Destroy(gameObject); //gameobject is already destroyed when you stop the game (sometimes)    
        }
    }
}