187 lines
6.1 KiB
C#
187 lines
6.1 KiB
C#
|
|
using System.Collections.Generic;
|
||
|
|
using UnityEngine;
|
||
|
|
|
||
|
|
namespace MedievalShopkeeper
|
||
|
|
{
|
||
|
|
public class Shelf : Interactable
|
||
|
|
{
|
||
|
|
[Header("Shelf Settings")]
|
||
|
|
[Tooltip("The maximum amount of items this shelf can hold.")]
|
||
|
|
[SerializeField] private int maxCapacity = 10;
|
||
|
|
|
||
|
|
[Tooltip("Transforms where visual prefabs should be spawned. The array size visually dictates capacity layout (can be fewer than maxCapacity, just overlaps/stacks visually).")]
|
||
|
|
[SerializeField] private Transform[] displaySpots;
|
||
|
|
|
||
|
|
[Header("Current Status (Read-Only)")]
|
||
|
|
[SerializeField] private ItemData assignedItem;
|
||
|
|
[SerializeField] private int currentStock = 0;
|
||
|
|
|
||
|
|
private List<GameObject> visualPrefabs = new List<GameObject>();
|
||
|
|
|
||
|
|
public ItemData AssignedItem => assignedItem;
|
||
|
|
public int CurrentStock => currentStock;
|
||
|
|
|
||
|
|
public override string InteractionPrompt
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
if (assignedItem != null)
|
||
|
|
{
|
||
|
|
return $"Restock {assignedItem.itemName} ({currentStock}/{maxCapacity})";
|
||
|
|
}
|
||
|
|
return "Stock Shelf (Empty)";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public override void Interact(PlayerController player)
|
||
|
|
{
|
||
|
|
base.Interact(player);
|
||
|
|
|
||
|
|
if (UIManager.Instance != null)
|
||
|
|
{
|
||
|
|
UIManager.Instance.OpenShelfMenu(this, player);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// Fallback to auto-stocking if no UI manager exists
|
||
|
|
StockFromPlayer(player);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void StockFromPlayer(PlayerController player)
|
||
|
|
{
|
||
|
|
if (!player.IsCarryingItem)
|
||
|
|
{
|
||
|
|
Debug.Log("[Shelf] Player is not carrying any stock box.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
GameObject carriedObj = player.CurrentCarriedItem;
|
||
|
|
StockBox stockBox = carriedObj.GetComponent<StockBox>();
|
||
|
|
|
||
|
|
if (stockBox == null)
|
||
|
|
{
|
||
|
|
Debug.LogWarning("[Shelf] Carried item is not a StockBox!");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Dynamically assign item if empty
|
||
|
|
if (currentStock == 0)
|
||
|
|
{
|
||
|
|
assignedItem = stockBox.itemData;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if items match
|
||
|
|
if (assignedItem != stockBox.itemData)
|
||
|
|
{
|
||
|
|
Debug.Log($"[Shelf] Mismatch! Shelf holds {assignedItem.itemName}, but box contains {stockBox.itemData.itemName}.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Calculate how much to transfer
|
||
|
|
int spaceAvailable = maxCapacity - currentStock;
|
||
|
|
if (spaceAvailable <= 0)
|
||
|
|
{
|
||
|
|
Debug.Log("[Shelf] Shelf is already full!");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
int transferAmount = Mathf.Min(spaceAvailable, stockBox.quantity);
|
||
|
|
|
||
|
|
// Transfer
|
||
|
|
currentStock += transferAmount;
|
||
|
|
stockBox.RemoveQuantity(transferAmount);
|
||
|
|
|
||
|
|
Debug.Log($"[Shelf] Restocked {transferAmount}x {assignedItem.itemName}. Shelf is now at {currentStock}/{maxCapacity}.");
|
||
|
|
|
||
|
|
UpdateVisuals();
|
||
|
|
|
||
|
|
// If box is empty, drop and destroy it
|
||
|
|
if (stockBox.quantity <= 0)
|
||
|
|
{
|
||
|
|
GameObject dropped = player.DropCarriedItem();
|
||
|
|
if (dropped != null) Destroy(dropped);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void EmptyShelf()
|
||
|
|
{
|
||
|
|
if (currentStock <= 0 || assignedItem == null)
|
||
|
|
{
|
||
|
|
Debug.Log("[Shelf] Shelf is already empty.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Return items to virtual Shop Inventory
|
||
|
|
if (ShopInventory.Instance != null)
|
||
|
|
{
|
||
|
|
ShopInventory.Instance.AddStock(assignedItem, currentStock);
|
||
|
|
Debug.Log($"[Shelf] Emptied {currentStock}x {assignedItem.itemName} back to Shop Inventory.");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
Debug.LogWarning("[Shelf] No ShopInventory found to return items to! Items lost.");
|
||
|
|
}
|
||
|
|
|
||
|
|
currentStock = 0;
|
||
|
|
assignedItem = null;
|
||
|
|
UpdateVisuals();
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool HasStock => currentStock > 0 && assignedItem != null;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Called by NPCs when they want to take an item to put in their basket.
|
||
|
|
/// </summary>
|
||
|
|
public bool TryTakeItem(out ItemData purchasedItem)
|
||
|
|
{
|
||
|
|
purchasedItem = null;
|
||
|
|
if (currentStock > 0 && assignedItem != null)
|
||
|
|
{
|
||
|
|
purchasedItem = assignedItem;
|
||
|
|
currentStock--;
|
||
|
|
|
||
|
|
if (currentStock == 0)
|
||
|
|
{
|
||
|
|
assignedItem = null; // Free up the shelf for a different item if desired
|
||
|
|
}
|
||
|
|
|
||
|
|
UpdateVisuals();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void UpdateVisuals()
|
||
|
|
{
|
||
|
|
// Clear old visuals
|
||
|
|
foreach (var vis in visualPrefabs)
|
||
|
|
{
|
||
|
|
if (vis != null) Destroy(vis);
|
||
|
|
}
|
||
|
|
visualPrefabs.Clear();
|
||
|
|
|
||
|
|
if (assignedItem == null || assignedItem.displayPrefab == null || displaySpots == null || displaySpots.Length == 0) return;
|
||
|
|
|
||
|
|
// Spawn new visuals based on stock
|
||
|
|
// We map the stock linearly across available display spots.
|
||
|
|
int visualsToSpawn = Mathf.Min(currentStock, displaySpots.Length);
|
||
|
|
|
||
|
|
for (int i = 0; i < visualsToSpawn; i++)
|
||
|
|
{
|
||
|
|
GameObject vis = Instantiate(assignedItem.displayPrefab, displaySpots[i].position, displaySpots[i].rotation, displaySpots[i]);
|
||
|
|
vis.transform.localScale = assignedItem.displayScale;
|
||
|
|
|
||
|
|
// Destroy any colliders on the visual so it doesn't block rays
|
||
|
|
foreach(var col in vis.GetComponentsInChildren<Collider>())
|
||
|
|
{
|
||
|
|
Destroy(col);
|
||
|
|
}
|
||
|
|
|
||
|
|
visualPrefabs.Add(vis);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|