diff --git a/.idea/.idea.HALLUCINATE/.idea/workspace.xml b/.idea/.idea.HALLUCINATE/.idea/workspace.xml
index 6d5c51a3..bd84ceb1 100644
--- a/.idea/.idea.HALLUCINATE/.idea/workspace.xml
+++ b/.idea/.idea.HALLUCINATE/.idea/workspace.xml
@@ -6,12 +6,12 @@
+
-
-
-
-
-
+
+
+
+
@@ -181,7 +181,7 @@
-
+
diff --git a/Assets/Prefabs/NPC/KamikazeAI.prefab b/Assets/Prefabs/NPC/KamikazeAI.prefab
index e550b4b4..357873be 100644
--- a/Assets/Prefabs/NPC/KamikazeAI.prefab
+++ b/Assets/Prefabs/NPC/KamikazeAI.prefab
@@ -14,6 +14,7 @@ GameObject:
- component: {fileID: 6087599376744356948}
- component: {fileID: 3563399533700019190}
- component: {fileID: 681314853465352057}
+ - component: {fileID: 4042483058127329834}
m_Layer: 0
m_Name: KamikazeAI
m_TagString: Untagged
@@ -155,3 +156,41 @@ MonoBehaviour:
patrolRadius: 12
patrolWaitTime: 2
explosionEffectPrefab: {fileID: 8568474719719117872, guid: 39bf32dcd9299df4ca44fd10a817eda4, type: 3}
+--- !u!114 &4042483058127329834
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6425756872251228809}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f56a83ea88140fa4f869bb2f7ffdb184, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Invector.vHealthController
+ openCloseEvents: 0
+ openCloseWindow: 1
+ selectedToolbar: 0
+ _isDead: 1
+ _currentHealth: 0
+ isImmortal: 0
+ fillHealthOnStart: 1
+ _maxHealth: 100
+ _healthRecovery: 0
+ _healthRecoveryDelay: 0
+ checkHealthEvents: []
+ _onStartReceiveDamage:
+ m_PersistentCalls:
+ m_Calls: []
+ _onReceiveDamage:
+ m_PersistentCalls:
+ m_Calls: []
+ _onDead:
+ m_PersistentCalls:
+ m_Calls: []
+ onChangeHealth:
+ m_PersistentCalls:
+ m_Calls: []
+ onResetHealth:
+ m_PersistentCalls:
+ m_Calls: []
diff --git a/Assets/Prefabs/NPC/xNPC.prefab b/Assets/Prefabs/NPC/xNPC.prefab
index 6756ea76..6419b6c4 100644
--- a/Assets/Prefabs/NPC/xNPC.prefab
+++ b/Assets/Prefabs/NPC/xNPC.prefab
@@ -472,6 +472,7 @@ GameObject:
- component: {fileID: 6469822191588635990}
- component: {fileID: 9027690817715396964}
- component: {fileID: 204793640880232070}
+ - component: {fileID: 2997468006321853334}
m_Layer: 9
m_Name: xNPC
m_TagString: Enemy
@@ -516,19 +517,20 @@ MonoBehaviour:
patrolSpeed: 2
patrolRadius: 5
playerHasArtifact: 0
+ isAggroedBySound: 0
laserPrefab: {fileID: 3965388737199864462, guid: fbec2b501d70daa4c9cb481ba53fc0b8, type: 3}
firePoint: {fileID: 5863061020199015852}
minShootDelay: 1
- maxShootDelay: 5
+ maxShootDelay: 3
dodgeForce: 8
dodgeDuration: 0.5
dodgeCooldown: 3
minStrafeDuration: 0.5
maxStrafeDuration: 2.2
- maxSpreadAngle: 6
+ maxSpreadAngle: 3
burstInterval: 0.12
- approachWeight: 0.35
- minCombatDistance: 5
+ approachWeight: 1
+ minCombatDistance: 6
npcName: Guard
persona: You are a grumpy guard protecting gold.
talkRange: 5
@@ -621,18 +623,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 35bba55c2a743d042ab1fff35e29db50, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::AnimatorAI
- debugMode: 1
- debugColor: {r: 1, g: 0, b: 0.21842146, a: 1}
- useSimulation: 0
- autoCycleSpeed: 0
- simVerticalVelocity: 0
- simIsSprinting: 0
- simIsAiming: 0
- simMoveSetID: 0
sprintThreshold: 0.8
dampTime: 0.1
- forcePlayState:
- showLayerWeights: 0
+ groundDistanceValue: 0.05
--- !u!136 &9027690817715396964
CapsuleCollider:
m_ObjectHideFlags: 0
@@ -694,6 +687,24 @@ MonoBehaviour:
onResetHealth:
m_PersistentCalls:
m_Calls: []
+--- !u!114 &2997468006321853334
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7522161431095319480}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 9d3efef3ad62cd548b0f85eb11858ed1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Invector.vHitDamageParticle
+ openCloseEvents: 0
+ openCloseWindow: 1
+ selectedToolbar: 0
+ defaultDamageEffects:
+ - {fileID: 120346, guid: b8f2fe1ee1a01724cb72f5b0e6bd2eef, type: 3}
+ customDamageEffects: []
--- !u!1001 &7561534673732472622
PrefabInstance:
m_ObjectHideFlags: 0
diff --git a/Assets/Scripts/AI NPC/EnemyAI.cs b/Assets/Scripts/AI NPC/EnemyAI.cs
index 194e95ce..d6e976dc 100644
--- a/Assets/Scripts/AI NPC/EnemyAI.cs
+++ b/Assets/Scripts/AI NPC/EnemyAI.cs
@@ -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();
fov = GetComponent();
chatBubble = GetComponentInChildren(true);
+ mainCollider = GetComponent();
+
+ // 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 { 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 { new TaskNode(CheckCombatConditions), new TaskNode(ActionFocusAndShoot) });
-
+
var chaseSequence = new Sequence(new List { new TaskNode(CheckCanSeePlayer), new TaskNode(ActionChasePlayer) });
var investigateSequence = new Sequence(new List { new TaskNode(CheckHasInvestigateTarget), new TaskNode(ActionInvestigate) });
var talkSequence = new Sequence(new List { 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();
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($"[AI {npcName}] 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($"[AI {npcName}] GUNFIRE DETECTED! 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(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;
diff --git a/Assets/Scripts/Player/Weapon/vShooterWeaponBase.cs b/Assets/Scripts/Player/Weapon/vShooterWeaponBase.cs
index e77a41e3..aa798263 100644
--- a/Assets/Scripts/Player/Weapon/vShooterWeaponBase.cs
+++ b/Assets/Scripts/Player/Weapon/vShooterWeaponBase.cs
@@ -302,6 +302,15 @@ namespace Invector.vShooter
var rotation = Quaternion.LookRotation(dir);
GameObject bulletObject = null;
var velocityChanged = 0f;
+
+ if (projectile == null)
+ {
+ Debug.LogError($"WEAPON ERROR: No Projectile Prefab assigned to {gameObject.name}!");
+ return;
+ }
+
+ Debug.Log($"WEAPON SHOOT: 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();
+ if (pCtrl == null)
+ {
+ Debug.LogError($"PROJECTILE ERROR: {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();
+
+ if (pCtrl == null)
+ {
+ Debug.LogError($"PROJECTILE ERROR: {projectile.name} does not have vProjectileControl script!");
+ return;
+ }
+
if (pCtrl.debugTrajetory)
{
startPoint.DebugPoint(Color.red, 10, 0.1f);