started working inventory system
This commit is contained in:
@@ -5,11 +5,20 @@ using UnityEngine.Events;
|
||||
using EasyTalk.Controller;
|
||||
using EasyTalk.Nodes;
|
||||
|
||||
public abstract class Character : MonoBehaviour
|
||||
[RequireComponent(typeof(CharacterController))]
|
||||
public abstract class Character : MonoBehaviour, IInteractable
|
||||
{
|
||||
public enum Attitude { Friendly, Neutral, Hostile }
|
||||
public Attitude attitude = Attitude.Neutral;
|
||||
[Header("Stats")]
|
||||
public int CurHp;
|
||||
public int MaxHp;
|
||||
public int Damage;
|
||||
public int level = 1;
|
||||
public float AttackRange = 2.0f;
|
||||
public float ChaseRange = 5.0f;
|
||||
public float AttackRate = 1.0f;
|
||||
public float MoveSpeed = 5.0f;
|
||||
|
||||
[Header("Components")]
|
||||
public CharacterController Controller;
|
||||
@@ -18,16 +27,19 @@ public abstract class Character : MonoBehaviour
|
||||
public Dialogue dialogue;
|
||||
protected DialogueController dialogueController;
|
||||
public event UnityAction onTakeDamage;
|
||||
public Inventory Inventory { get { return inventory; } }
|
||||
private Inventory inventory;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
dialogueController = FindFirstObjectByType<DialogueController>();
|
||||
CurHp = MaxHp;
|
||||
// Get the Inventory component attached to this same GameObject
|
||||
inventory = GetComponent<Inventory>();
|
||||
}
|
||||
public void TakeDamage(int damageToTake)
|
||||
{
|
||||
CurHp -= damageToTake;
|
||||
|
||||
onTakeDamage?.Invoke();
|
||||
|
||||
if (CurHp <= 0)
|
||||
@@ -43,4 +55,38 @@ public abstract class Character : MonoBehaviour
|
||||
{
|
||||
target = t;
|
||||
}
|
||||
public virtual string GetInteractPrompt()
|
||||
{
|
||||
switch (attitude)
|
||||
{
|
||||
case Attitude.Friendly:
|
||||
return $"Talk to {charName}";
|
||||
case Attitude.Neutral:
|
||||
return $"Talk to {charName}";
|
||||
case Attitude.Hostile:
|
||||
return $"Attack {charName}";
|
||||
default:
|
||||
return $"Interact with {charName}";
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnInteract()
|
||||
{
|
||||
switch (attitude)
|
||||
{
|
||||
case Attitude.Friendly:
|
||||
case Attitude.Neutral:
|
||||
// Start dialogue for friendly or neutral characters
|
||||
if (dialogue != null && dialogueController != null)
|
||||
{
|
||||
dialogueController.ChangeDialogue(dialogue);
|
||||
dialogueController.PlayDialogue();
|
||||
}
|
||||
break;
|
||||
case Attitude.Hostile:
|
||||
// Interacting with a hostile character could set them as the combat target
|
||||
Player.Current.SetTarget(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,16 @@ public class CharacterController : MonoBehaviour
|
||||
{
|
||||
if (moveTarget != null && Time.time - lastMoveToUpdate > moveToUpdateRate)
|
||||
{
|
||||
animator.ResetTrigger("attack");
|
||||
lastMoveToUpdate = Time.time;
|
||||
MoveToPosition(moveTarget.position);
|
||||
}
|
||||
if (animator != null)
|
||||
{
|
||||
float speed = agent.velocity.magnitude;
|
||||
float normalisedSpeed = speed / agent.speed;
|
||||
animator.SetFloat("moveSpeed", normalisedSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
public void LookTowards (Vector3 direction)
|
||||
@@ -33,7 +40,6 @@ public class CharacterController : MonoBehaviour
|
||||
|
||||
public void MoveToTarget (Transform target)
|
||||
{
|
||||
animator.SetBool("isMoving", true);
|
||||
moveTarget = target;
|
||||
}
|
||||
|
||||
@@ -47,6 +53,5 @@ public class CharacterController : MonoBehaviour
|
||||
{
|
||||
agent.isStopped = true;
|
||||
moveTarget = null;
|
||||
animator.SetBool("isMoving", false);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,26 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public interface IInteractable
|
||||
{
|
||||
string GetInteractPrompt();
|
||||
void OnInteract();
|
||||
}
|
||||
|
||||
public class ClickController : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private LayerMask layerMask;
|
||||
[SerializeField] private TextMeshProUGUI promptText;
|
||||
|
||||
void Update ()
|
||||
{
|
||||
Hover();
|
||||
if (Mouse.current.rightButton.wasPressedThisFrame)
|
||||
Click();
|
||||
|
||||
}
|
||||
|
||||
void Click ()
|
||||
@@ -22,48 +32,53 @@ public class ClickController : MonoBehaviour
|
||||
// Shoot a raycast from our mouse to what ever we are pointing at.
|
||||
if (Physics.Raycast(ray, out hit, 1000, layerMask))
|
||||
{
|
||||
int hitLayer = hit.collider.gameObject.layer;
|
||||
|
||||
// Did we hit the GROUND?
|
||||
if (hitLayer == LayerMask.NameToLayer("Ground"))
|
||||
// This single block now handles NPCs, ItemObjects, and more!
|
||||
if (hit.collider.TryGetComponent(out IInteractable interactable))
|
||||
{
|
||||
interactable.OnInteract();
|
||||
}
|
||||
else if (hit.collider.TryGetComponent(out Enemy enemy))
|
||||
{
|
||||
// Combat targeting is a different action, so it's fine to keep it separate.
|
||||
Debug.Log("Enemy Clicked");
|
||||
Player.Current.SetTarget(enemy);
|
||||
}
|
||||
else if (hit.collider.gameObject.layer == LayerMask.NameToLayer("Ground"))
|
||||
{
|
||||
Debug.Log("Ground Clicked");
|
||||
Player.Current.SetTarget(null);
|
||||
Player.Current.Controller.MoveToPosition(hit.point);
|
||||
}
|
||||
// Did we hit an ENEMY?
|
||||
else if (hitLayer == LayerMask.NameToLayer("Enemy"))
|
||||
}
|
||||
}
|
||||
void Hover ()
|
||||
{
|
||||
Vector2 mousePos = Mouse.current.position.ReadValue();
|
||||
Ray ray = Camera.main.ScreenPointToRay(mousePos);
|
||||
RaycastHit hit;
|
||||
// Shoot a raycast from our mouse to what ever we are pointing at.
|
||||
if (Physics.Raycast(ray, out hit, 1000, layerMask))
|
||||
{
|
||||
//int hitLayer = hit.collider.gameObject.layer;
|
||||
//if (hitLayer == LayerMask.NameToLayer("Interactable"))
|
||||
if(hit.collider.TryGetComponent(out IInteractable interactable))
|
||||
{
|
||||
Debug.Log("Enemy Clicked");
|
||||
Character enemy = hit.collider.GetComponent<Character>();
|
||||
Player.Current.SetTarget(enemy);
|
||||
}
|
||||
else if (hitLayer == LayerMask.NameToLayer("Interactable"))
|
||||
{
|
||||
Debug.Log("Interactable Clicked");
|
||||
if (hit.collider.TryGetComponent(out Enemy enemy))
|
||||
// Set the position of the prompt to the mouse position
|
||||
promptText.rectTransform.position = mousePos + new Vector2(15, -15);
|
||||
promptText.gameObject.SetActive(true);
|
||||
promptText.text = interactable.GetInteractPrompt();
|
||||
if (hit.collider.TryGetComponent(out Character character))
|
||||
{
|
||||
Debug.Log("Enemy Clicked");
|
||||
//Character enemy = hit.collider.GetComponent<Character>();
|
||||
Player.Current.SetTarget(enemy);
|
||||
}
|
||||
else if (hit.collider.TryGetComponent(out NPC npc))
|
||||
{
|
||||
Debug.Log("NPC Clicked");
|
||||
Player.Current.SetTarget(npc);
|
||||
npc.Interact();
|
||||
return;
|
||||
HealthBarUI.instance.UpdateInfoPanel(character.charName, character.level, character.attitude);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Something Else Clicked");
|
||||
Player.Current.SetTarget(null);
|
||||
//Debug.Log("Something Else Hovered");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Something Else Clicked");
|
||||
Player.Current.SetTarget(null);
|
||||
//Debug.Log("Something Else Hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,7 @@ public class Enemy : Character
|
||||
|
||||
private State curState = State.Idle;
|
||||
|
||||
[Header("Ranges")]
|
||||
[SerializeField] private float chaseRange;
|
||||
[SerializeField] private float attackRange;
|
||||
|
||||
[Header("Attack")]
|
||||
[SerializeField] private float attackRate;
|
||||
private float lastAttackTime;
|
||||
[SerializeField] private GameObject attackPrefab;
|
||||
|
||||
@@ -75,30 +70,30 @@ public class Enemy : Character
|
||||
// Called every frame while in the IDLE state.
|
||||
void IdleUpdate()
|
||||
{
|
||||
if (targetDistance < chaseRange && targetDistance > attackRange)
|
||||
if (targetDistance < ChaseRange && targetDistance > AttackRange)
|
||||
SetState(State.Chase);
|
||||
else if (targetDistance < attackRange)
|
||||
else if (targetDistance < AttackRange)
|
||||
SetState(State.Attack);
|
||||
}
|
||||
|
||||
// Called every frame while in the CHASE state.
|
||||
void ChaseUpdate ()
|
||||
{
|
||||
if (targetDistance > chaseRange)
|
||||
if (targetDistance > ChaseRange)
|
||||
SetState(State.Idle);
|
||||
else if (targetDistance < attackRange)
|
||||
else if (targetDistance < AttackRange)
|
||||
SetState(State.Attack);
|
||||
}
|
||||
|
||||
// Called every frame while in the ATTACK state.
|
||||
void AttackUpdate ()
|
||||
{
|
||||
if (targetDistance > attackRange)
|
||||
if (targetDistance > AttackRange)
|
||||
SetState(State.Chase);
|
||||
|
||||
Controller.LookTowards(target.transform.position - transform.position);
|
||||
|
||||
if(Time.time - lastAttackTime > attackRate)
|
||||
if(Time.time - lastAttackTime > AttackRate)
|
||||
{
|
||||
lastAttackTime = Time.time;
|
||||
AttackTarget();
|
||||
|
||||
@@ -2,11 +2,19 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
|
||||
public class HealthBarUI : MonoBehaviour
|
||||
{
|
||||
public static HealthBarUI instance;
|
||||
private void Awake() { instance = this; }
|
||||
[SerializeField] private Image healthFill;
|
||||
[SerializeField] private Character character;
|
||||
[SerializeField] private TextMeshProUGUI nameText;
|
||||
[SerializeField] private TextMeshProUGUI levelText;
|
||||
[SerializeField] private TextMeshProUGUI attitudeTxt;
|
||||
[SerializeField] private TextMeshProUGUI goldTxt;
|
||||
|
||||
|
||||
void OnEnable ()
|
||||
{
|
||||
@@ -32,4 +40,14 @@ public class HealthBarUI : MonoBehaviour
|
||||
{
|
||||
healthFill.fillAmount = (float)character.CurHp / (float)character.MaxHp;
|
||||
}
|
||||
public void UpdateInfoPanel(string name, int level, Character.Attitude attitude)
|
||||
{
|
||||
nameText.text = "Name: " + name;
|
||||
levelText.text = "Level: " + level;
|
||||
attitudeTxt.text = "Attitude " + attitude;
|
||||
}
|
||||
public void UpdateGold(int gold)
|
||||
{
|
||||
goldTxt.text = "Gold: " + gold;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f58975b416465f14fba784246f317ab5
|
||||
guid: f58975b416465f14fba784246f317ab5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
61
Assets/Scripts/Inventory.cs
Normal file
61
Assets/Scripts/Inventory.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class Inventory : MonoBehaviour
|
||||
{
|
||||
public int maxCapacity = 20;
|
||||
protected Dictionary<string, int> items = new();
|
||||
public List<InventoryEntry> startingItems; //List for inspector
|
||||
private int gold;
|
||||
public int Gold
|
||||
{
|
||||
get { return gold; }
|
||||
set { gold = Mathf.Max(0, value); }
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Initialize inventory with starting items
|
||||
foreach (var entry in startingItems)
|
||||
{
|
||||
for (int i = 0; i < entry.quantity; i++)
|
||||
{
|
||||
AddItem(entry.itemName);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 'virtual' allows this method to be overridden by subclasses like PlayerInventory
|
||||
public virtual void AddItem(string itemName)
|
||||
{
|
||||
if (items.Count >= maxCapacity && !items.ContainsKey(itemName))
|
||||
{
|
||||
Debug.Log("Inventory is full. Cannot add new item: " + itemName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (items.ContainsKey(itemName))
|
||||
{
|
||||
items[itemName]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
items[itemName] = 1;
|
||||
}
|
||||
Debug.Log($"Added {itemName} to an inventory. Total: {items[itemName]}");
|
||||
}
|
||||
|
||||
public void RemoveItem(string itemName)
|
||||
{
|
||||
items.Remove(itemName);
|
||||
}
|
||||
public int GetItemCount(string itemName)
|
||||
{
|
||||
return items.ContainsKey(itemName) ? items[itemName] : 0;
|
||||
}
|
||||
}
|
||||
[System.Serializable]
|
||||
public class InventoryEntry
|
||||
{
|
||||
public string itemName;
|
||||
public int quantity;
|
||||
}
|
||||
2
Assets/Scripts/Inventory.cs.meta
Normal file
2
Assets/Scripts/Inventory.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b32da55f42c25d499e739e39c423d4f
|
||||
@@ -6,8 +6,6 @@ public class NPC : Character
|
||||
public enum NPCType { Villager, Merchant, Guard }
|
||||
public NPCType npcType;
|
||||
|
||||
public float speed = 5f;
|
||||
public string npcName = "NPC";
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
@@ -23,21 +21,20 @@ public class NPC : Character
|
||||
{
|
||||
if (npcType == NPCType.Merchant)
|
||||
{
|
||||
Debug.Log($"Welcome to my shop, {npcName}!");
|
||||
Debug.Log($"Welcome to my shop, {charName}!");
|
||||
}
|
||||
else if (npcType == NPCType.Villager)
|
||||
{
|
||||
//Debug.Log($"Hello there! I'm {npcName}, a humble villager.");
|
||||
dialogueController.ChangeDialogue(dialogue);
|
||||
dialogueController.PlayDialogue();
|
||||
}
|
||||
else if (npcType == NPCType.Guard)
|
||||
{
|
||||
Debug.Log($"Stay out of trouble, citizen. I'm {npcName}, a guard.");
|
||||
Debug.Log($"Stay out of trouble, citizen. I'm {charName}, a guard.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Greetings from {npcName}!");
|
||||
Debug.Log($"Greetings from {charName}!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class Player : Character
|
||||
|
||||
if (Time.time - lastAttackTime > attackRate && target.GetComponent<Enemy>() != null)
|
||||
{
|
||||
//animator.SetTrigger("attack");
|
||||
animator.SetTrigger("attack");
|
||||
lastAttackTime = Time.time;
|
||||
GameObject proj = Instantiate(attackPrefab, target.transform.position + Vector3.up, Quaternion.LookRotation(target.transform.position - transform.position));
|
||||
proj.GetComponent<Projectile>().Setup(this);
|
||||
|
||||
28
Assets/Scripts/PlayerInventory.cs
Normal file
28
Assets/Scripts/PlayerInventory.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class PlayerInventory : Inventory
|
||||
{
|
||||
public static PlayerInventory instance;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// We can update the UI here because this is the Player's inventory
|
||||
HealthBarUI.instance.UpdateGold(Gold);
|
||||
}
|
||||
|
||||
// 'override' modifies the base AddItem method to add UI updates
|
||||
public override void AddItem(string itemName)
|
||||
{
|
||||
// This calls the base AddItem logic first
|
||||
base.AddItem(itemName);
|
||||
|
||||
// Then, it adds the player-specific UI update
|
||||
Debug.Log("Updating player UI for item: " + itemName);
|
||||
// You would add your specific UI update logic here, e.g., UpdateInventoryUI();
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/PlayerInventory.cs.meta
Normal file
2
Assets/Scripts/PlayerInventory.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b7212ac7e102614f86ce053ab247dcd
|
||||
49
Assets/Scripts/QuestManager.cs
Normal file
49
Assets/Scripts/QuestManager.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class QuestManager : MonoBehaviour
|
||||
{
|
||||
public static QuestManager instance;
|
||||
private void Awake() { instance = this; }
|
||||
public enum QuestState { NotStarted, InProgress, Completed }
|
||||
public QuestState currentState = QuestState.NotStarted;
|
||||
public enum QuestPriority { Main, Side }
|
||||
public QuestPriority questType;
|
||||
public enum QuestDifficulty { Easy, Medium, Hard }
|
||||
public QuestDifficulty questDifficulty;
|
||||
public enum QuestReward { Gold, Item, Experience }
|
||||
public QuestReward questReward;
|
||||
public enum QuestType { Fetch, Kill, Escort }
|
||||
public QuestType questObjectiveType;
|
||||
public string questName;
|
||||
public int questID;
|
||||
public string questDescription;
|
||||
public int experienceReward;
|
||||
public int goldReward;
|
||||
public string itemReward;
|
||||
public int objectiveAmount;
|
||||
public int currentAmount;
|
||||
public string objectiveTarget;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
public void QuestStart(int questId)
|
||||
{
|
||||
currentState = QuestState.InProgress;
|
||||
Debug.Log("Quest " + questID + " started.");
|
||||
}
|
||||
public void QuestComplete(int questId)
|
||||
{
|
||||
currentState = QuestState.Completed;
|
||||
Debug.Log("Quest " + questID + " completed! Rewards: " + experienceReward + " XP, " + goldReward + " Gold, Item: " + itemReward);
|
||||
// Here you would typically add code to give the player their rewards
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/QuestManager.cs.meta
Normal file
2
Assets/Scripts/QuestManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 821ad8b6d539d534986be2f186a56cf6
|
||||
Reference in New Issue
Block a user