This commit is contained in:
2026-06-06 00:07:42 +07:00
parent 948a338191
commit 6b4e3f1271
7 changed files with 9631 additions and 9483 deletions

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
@@ -20,6 +20,7 @@ public class EnemyAI : MonoBehaviour
private NavMeshAgent agent;
private Rigidbody rb;
private FieldOfView fov;
private Collider mainCollider;
[Header("Movement Settings")]
public float moveSpeed = 3f;
@@ -53,7 +54,7 @@ public class EnemyAI : MonoBehaviour
public float maxStrafeDuration = 2.2f;
public float maxSpreadAngle = 6f;
public float burstInterval = 0.12f;
public float approachWeight = 0.35f;
public float minCombatDistance = 5.0f;
@@ -79,7 +80,7 @@ public class EnemyAI : MonoBehaviour
public float investigationThreshold = 30f;
public float alertNeighborsThreshold = 70f;
public float alertRange = 20f;
public Node rootNode;
void Start()
@@ -88,6 +89,19 @@ public class EnemyAI : MonoBehaviour
rb = GetComponent<Rigidbody>();
fov = GetComponent<FieldOfView>();
chatBubble = GetComponentInChildren<Hallucinate.UI.ChatBubble>(true);
mainCollider = GetComponent<Collider>();
// Tự động gán Layer Enemy nếu chưa có
if (gameObject.layer == LayerMask.NameToLayer("Default"))
{
gameObject.layer = LayerMask.NameToLayer("Enemy");
Debug.Log($"[AI {npcName}] Tự động chuyển Layer sang Enemy để súng có thể bắn trúng.");
}
if (mainCollider == null)
{
Debug.LogError($"[AI {npcName}] THIẾU COLLIDER! NPC này sẽ không thể bị bắn trúng.");
}
rb.isKinematic = true;
rb.freezeRotation = true;
@@ -105,10 +119,10 @@ public class EnemyAI : MonoBehaviour
void InitTree()
{
var dodgeSequence = new Sequence(new List<Node> { new TaskNode(CheckDodgeConditions), new TaskNode(ActionDodge) });
// Đổi hàm CheckHasArtifact thành CheckCombatConditions để dùng chung cho cả âm thanh
var laserSequence = new Sequence(new List<Node> { new TaskNode(CheckCombatConditions), new TaskNode(ActionFocusAndShoot) });
var chaseSequence = new Sequence(new List<Node> { new TaskNode(CheckCanSeePlayer), new TaskNode(ActionChasePlayer) });
var investigateSequence = new Sequence(new List<Node> { new TaskNode(CheckHasInvestigateTarget), new TaskNode(ActionInvestigate) });
var talkSequence = new Sequence(new List<Node> { new TaskNode(CheckCanTalkToNPC), new TaskNode(ActionTalk) });
@@ -129,6 +143,13 @@ public class EnemyAI : MonoBehaviour
{
if (player == null) return;
// Đảm bảo Collider luôn bật
if (mainCollider != null && !mainCollider.enabled)
{
mainCollider.enabled = true;
Debug.LogWarning($"[AI {npcName}] Collider bị tắt trái phép! Đã tự động bật lại.");
}
if (!agent.isOnNavMesh) return;
suspicionLevel = Mathf.Max(0, suspicionLevel - Time.deltaTime * 0.5f);
@@ -202,7 +223,7 @@ public class EnemyAI : MonoBehaviour
foreach (var hit in hitColliders)
{
if (hit.gameObject == gameObject) continue;
EnemyAI other = hit.GetComponentInParent<EnemyAI>();
if (other != null && !other.isTalking && Time.time >= other.lastTalkTime + talkCooldown)
{
@@ -226,8 +247,8 @@ public class EnemyAI : MonoBehaviour
{
suspicionLevel += volume * 15f;
if (fov != null) fov.lastKnownPlayerPosition = location;
// CẬP NHẬT: Nếu AI nghe thấy tiếng động làm mức nghi ngờ vượt mốc điều tra, chuyển sang trạng thái chiến đấu (bắn bồi)
// Nếu AI nghe thấy tiếng động làm mức nghi ngờ vượt mốc điều tra, chuyển sang trạng thái chiến đấu
if (suspicionLevel >= investigationThreshold)
{
isAggroedBySound = true;
@@ -238,6 +259,16 @@ public class EnemyAI : MonoBehaviour
Debug.Log($"<color=orange>[AI {npcName}]</color> Heard noise! Suspicion: {suspicionLevel}");
}
public void TriggerCombatAlert(Vector3 sourceLocation)
{
suspicionLevel = 100f;
isAggroedBySound = true; // Ép vào trạng thái tấn công
if (fov != null) fov.lastKnownPlayerPosition = sourceLocation;
StopConversation();
AlertNeighbors();
Debug.Log($"<color=red>[AI {npcName}] GUNFIRE DETECTED!</color> Entering combat mode.");
}
public void AlertNeighbors()
{
Collider[] hitColliders = Physics.OverlapSphere(transform.position, alertRange);
@@ -277,7 +308,7 @@ public class EnemyAI : MonoBehaviour
{
DialogueResult result = JsonUtility.FromJson<DialogueResult>(json);
if (chatBubble != null) chatBubble.Show(result.text);
moveSpeed += result.speedMod;
suspicionLevel = Mathf.Clamp(suspicionLevel + result.suspicionMod, 0, 100);
lastTalkTime = Time.time;
@@ -334,7 +365,7 @@ public class EnemyAI : MonoBehaviour
agent.isStopped = false;
agent.speed = moveSpeed * 0.7f;
agent.SetDestination(fov.lastKnownPlayerPosition);
if (Vector3.Distance(transform.position, fov.lastKnownPlayerPosition) < 1.5f)
{
currentWaitTime += Time.deltaTime;
@@ -357,12 +388,12 @@ public class EnemyAI : MonoBehaviour
// CẢI TIẾN: Xác định mục tiêu linh hoạt để tránh Wallhack.
Vector3 targetPos = player.position; // Mặc định khóa chặt người chơi
// Nếu không cầm Artifact và cũng chưa bị nhìn thấy, AI chỉ nhắm vào MỐC ÂM THANH
if (!playerHasArtifact && fov != null && !fov.canSeePlayer && fov.lastKnownPlayerPosition != Vector3.zero)
{
targetPos = fov.lastKnownPlayerPosition;
// Nếu AI tiến hành áp sát và xả đạn vào nơi phát ra tiếng mà không thấy ai, ngưng bắn
if (Vector3.Distance(transform.position, targetPos) < 2f)
{
@@ -472,10 +503,16 @@ public class EnemyAI : MonoBehaviour
isDodging = true;
agent.enabled = false;
rb.isKinematic = false;
// GIỮ COLLIDER LUÔN BẬT KHI NÉ
if (mainCollider != null) mainCollider.enabled = true;
Vector3 dir = (player.position - transform.position).normalized;
Vector3 perp = new Vector3(-dir.z, 0, dir.x);
rb.AddForce((Random.value > 0.5f ? perp : -perp) * dodgeForce, ForceMode.Impulse);
yield return new WaitForSeconds(dodgeDuration);
rb.linearVelocity = Vector3.zero;
rb.isKinematic = true;
agent.enabled = true;

View File

@@ -302,6 +302,15 @@ namespace Invector.vShooter
var rotation = Quaternion.LookRotation(dir);
GameObject bulletObject = null;
var velocityChanged = 0f;
if (projectile == null)
{
Debug.LogError($"<color=red>WEAPON ERROR:</color> No Projectile Prefab assigned to {gameObject.name}!");
return;
}
Debug.Log($"<color=white>WEAPON SHOOT:</color> Spawning projectile. HitLayer: {hitLayer.value}");
if (dispersion > 0 && projectile)
{
for (int i = 0; i < projectilesPerShot; i++)
@@ -311,6 +320,12 @@ namespace Invector.vShooter
bulletObject = Instantiate(projectile, startPoint, spreadRotation);
var pCtrl = bulletObject.GetComponent<vProjectileControl>();
if (pCtrl == null)
{
Debug.LogError($"<color=red>PROJECTILE ERROR:</color> {projectile.name} does not have vProjectileControl script!");
continue;
}
if (pCtrl.debugTrajetory && i == 0)
{
startPoint.DebugPoint(Color.red, 10, 0.1f);
@@ -338,6 +353,13 @@ namespace Invector.vShooter
{
bulletObject = Instantiate(projectile, startPoint, rotation);
var pCtrl = bulletObject.GetComponent<vProjectileControl>();
if (pCtrl == null)
{
Debug.LogError($"<color=red>PROJECTILE ERROR:</color> {projectile.name} does not have vProjectileControl script!");
return;
}
if (pCtrl.debugTrajetory)
{
startPoint.DebugPoint(Color.red, 10, 0.1f);