first commit

This commit is contained in:
2026-06-04 23:22:13 +03:00
commit d329f501be
310 changed files with 36395 additions and 0 deletions
+65
View File
@@ -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();
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5cc71487b17c578579e59895008f7bb4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+125
View File
@@ -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);
}
}
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e203683fe758dec587d4bddb4347392
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+55
View File
@@ -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: