Add Shop system with ShopManager, ShopUI, and ShopSlotUI; implement item purchasing and shop UI functionality

This commit is contained in:
2025-07-12 02:07:43 +02:00
parent 8f3a913b7e
commit ad02e07a87
15 changed files with 753 additions and 3 deletions

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using Inventory;
using Sirenix.Serialization;
using Systems;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Shop
{
public class ShopManager : MonoBehaviour
{
private List<StatModifierItem> currentItemChoices = new();
private List<WeaponItem> currentWeaponChoices = new();
[SerializeField] private ShopUI shopUI;
[SerializeField] private InventoryManager inventoryManager;
[SerializeField] private int itemsPerShop = 4;
[OdinSerialize, SerializeField] private List<StatModifierItem> possibleItems = new();
[OdinSerialize, SerializeField] private List<WeaponItem> possibleWeapons = new();
private void OnEnable()
{
// GameManager.Instance.OnRoundEnd += OpenShop;
GameManager.Instance.OnStoreOpen += OpenShop;
}
private void OnDisable()
{
// GameManager.Instance.OnRoundEnd -= OpenShop;
GameManager.Instance.OnStoreOpen -= OpenShop;
}
public void CloseShop()
{
shopUI.Hide();
Time.timeScale = 1f;
}
public void BuyItem(StatModifierItem item, int price)
{
if (GameManager.Instance.Coins < price) return;
GameManager.Instance.SpendCoins(price);
inventoryManager.EquipItem(item);
shopUI.MarkAsPurchased(item);
}
public void BuyWeapon(WeaponItem weapon, int price)
{
if (GameManager.Instance.Coins < price) return;
GameManager.Instance.SpendCoins(price);
inventoryManager.EquipWeapon(weapon);
shopUI.MarkAsPurchased(weapon);
}
public void RerollShop()
{
currentItemChoices = DrawRandomItems(possibleItems, itemsPerShop);
currentWeaponChoices = DrawRandomItems(possibleWeapons, itemsPerShop);
shopUI.Show(currentItemChoices, currentWeaponChoices, this);
}
private void OpenShop()
{
OpenShop(GameManager.Instance.CurrentRound);
}
private void OpenShop(int round)
{
currentItemChoices = DrawRandomItems(possibleItems, itemsPerShop);
currentWeaponChoices = DrawRandomItems(possibleWeapons, itemsPerShop);
shopUI.Show(currentItemChoices, currentWeaponChoices, this);
Time.timeScale = 0f;
}
private List<T> DrawRandomItems<T>(List<T> pool, int count)
{
var result = new List<T>();
var poolCopy = new List<T>(pool);
for (var i = 0; i < count && poolCopy.Count > 0; i++)
{
var idx = Random.Range(0, poolCopy.Count);
result.Add(poolCopy[idx]);
poolCopy.RemoveAt(idx);
}
return result;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3f37c728615643a1abc988bbfd34986c
timeCreated: 1752272671

View File

@@ -0,0 +1,56 @@
using Inventory;
using Systems;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Shop
{
public class ShopSlotUI : MonoBehaviour
{
private ScriptableObject item;
private ShopManager shopManager;
private int price;
[SerializeField] private Image icon;
[SerializeField] private TextMeshProUGUI nameText;
[SerializeField] private TextMeshProUGUI descriptionText;
[SerializeField] private TextMeshProUGUI priceText;
[SerializeField] private Button purchaseButton;
public void Setup(StatModifierItem item, ShopManager manager)
{
this.item = item;
shopManager = manager;
price = item.price;
icon.sprite = item.icon;
nameText.text = item.name;
descriptionText.text = item.description;
priceText.text = $"Price: {price}";
purchaseButton.interactable = GameManager.Instance.Coins >= price;
purchaseButton.onClick.AddListener(() => shopManager.BuyItem(item, price));
}
public void Setup(WeaponItem weapon, ShopManager manager)
{
item = weapon;
shopManager = manager;
price = weapon.price;
icon.sprite = weapon.icon;
nameText.text = weapon.weaponName;
descriptionText.text = weapon.description;
priceText.text = $"Price: {price}";
purchaseButton.interactable = GameManager.Instance.Coins >= price;
purchaseButton.onClick.AddListener(() => shopManager.BuyWeapon(weapon, price));
}
public bool MatchesItem(ScriptableObject item) => this.item == item;
public void MarkAsPurchased()
{
purchaseButton.interactable = false;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d76cc9c15ed441b930dbe52e5e9af5f
timeCreated: 1752273703

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using Inventory;
using Systems;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Shop
{
public class ShopUI : MonoBehaviour
{
[SerializeField] private GameObject shopPanel;
[SerializeField] private Transform itemSlotParent;
[SerializeField] private Transform weaponSlotParent;
[SerializeField] private ShopSlotUI slotPrefab;
[SerializeField] private TextMeshProUGUI roundsText;
private List<ShopSlotUI> currentSlots = new();
private void OnEnable()
{
GameManager.Instance.OnRoundEnd += UpdateRoundText;
}
private void OnDisable()
{
GameManager.Instance.OnRoundEnd -= UpdateRoundText;
}
public void Show(List<StatModifierItem> items, List<WeaponItem> weapons, ShopManager shopManager)
{
GameManager.Instance.StoreIsClosed = false;
UpdateRoundText(GameManager.Instance.CurrentRound);
shopPanel.SetActive(true);
ClearSlots();
foreach (var item in items)
{
var slot = Instantiate(slotPrefab, itemSlotParent);
slot.Setup(item, shopManager);
currentSlots.Add(slot);
}
foreach (var weapon in weapons)
{
var slot = Instantiate(slotPrefab, weaponSlotParent);
slot.Setup(weapon, shopManager);
currentSlots.Add(slot);
}
}
public void Hide()
{
GameManager.Instance.StoreIsClosed = true;
shopPanel.SetActive(false);
ClearSlots();
}
public void MarkAsPurchased(ScriptableObject item)
{
foreach (var slot in currentSlots)
{
if (slot.MatchesItem(item)) slot.MarkAsPurchased();
}
}
private void ClearSlots()
{
foreach (var slot in currentSlots) Destroy(slot.gameObject);
currentSlots.Clear();
}
private void UpdateRoundText(int round)
{
var nextRound = Mathf.Min(round + 1, GameManager.Instance.MaxRounds);
roundsText.text = $"Round: {nextRound}/{GameManager.Instance.MaxRounds}";
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: abe11e705b4f47feb25a4e4845d1e6e9
timeCreated: 1752272723