first commit
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
using OTGIntegrated.Combat;
|
||||
using OTGIntegrated.Stats;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTGIntegrated.Weapons
|
||||
{
|
||||
public sealed class WeaponController : MonoBehaviour
|
||||
{
|
||||
public Transform attackOrigin;
|
||||
public LayerMask hitMask = ~0;
|
||||
[SerializeField] private float meleeRadius = 0.7f;
|
||||
|
||||
private readonly Collider[] hits = new Collider[16];
|
||||
|
||||
public bool Attack(WeaponData weapon, PlayerStats stats)
|
||||
{
|
||||
if (weapon == null || stats == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Transform origin = attackOrigin != null ? attackOrigin : transform;
|
||||
Vector3 start = origin.position + Vector3.up * 0.8f;
|
||||
Vector3 forward = origin.forward;
|
||||
forward.y = 0f;
|
||||
forward = forward.sqrMagnitude > 0.001f ? forward.normalized : transform.forward;
|
||||
|
||||
float finalDamage = weapon.damage + stats.Damage;
|
||||
bool critical = Random.value < stats.CriticalChance;
|
||||
if (critical)
|
||||
{
|
||||
finalDamage *= 1.75f;
|
||||
}
|
||||
|
||||
if (weapon.isRanged)
|
||||
{
|
||||
if (Physics.Raycast(start, forward, out RaycastHit hit, weapon.range, hitMask, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
return ApplyDamage(hit.collider, finalDamage, critical);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 center = start + forward * Mathf.Max(0.6f, weapon.range * 0.55f);
|
||||
int count = Physics.OverlapSphereNonAlloc(center, meleeRadius + weapon.range * 0.12f, hits, hitMask, QueryTriggerInteraction.Ignore);
|
||||
bool damaged = false;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Collider hit = hits[i];
|
||||
if (hit == null || hit.transform == transform || hit.GetComponentInParent<PlayerStats>() != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ApplyDamage(hit, finalDamage, critical))
|
||||
{
|
||||
damaged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return damaged;
|
||||
}
|
||||
|
||||
private bool ApplyDamage(Collider collider, float amount, bool critical)
|
||||
{
|
||||
Damageable damageable = collider.GetComponentInParent<Damageable>();
|
||||
if (damageable == null || !damageable.IsAlive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
damageable.TakeDamage(amount, gameObject);
|
||||
if (critical)
|
||||
{
|
||||
OTGIntegrated.Core.GameEvents.RaiseNotification("Критический удар");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = new Color(1f, 0.45f, 0.1f, 0.25f);
|
||||
Transform origin = attackOrigin != null ? attackOrigin : transform;
|
||||
Gizmos.DrawWireSphere(origin.position + origin.forward * 1.3f + Vector3.up * 0.8f, meleeRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d25d48e1be8c2d95b45706eb4944524
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTGIntegrated.Weapons
|
||||
{
|
||||
[CreateAssetMenu(fileName = "WeaponData", menuName = "OTG Integrated/Weapon Data")]
|
||||
public sealed class WeaponData : ScriptableObject
|
||||
{
|
||||
public string weaponId;
|
||||
public string displayName;
|
||||
public float damage = 10f;
|
||||
public float attackRate = 0.8f;
|
||||
public float range = 2.2f;
|
||||
public bool isRanged;
|
||||
public float manaCost;
|
||||
[TextArea(2, 5)] public string description;
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(weaponId))
|
||||
{
|
||||
weaponId = name;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(displayName))
|
||||
{
|
||||
displayName = weaponId;
|
||||
}
|
||||
|
||||
damage = Mathf.Max(0f, damage);
|
||||
attackRate = Mathf.Max(0.05f, attackRate);
|
||||
range = Mathf.Max(0.2f, range);
|
||||
manaCost = Mathf.Max(0f, manaCost);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c307d00ca873da936be63d5e61d7d250
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OTGIntegrated.Core;
|
||||
using OTGIntegrated.Items;
|
||||
using OTGIntegrated.Player;
|
||||
using OTGIntegrated.Stats;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace OTGIntegrated.Weapons
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class WeaponUnlock
|
||||
{
|
||||
public ItemData item;
|
||||
public WeaponData weapon;
|
||||
}
|
||||
|
||||
public sealed class WeaponManager : MonoBehaviour
|
||||
{
|
||||
public List<WeaponData> availableWeapons = new List<WeaponData>();
|
||||
public List<WeaponUnlock> itemUnlocks = new List<WeaponUnlock>();
|
||||
public WeaponData startingWeapon;
|
||||
public WeaponController weaponController;
|
||||
|
||||
private PlayerStats playerStats;
|
||||
private PlayerController playerController;
|
||||
private OTGIntegrated.Inventory.Inventory inventory;
|
||||
private WeaponData currentWeapon;
|
||||
private float nextAttackTime;
|
||||
|
||||
public WeaponData CurrentWeapon => currentWeapon;
|
||||
public float CurrentFinalDamage => currentWeapon == null || playerStats == null ? 0f : currentWeapon.damage + playerStats.Damage;
|
||||
|
||||
public void Initialize(PlayerStats stats, OTGIntegrated.Inventory.Inventory inventory)
|
||||
{
|
||||
playerStats = stats;
|
||||
this.inventory = inventory;
|
||||
playerController = GetComponent<PlayerController>();
|
||||
if (weaponController == null)
|
||||
{
|
||||
weaponController = GetComponent<WeaponController>();
|
||||
}
|
||||
|
||||
if (currentWeapon == null)
|
||||
{
|
||||
EquipWeapon(startingWeapon != null ? startingWeapon : availableWeapons.Count > 0 ? availableWeapons[0] : null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
GameEvents.OnItemAdded += HandleItemAdded;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
GameEvents.OnItemAdded -= HandleItemAdded;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetMouseButtonDown(0) && CanAcceptCombatInput())
|
||||
{
|
||||
TryAttack();
|
||||
}
|
||||
}
|
||||
|
||||
public bool EquipWeapon(WeaponData weapon)
|
||||
{
|
||||
if (weapon == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
currentWeapon = weapon;
|
||||
if (!availableWeapons.Contains(weapon))
|
||||
{
|
||||
availableWeapons.Add(weapon);
|
||||
}
|
||||
|
||||
GameEvents.RaiseWeaponChanged(currentWeapon);
|
||||
GameEvents.RaiseNotification($"Оружие: {currentWeapon.displayName}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryAttack()
|
||||
{
|
||||
if (currentWeapon == null || weaponController == null || playerStats == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Time.time < nextAttackTime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentWeapon.manaCost > 0f && !playerStats.SpendMana(currentWeapon.manaCost))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nextAttackTime = Time.time + currentWeapon.attackRate;
|
||||
bool hit = weaponController.Attack(currentWeapon, playerStats);
|
||||
if (!hit)
|
||||
{
|
||||
GameEvents.RaiseNotification("Атака");
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
public string SaveData()
|
||||
{
|
||||
return currentWeapon != null ? currentWeapon.weaponId : string.Empty;
|
||||
}
|
||||
|
||||
public void LoadData(string weaponId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(weaponId))
|
||||
{
|
||||
EquipWeapon(startingWeapon);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < availableWeapons.Count; i++)
|
||||
{
|
||||
WeaponData weapon = availableWeapons[i];
|
||||
if (weapon != null && weapon.weaponId == weaponId)
|
||||
{
|
||||
EquipWeapon(weapon);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < itemUnlocks.Count; i++)
|
||||
{
|
||||
WeaponUnlock unlock = itemUnlocks[i];
|
||||
if (unlock != null && unlock.weapon != null && unlock.weapon.weaponId == weaponId)
|
||||
{
|
||||
EquipWeapon(unlock.weapon);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleItemAdded(ItemData item, int amount)
|
||||
{
|
||||
if (item == null || amount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < itemUnlocks.Count; i++)
|
||||
{
|
||||
WeaponUnlock unlock = itemUnlocks[i];
|
||||
if (unlock != null && unlock.item == item && unlock.weapon != null)
|
||||
{
|
||||
EquipWeapon(unlock.weapon);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanAcceptCombatInput()
|
||||
{
|
||||
if (playerController == null)
|
||||
{
|
||||
playerController = GetComponent<PlayerController>();
|
||||
}
|
||||
|
||||
if (playerController != null && !playerController.InputEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GameManager.Instance != null && GameManager.Instance.uiManager != null && GameManager.Instance.uiManager.IsBlockingGameplay)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return EventSystem.current == null || !EventSystem.current.IsPointerOverGameObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f116a0ea3320b4ef29761908b1f7a3ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user