2026-05-30 17:41:31 +07:00
using System ;
using System.Collections ;
using System.Text ;
using UnityEngine ;
using UnityEngine.InputSystem ;
using UnityEngine.Networking ;
2026-06-04 23:01:39 +07:00
using Hallucinate.Audio ;
2026-05-30 17:41:31 +07:00
[Serializable]
public class Part
{
public string text ;
}
[Serializable]
public class Content
{
public Part [ ] parts ;
}
[Serializable]
public class Candidate
{
public Content content ;
}
[Serializable]
public class GeminiResponse
{
public Candidate [ ] candidates ;
}
2026-06-02 10:00:42 +07:00
public class GerminiNPC : MonoBehaviour
2026-05-30 17:41:31 +07:00
{
[SerializeField]
private string apiKey = "AQ.Ab8RN6I2hU_p8yHiPNNHtWzYBiLugbPP22gC6lzTWaYEWj4v0g" ;
[SerializeField]
private string germiniURL =
"https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-latest:generateContent" ;
public string npcPersona =
2026-06-02 10:00:42 +07:00
"Ngươi là một lão thợ rèn cọc cằn tên là Tom, ngươi rất ghét những kẻ mang phế liệu đến tiệm của mình. Chỉ trả lời ngắn gọn trong 2 câu, theo phong cách trung cổ." ;
2026-05-30 17:41:31 +07:00
public string playerHeldItem = "Thanh kiếm rỉ sét" ;
2026-06-02 10:00:42 +07:00
public float interactionDistance = 5f ; // Khoảng cách tối đa để nói chuyện
public Transform playerTransform ; // Gán transform của Player vào đây
2026-05-30 17:41:31 +07:00
2026-06-04 23:01:39 +07:00
[Header("Audio")]
public string startTalkSound = "NPC_Interact" ;
public string responseSound = "NPC_Response" ;
2026-05-30 17:41:31 +07:00
private void Update ( )
{
2026-06-02 10:00:42 +07:00
if ( Keyboard . current ! = null & & Keyboard . current . fKey . wasPressedThisFrame )
{
if ( CanSeePlayer ( ) )
{
2026-06-04 23:01:39 +07:00
AudioManager . Instance ? . Play ( startTalkSound , position : transform . position ) ;
2026-06-02 10:00:42 +07:00
StartCoroutine ( GetGerminiReponse ( ) ) ;
}
else
{
Debug . Log ( "<color=yellow>Hệ thống:</color> Bạn ở quá xa hoặc bị tường che khuất!" ) ;
}
}
}
private bool CanSeePlayer ( )
{
if ( playerTransform = = null )
{
// Tự tìm player nếu chưa gán
GameObject player = GameObject . FindGameObjectWithTag ( "Player" ) ;
if ( player ! = null ) playerTransform = player . transform ;
else return false ;
}
// 1. Check khoảng cách
float dist = Vector3 . Distance ( transform . position , playerTransform . position ) ;
if ( dist > interactionDistance ) return false ;
// 2. Check xem có bị tường che không (Raycast)
Vector3 direction = ( playerTransform . position + Vector3 . up ) - ( transform . position + Vector3 . up ) ;
RaycastHit hit ;
if ( Physics . Raycast ( transform . position + Vector3 . up , direction , out hit , interactionDistance ) )
2026-05-30 17:41:31 +07:00
{
2026-06-02 10:00:42 +07:00
if ( hit . collider . CompareTag ( "Player" ) | | hit . collider . transform . IsChildOf ( playerTransform ) )
{
return true ; // Thấy đầu/người player
}
2026-05-30 17:41:31 +07:00
}
2026-06-02 10:00:42 +07:00
return false ;
2026-05-30 17:41:31 +07:00
}
private IEnumerator GetGerminiReponse ( )
{
2026-06-02 10:00:42 +07:00
var jsonBody = $ @ "{{
"" systemInstruction "" : { { "" parts "" : [ { { "" text "" : "" { npcPersona } "" } } ] } } ,
"" contents "" : [ { { "" parts "" : [ { { "" text "" : "" Ta muốn bán cho ô ng món đ ồ này : { playerHeldItem } "" } } ] } } ]
} } ";
// 1. Sửa tham số thành ?key= (trước đó là ?ket=)
var requestURL = $"{germiniURL}?key={apiKey}" ;
// 2. Sử dụng requestURL (có chứa key) thay vì germiniURL gốc
using ( var request = new UnityWebRequest ( requestURL , "POST" ) )
2026-05-30 17:41:31 +07:00
{
var bodyRaw = Encoding . UTF8 . GetBytes ( jsonBody ) ;
request . uploadHandler = new UploadHandlerRaw ( bodyRaw ) ;
request . downloadHandler = new DownloadHandlerBuffer ( ) ;
request . SetRequestHeader ( "Content-Type" , "application/json" ) ;
2026-06-02 10:00:42 +07:00
2026-05-30 17:41:31 +07:00
yield return request . SendWebRequest ( ) ;
2026-06-02 10:00:42 +07:00
if ( request . result = = UnityWebRequest . Result . ProtocolError | | request . result = = UnityWebRequest . Result . ConnectionError )
2026-05-30 17:41:31 +07:00
{
2026-06-02 10:00:42 +07:00
Debug . LogError ( $"[Gemini Error] {request.error} - Response: {request.downloadHandler.text}" ) ;
2026-05-30 17:41:31 +07:00
}
else
{
var responseTEXT = request . downloadHandler . text ;
2026-06-02 10:00:42 +07:00
try
{
var geminiResponse = JsonUtility . FromJson < GeminiResponse > ( responseTEXT ) ;
if ( geminiResponse ! = null & & geminiResponse . candidates ! = null & & geminiResponse . candidates . Length > 0 )
{
var npcResponse = geminiResponse . candidates [ 0 ] . content . parts [ 0 ] . text ;
Debug . Log ( $"<color=green>Tom:</color> {npcResponse}" ) ;
2026-06-04 23:01:39 +07:00
AudioManager . Instance ? . Play ( responseSound , position : transform . position ) ;
2026-06-02 10:00:42 +07:00
}
}
catch ( Exception e )
2026-05-30 17:41:31 +07:00
{
2026-06-02 10:00:42 +07:00
Debug . LogError ( $"[JSON Parse Error] {e.Message}" ) ;
2026-05-30 17:41:31 +07:00
}
}
}
}
}