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 @@ + - - - - - + + + + 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);