366 lines
14 KiB
C#
366 lines
14 KiB
C#
using OctoberStudio.Easing;
|
|
using OctoberStudio.Extensions;
|
|
using OctoberStudio.Upgrades;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
namespace OctoberStudio
|
|
{
|
|
public class PlayerBehavior : MonoBehaviour
|
|
{
|
|
protected static readonly int DEATH_HASH = "Death".GetHashCode();
|
|
protected static readonly int REVIVE_HASH = "Revive".GetHashCode();
|
|
protected static readonly int RECEIVING_DAMAGE_HASH = "Receiving Damage".GetHashCode();
|
|
|
|
protected static PlayerBehavior instance;
|
|
public static PlayerBehavior Player => instance;
|
|
|
|
[SerializeField] protected CharactersDatabase charactersDatabase;
|
|
|
|
[Header("Stats")]
|
|
[SerializeField, Min(0.01f)] protected float speed = 2;
|
|
[SerializeField, Min(0.1f)] protected float defaultMagnetRadius = 0.75f;
|
|
[SerializeField, Min(1f)] protected float xpMultiplier = 1;
|
|
[SerializeField, Range(0.1f, 1f)] protected float cooldownMultiplier = 1;
|
|
[SerializeField, Range(0, 100)] protected int initialDamageReductionPercent = 0;
|
|
[SerializeField, Min(1f)] protected float initialProjectileSpeedMultiplier = 1;
|
|
[SerializeField, Min(1f)] protected float initialSizeMultiplier = 1f;
|
|
[SerializeField, Min(1f)] protected float initialDurationMultiplier = 1f;
|
|
[SerializeField, Min(1f)] protected float initialGoldMultiplier = 1;
|
|
|
|
[Header("References")]
|
|
[SerializeField] protected HealthbarBehavior healthbar;
|
|
[SerializeField] protected Transform centerPoint;
|
|
[SerializeField] protected PlayerEnemyCollisionHelper collisionHelper;
|
|
|
|
public static Transform CenterTransform => instance.centerPoint;
|
|
public static Vector2 CenterPosition {
|
|
get
|
|
{
|
|
if(instance.Character != null && instance.Character.CenterTransform != null)
|
|
{
|
|
return instance.Character.CenterTransform.position;
|
|
}
|
|
return instance.centerPoint.position;
|
|
}
|
|
}
|
|
|
|
[Header("Death and Revive")]
|
|
[SerializeField] protected ParticleSystem reviveParticle;
|
|
|
|
[Space]
|
|
[SerializeField] protected SpriteRenderer reviveBackgroundSpriteRenderer;
|
|
[SerializeField, Range(0, 1)] protected float reviveBackgroundAlpha;
|
|
[SerializeField, Range(0, 1)] protected float reviveBackgroundSpawnDelay;
|
|
[SerializeField, Range(0, 1)] protected float reviveBackgroundHideDelay;
|
|
|
|
[Space]
|
|
[SerializeField] protected SpriteRenderer reviveBottomSpriteRenderer;
|
|
[SerializeField, Range(0, 1)] protected float reviveBottomAlpha;
|
|
[SerializeField, Range(0, 1)] protected float reviveBottomSpawnDelay;
|
|
[SerializeField, Range(0, 1)] protected float reviveBottomHideDelay;
|
|
|
|
[Header("Other")]
|
|
[SerializeField] protected Vector2 fenceOffset;
|
|
[SerializeField] protected Color hitColor;
|
|
[SerializeField] protected float enemyInsideDamageInterval = 2f;
|
|
|
|
public event UnityAction onPlayerDied;
|
|
|
|
public float Damage { get; protected set; }
|
|
public float MagnetRadiusSqr { get; protected set; }
|
|
public float Speed { get; protected set; }
|
|
|
|
public float XPMultiplier { get; protected set; }
|
|
public float CooldownMultiplier { get; protected set; }
|
|
public float DamageReductionMultiplier { get; protected set; }
|
|
public float ProjectileSpeedMultiplier { get; protected set; }
|
|
public float SizeMultiplier { get; protected set; }
|
|
public float DurationMultiplier { get; protected set; }
|
|
public float GoldMultiplier { get; protected set; }
|
|
|
|
public Vector2 LookDirection { get; protected set; }
|
|
public bool IsMovingAlowed { get; set; }
|
|
|
|
protected bool invincible = false;
|
|
|
|
protected List<EnemyBehavior> enemiesInside = new List<EnemyBehavior>();
|
|
|
|
protected CharactersSave charactersSave;
|
|
public CharacterData Data { get; set; }
|
|
protected ICharacterBehavior Character { get; set; }
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
charactersSave = GameController.SaveManager.GetSave<CharactersSave>("Characters");
|
|
Data = charactersDatabase.GetCharacterData(charactersSave.SelectedCharacterId);
|
|
|
|
Character = Instantiate(Data.Prefab).GetComponent<ICharacterBehavior>();
|
|
Character.Transform.SetParent(transform);
|
|
Character.Transform.ResetLocal();
|
|
|
|
instance = this;
|
|
healthbar.Init(Data.BaseHP);
|
|
healthbar.SetAutoHideWhenMax(true);
|
|
healthbar.SetAutoShowOnChanged(true);
|
|
|
|
RecalculateMagnetRadius(1);
|
|
RecalculateMoveSpeed(1);
|
|
RecalculateDamage(1);
|
|
RecalculateMaxHP(1);
|
|
RecalculateXPMuliplier(1);
|
|
RecalculateCooldownMuliplier(1);
|
|
RecalculateDamageReduction(0);
|
|
RecalculateProjectileSpeedMultiplier(1f);
|
|
RecalculateSizeMultiplier(1f);
|
|
RecalculateDurationMultiplier(1);
|
|
RecalculateGoldMultiplier(1);
|
|
|
|
LookDirection = Vector2.right;
|
|
|
|
IsMovingAlowed = true;
|
|
}
|
|
|
|
protected virtual void Update()
|
|
{
|
|
if (healthbar.IsZero) return;
|
|
|
|
foreach(var enemy in enemiesInside)
|
|
{
|
|
if (Time.time - enemy.LastTimeDamagedPlayer > enemyInsideDamageInterval)
|
|
{
|
|
TakeDamage(enemy.GetDamage());
|
|
enemy.LastTimeDamagedPlayer = Time.time;
|
|
}
|
|
}
|
|
|
|
if (!IsMovingAlowed) return;
|
|
|
|
var input = GameController.InputManager.MovementValue;
|
|
|
|
float joysticPower = input.magnitude;
|
|
Character.SetSpeed(joysticPower);
|
|
|
|
if (!Mathf.Approximately(joysticPower, 0) && Time.timeScale > 0)
|
|
{
|
|
var frameMovement = input * Time.deltaTime * Speed;
|
|
|
|
if (StageController.FieldManager.ValidatePosition(transform.position + Vector3.right * frameMovement.x, fenceOffset))
|
|
{
|
|
transform.position += Vector3.right * frameMovement.x;
|
|
}
|
|
|
|
if (StageController.FieldManager.ValidatePosition(transform.position + Vector3.up * frameMovement.y, fenceOffset))
|
|
{
|
|
transform.position += Vector3.up * frameMovement.y;
|
|
}
|
|
|
|
collisionHelper.transform.localPosition = Vector3.zero;
|
|
|
|
Character.SetLocalScale(new Vector3(input.x > 0 ? 1 : -1, 1, 1));
|
|
|
|
LookDirection = input.normalized;
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public virtual bool IsInsideMagnetRadius(Transform target)
|
|
{
|
|
return (transform.position - target.position).sqrMagnitude <= MagnetRadiusSqr;
|
|
}
|
|
|
|
public virtual void RecalculateMagnetRadius(float magnetRadiusMultiplier)
|
|
{
|
|
MagnetRadiusSqr = Mathf.Pow(defaultMagnetRadius * magnetRadiusMultiplier, 2);
|
|
}
|
|
|
|
public virtual void RecalculateMoveSpeed(float moveSpeedMultiplier)
|
|
{
|
|
Speed = speed * moveSpeedMultiplier;
|
|
}
|
|
|
|
public virtual void RecalculateDamage(float damageMultiplier)
|
|
{
|
|
Damage = Data.BaseDamage * damageMultiplier;
|
|
if (GameController.UpgradesManager.IsUpgradeAquired(UpgradeType.Damage))
|
|
{
|
|
Damage *= GameController.UpgradesManager.GetUpgadeValue(UpgradeType.Damage);
|
|
}
|
|
}
|
|
|
|
public virtual void RecalculateMaxHP(float maxHPMultiplier)
|
|
{
|
|
var upgradeValue = GameController.UpgradesManager.GetUpgadeValue(UpgradeType.Health);
|
|
healthbar.ChangeMaxHP((Data.BaseHP + upgradeValue) * maxHPMultiplier);
|
|
}
|
|
|
|
public virtual void RecalculateXPMuliplier(float xpMultiplier)
|
|
{
|
|
XPMultiplier = this.xpMultiplier * xpMultiplier;
|
|
}
|
|
|
|
public virtual void RecalculateCooldownMuliplier(float cooldownMultiplier)
|
|
{
|
|
CooldownMultiplier = this.cooldownMultiplier * cooldownMultiplier;
|
|
}
|
|
|
|
public virtual void RecalculateDamageReduction(float damageReductionPercent)
|
|
{
|
|
DamageReductionMultiplier = (100f - initialDamageReductionPercent - damageReductionPercent) / 100f;
|
|
|
|
if (GameController.UpgradesManager.IsUpgradeAquired(UpgradeType.Armor))
|
|
{
|
|
DamageReductionMultiplier *= GameController.UpgradesManager.GetUpgadeValue(UpgradeType.Armor);
|
|
}
|
|
}
|
|
|
|
public virtual void RecalculateProjectileSpeedMultiplier(float projectileSpeedMultiplier)
|
|
{
|
|
ProjectileSpeedMultiplier = initialProjectileSpeedMultiplier * projectileSpeedMultiplier;
|
|
}
|
|
|
|
public virtual void RecalculateSizeMultiplier(float sizeMultiplier)
|
|
{
|
|
SizeMultiplier = initialSizeMultiplier * sizeMultiplier;
|
|
}
|
|
|
|
public virtual void RecalculateDurationMultiplier(float durationMultiplier)
|
|
{
|
|
DurationMultiplier = initialDurationMultiplier * durationMultiplier;
|
|
}
|
|
|
|
public virtual void RecalculateGoldMultiplier(float goldMultiplier)
|
|
{
|
|
GoldMultiplier = initialGoldMultiplier * goldMultiplier;
|
|
}
|
|
|
|
public virtual void RestoreHP(float hpPercent)
|
|
{
|
|
healthbar.AddPercentage(hpPercent);
|
|
}
|
|
|
|
public virtual void Heal(float hp)
|
|
{
|
|
healthbar.AddHP(hp + GameController.UpgradesManager.GetUpgadeValue(UpgradeType.Healing));
|
|
}
|
|
|
|
public virtual void Revive()
|
|
{
|
|
Character.PlayReviveAnimation();
|
|
reviveParticle.Play();
|
|
|
|
invincible = true;
|
|
IsMovingAlowed = false;
|
|
healthbar.ResetHP(1f);
|
|
|
|
Character.SetSortingOrder(102);
|
|
|
|
reviveBackgroundSpriteRenderer.DoAlpha(0f, 0.3f, reviveBottomHideDelay).SetUnscaledTime(true).SetOnFinish(() => reviveBackgroundSpriteRenderer.gameObject.SetActive(false));
|
|
reviveBottomSpriteRenderer.DoAlpha(0f, 0.3f, reviveBottomHideDelay).SetUnscaledTime(true).SetOnFinish(() => reviveBottomSpriteRenderer.gameObject.SetActive(false));
|
|
|
|
GameController.AudioManager.PlaySound(REVIVE_HASH);
|
|
EasingManager.DoAfter(1f, () =>
|
|
{
|
|
IsMovingAlowed = true;
|
|
Character.SetSortingOrder(0);
|
|
});
|
|
|
|
EasingManager.DoAfter(3, () => invincible = false);
|
|
}
|
|
|
|
public virtual void CheckTriggerEnter2D(Collider2D collision)
|
|
{
|
|
if (collision.gameObject.layer == 7)
|
|
{
|
|
if (invincible) return;
|
|
|
|
var enemy = collision.GetComponent<EnemyBehavior>();
|
|
|
|
if (enemy != null)
|
|
{
|
|
enemiesInside.Add(enemy);
|
|
enemy.LastTimeDamagedPlayer = Time.time;
|
|
|
|
enemy.onEnemyDied += OnEnemyDied;
|
|
TakeDamage(enemy.GetDamage());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (invincible) return;
|
|
|
|
var projectile = collision.GetComponent<SimpleEnemyProjectileBehavior>();
|
|
if (projectile != null)
|
|
{
|
|
TakeDamage(projectile.Damage);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void CheckTriggerExit2D(Collider2D collision)
|
|
{
|
|
if (collision.gameObject.layer == 7)
|
|
{
|
|
if (invincible) return;
|
|
|
|
var enemy = collision.GetComponent<EnemyBehavior>();
|
|
|
|
if (enemy != null)
|
|
{
|
|
enemiesInside.Remove(enemy);
|
|
enemy.onEnemyDied -= OnEnemyDied;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void OnEnemyDied(EnemyBehavior enemy)
|
|
{
|
|
enemy.onEnemyDied -= OnEnemyDied;
|
|
enemiesInside.Remove(enemy);
|
|
}
|
|
|
|
protected float lastTimeVibrated = 0f;
|
|
|
|
public virtual void TakeDamage(float damage)
|
|
{
|
|
if (invincible || healthbar.IsZero) return;
|
|
|
|
healthbar.Subtract(damage * DamageReductionMultiplier);
|
|
|
|
Character.FlashHit();
|
|
|
|
if (healthbar.IsZero)
|
|
{
|
|
Character.PlayDefeatAnimation();
|
|
Character.SetSortingOrder(102);
|
|
|
|
reviveBackgroundSpriteRenderer.gameObject.SetActive(true);
|
|
reviveBackgroundSpriteRenderer.DoAlpha(reviveBackgroundAlpha, 0.3f, reviveBackgroundSpawnDelay).SetUnscaledTime(true);
|
|
reviveBackgroundSpriteRenderer.transform.position = transform.position.SetZ(reviveBackgroundSpriteRenderer.transform.position.z);
|
|
|
|
reviveBottomSpriteRenderer.gameObject.SetActive(true);
|
|
reviveBottomSpriteRenderer.DoAlpha(reviveBottomAlpha, 0.3f, reviveBottomSpawnDelay).SetUnscaledTime(true);
|
|
|
|
GameController.AudioManager.PlaySound(DEATH_HASH);
|
|
|
|
EasingManager.DoAfter(0.5f, () =>
|
|
{
|
|
onPlayerDied?.Invoke();
|
|
}).SetUnscaledTime(true);
|
|
|
|
GameController.VibrationManager.StrongVibration();
|
|
} else
|
|
{
|
|
if(Time.time - lastTimeVibrated > 0.05f)
|
|
{
|
|
GameController.VibrationManager.LightVibration();
|
|
lastTimeVibrated = Time.time;
|
|
}
|
|
|
|
GameController.AudioManager.PlaySound(RECEIVING_DAMAGE_HASH);
|
|
}
|
|
}
|
|
}
|
|
} |