first commit
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTGIntegrated.Combat
|
||||
{
|
||||
public class Damageable : MonoBehaviour
|
||||
{
|
||||
public string displayName = "Target";
|
||||
[Min(1f)] public float maxHealth = 50f;
|
||||
[SerializeField] protected float currentHealth = 50f;
|
||||
public Transform floatingDamageAnchor;
|
||||
|
||||
public event Action<float> OnDamageTaken;
|
||||
public event Action OnDeath;
|
||||
|
||||
public float CurrentHealth => currentHealth;
|
||||
public float MaxHealth => maxHealth;
|
||||
public bool IsAlive => currentHealth > 0f;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
maxHealth = Mathf.Max(1f, maxHealth);
|
||||
if (currentHealth <= 0f)
|
||||
{
|
||||
currentHealth = maxHealth;
|
||||
}
|
||||
|
||||
currentHealth = Mathf.Clamp(currentHealth, 0f, maxHealth);
|
||||
}
|
||||
|
||||
public virtual void TakeDamage(float amount, GameObject source = null)
|
||||
{
|
||||
if (!IsAlive || amount <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentHealth = Mathf.Clamp(currentHealth - amount, 0f, maxHealth);
|
||||
OnDamageTaken?.Invoke(amount);
|
||||
|
||||
Vector3 anchor = floatingDamageAnchor != null ? floatingDamageAnchor.position : transform.position + Vector3.up * 1.6f;
|
||||
FloatingDamage.Spawn(anchor, Mathf.RoundToInt(amount), new Color(1f, 0.46f, 0.24f, 1f));
|
||||
|
||||
if (currentHealth <= 0f)
|
||||
{
|
||||
HandleDeath(source);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Heal(float amount)
|
||||
{
|
||||
if (amount <= 0f || !IsAlive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentHealth = Mathf.Clamp(currentHealth + amount, 0f, maxHealth);
|
||||
}
|
||||
|
||||
protected virtual void HandleDeath(GameObject source)
|
||||
{
|
||||
OnDeath?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5cc71487b17c578579e59895008f7bb4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OTGIntegrated.Core;
|
||||
using OTGIntegrated.Items;
|
||||
using OTGIntegrated.Stats;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTGIntegrated.Combat
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class EnemyDrop
|
||||
{
|
||||
public ItemData item;
|
||||
[Min(1)] public int amount = 1;
|
||||
}
|
||||
|
||||
public sealed class Enemy : Damageable
|
||||
{
|
||||
public string enemyId = "Goblin";
|
||||
public float damage = 5f;
|
||||
public int expReward = 20;
|
||||
public List<EnemyDrop> dropItems = new List<EnemyDrop>();
|
||||
public float aggroRange = 8f;
|
||||
public float attackRange = 1.8f;
|
||||
public float attackCooldown = 1.4f;
|
||||
public float moveSpeed = 2.2f;
|
||||
|
||||
private Transform player;
|
||||
private PlayerStats playerStats;
|
||||
private float nextAttackTime;
|
||||
private bool dead;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
currentHealth = maxHealth;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (GameManager.Instance != null && GameManager.Instance.playerStats != null)
|
||||
{
|
||||
playerStats = GameManager.Instance.playerStats;
|
||||
player = playerStats.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerStats stats = FindObjectOfType<PlayerStats>();
|
||||
if (stats != null)
|
||||
{
|
||||
playerStats = stats;
|
||||
player = stats.transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsAlive || player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 toPlayer = player.position - transform.position;
|
||||
toPlayer.y = 0f;
|
||||
float distance = toPlayer.magnitude;
|
||||
|
||||
if (distance <= aggroRange && distance > attackRange)
|
||||
{
|
||||
Vector3 direction = toPlayer.normalized;
|
||||
transform.position += direction * moveSpeed * Time.deltaTime;
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), 8f * Time.deltaTime);
|
||||
}
|
||||
|
||||
if (distance <= attackRange && Time.time >= nextAttackTime)
|
||||
{
|
||||
nextAttackTime = Time.time + attackCooldown;
|
||||
if (playerStats != null)
|
||||
{
|
||||
playerStats.TakeDamage(damage);
|
||||
GameEvents.RaiseNotification($"{displayName} наносит {Mathf.RoundToInt(damage)} урона");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HandleDeath(GameObject source)
|
||||
{
|
||||
if (dead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dead = true;
|
||||
DropRewards();
|
||||
|
||||
if (GameManager.Instance != null && GameManager.Instance.levelSystem != null && expReward > 0)
|
||||
{
|
||||
GameManager.Instance.levelSystem.AddExperience(expReward);
|
||||
}
|
||||
|
||||
GameEvents.RaiseEnemyKilled(enemyId);
|
||||
GameEvents.RaiseNotification($"{displayName} повержен");
|
||||
base.HandleDeath(source);
|
||||
Destroy(gameObject, 0.15f);
|
||||
}
|
||||
|
||||
private void DropRewards()
|
||||
{
|
||||
OTGIntegrated.Inventory.Inventory inventory = GameManager.Instance != null ? GameManager.Instance.inventory : null;
|
||||
if (inventory == null || dropItems == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < dropItems.Count; i++)
|
||||
{
|
||||
EnemyDrop drop = dropItems[i];
|
||||
if (drop != null && drop.item != null && drop.amount > 0)
|
||||
{
|
||||
inventory.AddItem(drop.item, drop.amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e203683fe758dec587d4bddb4347392
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTGIntegrated.Combat
|
||||
{
|
||||
public sealed class FloatingDamage : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private float lifetime = 1.1f;
|
||||
[SerializeField] private float riseSpeed = 1.35f;
|
||||
|
||||
private TextMesh textMesh;
|
||||
private float age;
|
||||
|
||||
public static void Spawn(Vector3 position, int amount, Color color)
|
||||
{
|
||||
GameObject root = new GameObject("FloatingDamage");
|
||||
root.transform.position = position;
|
||||
FloatingDamage floatingDamage = root.AddComponent<FloatingDamage>();
|
||||
floatingDamage.Configure(amount.ToString(), color);
|
||||
}
|
||||
|
||||
private void Configure(string text, Color color)
|
||||
{
|
||||
textMesh = gameObject.AddComponent<TextMesh>();
|
||||
textMesh.text = text;
|
||||
textMesh.anchor = TextAnchor.MiddleCenter;
|
||||
textMesh.alignment = TextAlignment.Center;
|
||||
textMesh.characterSize = 0.22f;
|
||||
textMesh.fontSize = 48;
|
||||
textMesh.color = color;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
age += Time.deltaTime;
|
||||
transform.position += Vector3.up * riseSpeed * Time.deltaTime;
|
||||
|
||||
if (Camera.main != null)
|
||||
{
|
||||
transform.rotation = Quaternion.LookRotation(transform.position - Camera.main.transform.position, Vector3.up);
|
||||
}
|
||||
|
||||
if (textMesh != null)
|
||||
{
|
||||
Color color = textMesh.color;
|
||||
color.a = Mathf.Clamp01(1f - age / lifetime);
|
||||
textMesh.color = color;
|
||||
}
|
||||
|
||||
if (age >= lifetime)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b803690c5148050782bf7e789b81cde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user