diff --git a/Assets/Scripts/DoorTeleporter.cs b/Assets/Scripts/DoorTeleporter.cs new file mode 100644 index 0000000..2a56cbc --- /dev/null +++ b/Assets/Scripts/DoorTeleporter.cs @@ -0,0 +1,47 @@ +using UnityEngine; + +[RequireComponent(typeof(Collider2D))] +public class DoorTeleporter : MonoBehaviour +{ + [SerializeField] private Transform destinationPoint; + [SerializeField] private KeyCode interactKey = KeyCode.E; + + private bool playerInside; + private Transform player; + + private void Reset() + { + var col = GetComponent(); + col.isTrigger = true; + } + + private void Update() + { + if (playerInside && destinationPoint != null && Input.GetKeyDown(interactKey)) + { + player.position = destinationPoint.position; + } + } + + private void OnTriggerEnter2D(Collider2D other) + { + if (!other.CompareTag("Player")) + { + return; + } + + playerInside = true; + player = other.transform; + } + + private void OnTriggerExit2D(Collider2D other) + { + if (!other.CompareTag("Player")) + { + return; + } + + playerInside = false; + player = null; + } +} diff --git a/Assets/Scripts/Inventory2D.cs b/Assets/Scripts/Inventory2D.cs new file mode 100644 index 0000000..96f93b5 --- /dev/null +++ b/Assets/Scripts/Inventory2D.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using UnityEngine; + +public class Inventory2D : MonoBehaviour +{ + [SerializeField] private int maxItems = 10; + + private readonly List items = new List(); + + public bool AddItem(ItemType item) + { + if (item == ItemType.None || items.Count >= maxItems) + { + return false; + } + + items.Add(item); + return true; + } + + public bool HasItem(ItemType item) + { + return items.Contains(item); + } + + public bool RemoveItem(ItemType item) + { + return items.Remove(item); + } + + public IReadOnlyList Items + { + get { return items; } + } +} diff --git a/Assets/Scripts/ItemPickup.cs b/Assets/Scripts/ItemPickup.cs new file mode 100644 index 0000000..7248dc6 --- /dev/null +++ b/Assets/Scripts/ItemPickup.cs @@ -0,0 +1,32 @@ +using UnityEngine; + +[RequireComponent(typeof(Collider2D))] +public class ItemPickup : MonoBehaviour +{ + [SerializeField] private ItemType itemType; + + public void Configure(ItemType type) + { + itemType = type; + } + + private void Reset() + { + var col = GetComponent(); + col.isTrigger = true; + } + + private void OnTriggerEnter2D(Collider2D other) + { + var inventory = other.GetComponent(); + if (inventory == null) + { + return; + } + + if (inventory.AddItem(itemType)) + { + Destroy(gameObject); + } + } +} diff --git a/Assets/Scripts/ItemType.cs b/Assets/Scripts/ItemType.cs new file mode 100644 index 0000000..77d44a9 --- /dev/null +++ b/Assets/Scripts/ItemType.cs @@ -0,0 +1,10 @@ +public enum ItemType +{ + None = 0, + Key, + Book, + Candle, + Potion, + Gem, + Scroll +} diff --git a/Assets/Scripts/MansionQuestManager.cs b/Assets/Scripts/MansionQuestManager.cs new file mode 100644 index 0000000..129e466 --- /dev/null +++ b/Assets/Scripts/MansionQuestManager.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Events; + +public class MansionQuestManager : MonoBehaviour +{ + [SerializeField] private List npcs = new List(); + [SerializeField] private List possibleRequests = new List(); + [SerializeField] private bool uniqueRequests = true; + [SerializeField] private UnityEvent onAllNPCsSatisfied; + + private void Start() + { + AssignRequests(); + } + + public void AssignRequests() + { + if (npcs.Count == 0 || possibleRequests.Count == 0) + { + return; + } + + List pool = new List(possibleRequests); + + for (int i = 0; i < npcs.Count; i++) + { + if (npcs[i] == null) + { + continue; + } + + if (pool.Count == 0) + { + pool = new List(possibleRequests); + } + + int index = Random.Range(0, pool.Count); + ItemType selected = pool[index]; + npcs[i].SetRequest(selected); + + if (uniqueRequests) + { + pool.RemoveAt(index); + } + } + } + + public void CheckQuestState() + { + for (int i = 0; i < npcs.Count; i++) + { + if (npcs[i] != null && !npcs[i].IsSatisfied) + { + return; + } + } + + onAllNPCsSatisfied?.Invoke(); + } +} diff --git a/Assets/Scripts/NPCRequester.cs b/Assets/Scripts/NPCRequester.cs new file mode 100644 index 0000000..62913d4 --- /dev/null +++ b/Assets/Scripts/NPCRequester.cs @@ -0,0 +1,64 @@ +using UnityEngine; +using UnityEngine.Events; + +[RequireComponent(typeof(Collider2D))] +public class NPCRequester : MonoBehaviour +{ + [Header("Pedido")] + [SerializeField] private ItemType requestedItem = ItemType.None; + [SerializeField] private bool consumeItemOnDelivery = true; + [SerializeField] private MansionQuestManager questManager; + + [Header("Eventos")] + [SerializeField] private UnityEvent onCorrectItemDelivered; + [SerializeField] private UnityEvent onWrongItemOrMissing; + + public ItemType RequestedItem => requestedItem; + public bool IsSatisfied { get; private set; } + + private void Reset() + { + var col = GetComponent(); + col.isTrigger = true; + } + + public void SetRequest(ItemType item) + { + requestedItem = item; + IsSatisfied = false; + } + + private void OnTriggerStay2D(Collider2D other) + { + if (IsSatisfied) + { + return; + } + + if (!other.CompareTag("Player") || !Input.GetKeyDown(KeyCode.E)) + { + return; + } + + var inventory = other.GetComponent(); + if (inventory == null) + { + return; + } + + if (requestedItem != ItemType.None && inventory.HasItem(requestedItem)) + { + if (consumeItemOnDelivery) + { + inventory.RemoveItem(requestedItem); + } + + IsSatisfied = true; + onCorrectItemDelivered?.Invoke(); + questManager?.CheckQuestState(); + return; + } + + onWrongItemOrMissing?.Invoke(); + } +} diff --git a/Assets/Scripts/RandomItemSpawner.cs b/Assets/Scripts/RandomItemSpawner.cs new file mode 100644 index 0000000..5c93e97 --- /dev/null +++ b/Assets/Scripts/RandomItemSpawner.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using UnityEngine; + +public class RandomItemSpawner : MonoBehaviour +{ + [System.Serializable] + public struct SpawnEntry + { + public ItemType itemType; + public GameObject prefab; + } + + [SerializeField] private List itemPrefabs = new List(); + [SerializeField] private List spawnPoints = new List(); + [SerializeField] private int spawnCount = 8; + + private readonly List spawnedItems = new List(); + + private void Start() + { + SpawnItems(); + } + + public void SpawnItems() + { + ClearItems(); + + if (itemPrefabs.Count == 0 || spawnPoints.Count == 0) + { + return; + } + + int max = Mathf.Min(spawnCount, spawnPoints.Count); + List shuffledPoints = new List(spawnPoints); + Shuffle(shuffledPoints); + + for (int i = 0; i < max; i++) + { + SpawnEntry entry = itemPrefabs[Random.Range(0, itemPrefabs.Count)]; + if (entry.prefab == null) + { + continue; + } + + GameObject instance = Instantiate(entry.prefab, shuffledPoints[i].position, Quaternion.identity); + ItemPickup pickup = instance.GetComponent(); + if (pickup == null) + { + pickup = instance.AddComponent(); + } + + pickup.Configure(entry.itemType); + spawnedItems.Add(instance); + } + } + + public void ClearItems() + { + for (int i = 0; i < spawnedItems.Count; i++) + { + if (spawnedItems[i] != null) + { + Destroy(spawnedItems[i]); + } + } + + spawnedItems.Clear(); + } + + private static void Shuffle(List list) + { + for (int i = 0; i < list.Count; i++) + { + int randomIndex = Random.Range(i, list.Count); + T temp = list[i]; + list[i] = list[randomIndex]; + list[randomIndex] = temp; + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..94240d2 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# Sistema de missão para mansão 2D (Unity) + +Este repositório inclui scripts base para o cenário pedido: + +- 3 NPCs pedindo itens específicos. +- NPC aceita **somente** o item correto. +- Itens com spawn aleatório nas salas da mansão. +- Portas para transitar entre salas. + +## Scripts + +- `ItemType.cs`: enum com os tipos de item. +- `Inventory2D.cs`: inventário simples do jogador. +- `ItemPickup.cs`: coleta itens ao encostar no jogador. +- `NPCRequester.cs`: valida entrega de item no NPC ao pressionar `E`. +- `RandomItemSpawner.cs`: sorteia e instancia itens em pontos de spawn. +- `DoorTeleporter.cs`: teleporta jogador para outra sala ao interagir com porta. +- `MansionQuestManager.cs`: distribui pedidos aos NPCs e detecta conclusão. + +## Configuração rápida + +1. Adicione `Inventory2D` no objeto Player (com tag `Player`). +2. Crie prefabs de item com `Collider2D` trigger + `ItemPickup`. +3. Em cada NPC, adicione `Collider2D` trigger + `NPCRequester`. +4. Crie um objeto `GameManager` com `MansionQuestManager` e referencie os 3 NPCs. +5. Configure `possibleRequests` com os tipos de item permitidos. +6. Crie um objeto `RandomItemSpawner`, informe `spawnPoints` e os prefabs dos itens. +7. Em cada porta, adicione `DoorTeleporter` e informe o `destinationPoint` da próxima sala. + +## Fluxo de jogo + +- Jogador explora salas da mansão. +- Coleta itens sorteados nos pontos de spawn. +- Entra em portas para acessar novas salas. +- Entrega cada item no NPC correto usando `E`. +- Quando os 3 NPCs estiverem satisfeitos, `onAllNPCsSatisfied` é disparado.