This commit is contained in:
2025-06-16 07:59:50 +08:00
commit 7a6cd423fc
54 changed files with 4068 additions and 0 deletions

View File

@ -0,0 +1,121 @@
using Godot;
using System.Collections.Generic;
using System.Text.Json;
public partial class InventoryCategoryManager : Node
{
public static InventoryCategoryManager Instance { get; private set; }
public class InventoryCategory
{
public string CategoryName { get; set; }
public List<string> Items { get; set; } = new List<string>();
}
public class InventoryCategoryData
{
public List<InventoryCategory> Categories { get; set; } = new List<InventoryCategory>();
}
private InventoryCategoryData categoryData;
private Dictionary<string, InventoryCategory> categoryMap = new Dictionary<string, InventoryCategory>();
public override void _Ready()
{
if (Instance == null)
{
Instance = this;
LoadInventoryCategories();
}
else
{
QueueFree();
}
}
private void LoadInventoryCategories()
{
string configPath = "res://data/config/inventory_categories.json";
if (!FileAccess.FileExists(configPath))
{
GD.PrintErr($"库存分类配置文件不存在: {configPath}");
return;
}
using var file = FileAccess.Open(configPath, FileAccess.ModeFlags.Read);
if (file == null)
{
GD.PrintErr($"无法打开库存分类配置文件: {configPath}");
return;
}
string jsonContent = file.GetAsText();
try
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
categoryData = JsonSerializer.Deserialize<InventoryCategoryData>(jsonContent, options);
if (categoryData?.Categories != null)
{
// 构建分类映射
foreach (var category in categoryData.Categories)
{
categoryMap[category.CategoryName] = category;
GD.Print($"加载库存分类: {category.CategoryName}, 包含 {category.Items.Count} 个物品");
}
GD.Print($"成功加载 {categoryData.Categories.Count} 个库存分类");
}
}
catch (JsonException e)
{
GD.PrintErr($"解析库存分类配置文件失败: {e.Message}");
}
}
public List<InventoryCategory> GetAllCategories()
{
return categoryData?.Categories ?? new List<InventoryCategory>();
}
public InventoryCategory GetCategory(string categoryName)
{
return categoryMap.GetValueOrDefault(categoryName);
}
public Dictionary<string, GameData.ItemData> GetItemsByCategory(string categoryName)
{
var result = new Dictionary<string, GameData.ItemData>();
var category = GetCategory(categoryName);
if (category == null || GameData.Instance == null)
{
return result;
}
foreach (var itemId in category.Items)
{
var itemData = GameData.Instance.GetItem(itemId);
if (itemData != null)
{
result[itemId] = itemData;
}
}
return result;
}
public override void _ExitTree()
{
if (Instance == this)
{
Instance = null;
}
}
}

View File

@ -0,0 +1,368 @@
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
public partial class InventoryManager : Node
{
// 库存数据结构
public class InventoryItem
{
public string ItemId { get; set; }
public int Quantity { get; set; }
public InventoryItem(string itemId, int quantity = 0)
{
ItemId = itemId;
Quantity = quantity;
}
}
// 单例实例
private static InventoryManager instance;
public static InventoryManager Instance
{
get
{
return instance;
}
}
// 库存数据字典 - 物品ID -> 库存项
private Dictionary<string, InventoryItem> inventory = new Dictionary<string, InventoryItem>();
// 线程锁
private readonly object inventoryLock = new object();
// 事件委托
public delegate void InventoryChangedEventHandler(string itemId, int oldQuantity, int newQuantity);
public event InventoryChangedEventHandler InventoryChanged;
public override void _Ready()
{
GD.Print("InventoryManager _Ready 开始");
// 确保单例
if (instance == null)
{
instance = this;
InitializeInventory();
}
else
{
GD.PrintErr("InventoryManager 实例已存在!");
QueueFree();
}
}
// 初始化库存
private void InitializeInventory()
{
GD.Print("初始化库存系统");
lock (inventoryLock)
{
// 为所有已知物品创建库存条目初始数量为0
if (GameData.Instance != null)
{
var allItems = GameData.Instance.GetAllItems();
foreach (var item in allItems)
{
inventory[item.Key] = new InventoryItem(item.Key, 0);
}
GD.Print($"初始化了 {inventory.Count} 个物品的库存条目");
}
else
{
GD.PrintErr("GameData.Instance 为 null无法初始化库存");
}
}
// 添加一些测试数据
AddTestData();
}
// 添加测试数据
private void AddTestData()
{
GD.Print("添加测试库存数据");
AddItem("iron_ore", 100);
AddItem("copper_ore", 50);
AddItem("coal_ore", 75);
AddItem("water", 200);
AddItem("iron_ingot", 25);
AddItem("copper_ingot", 15);
}
// 添加物品到库存
public bool AddItem(string itemId, int quantity)
{
if (quantity <= 0) return false;
lock (inventoryLock)
{
int oldQuantity = GetItemQuantityUnsafe(itemId);
if (!inventory.ContainsKey(itemId))
{
inventory[itemId] = new InventoryItem(itemId, quantity);
}
else
{
inventory[itemId].Quantity += quantity;
}
int newQuantity = inventory[itemId].Quantity;
GD.Print($"添加物品: {itemId} +{quantity} (总量: {newQuantity})");
// 在锁外触发事件
CallDeferred(nameof(TriggerInventoryChanged), itemId, oldQuantity, newQuantity);
return true;
}
}
// 从库存中移除物品
public bool RemoveItem(string itemId, int quantity)
{
if (quantity <= 0) return false;
lock (inventoryLock)
{
if (!inventory.ContainsKey(itemId)) return false;
int oldQuantity = inventory[itemId].Quantity;
if (oldQuantity < quantity) return false; // 库存不足
inventory[itemId].Quantity -= quantity;
int newQuantity = inventory[itemId].Quantity;
GD.Print($"移除物品: {itemId} -{quantity} (剩余: {newQuantity})");
// 在锁外触发事件
CallDeferred(nameof(TriggerInventoryChanged), itemId, oldQuantity, newQuantity);
return true;
}
}
// 设置物品数量
public void SetItemQuantity(string itemId, int quantity)
{
lock (inventoryLock)
{
int oldQuantity = GetItemQuantityUnsafe(itemId);
if (!inventory.ContainsKey(itemId))
{
inventory[itemId] = new InventoryItem(itemId, quantity);
}
else
{
inventory[itemId].Quantity = quantity;
}
GD.Print($"设置物品数量: {itemId} = {quantity}");
// 在锁外触发事件
CallDeferred(nameof(TriggerInventoryChanged), itemId, oldQuantity, quantity);
}
}
// 获取物品数量(线程安全)
public int GetItemQuantity(string itemId)
{
lock (inventoryLock)
{
return GetItemQuantityUnsafe(itemId);
}
}
// 获取物品数量(非线程安全,内部使用)
private int GetItemQuantityUnsafe(string itemId)
{
if (inventory.ContainsKey(itemId))
{
return inventory[itemId].Quantity;
}
return 0;
}
// 检查是否有足够的物品
public bool HasEnoughItems(string itemId, int requiredQuantity)
{
return GetItemQuantity(itemId) >= requiredQuantity;
}
// 检查是否有足够的材料(用于配方检查)
public bool HasEnoughMaterials(Dictionary<string, int> recipe)
{
if (recipe == null) return true;
lock (inventoryLock)
{
foreach (var requirement in recipe)
{
if (GetItemQuantityUnsafe(requirement.Key) < requirement.Value)
{
return false;
}
}
return true;
}
}
// 消耗材料(用于生产)
public bool ConsumeMaterials(Dictionary<string, int> recipe)
{
if (recipe == null) return true;
lock (inventoryLock)
{
// 先检查是否有足够材料
foreach (var requirement in recipe)
{
if (GetItemQuantityUnsafe(requirement.Key) < requirement.Value)
{
return false;
}
}
// 消耗材料
foreach (var requirement in recipe)
{
int oldQuantity = GetItemQuantityUnsafe(requirement.Key);
inventory[requirement.Key].Quantity -= requirement.Value;
int newQuantity = inventory[requirement.Key].Quantity;
// 在锁外触发事件
CallDeferred(nameof(TriggerInventoryChanged), requirement.Key, oldQuantity, newQuantity);
}
return true;
}
}
// 获取所有库存物品
public Dictionary<string, InventoryItem> GetAllInventory()
{
lock (inventoryLock)
{
var result = new Dictionary<string, InventoryItem>();
foreach (var item in inventory)
{
result[item.Key] = new InventoryItem(item.Value.ItemId, item.Value.Quantity);
}
return result;
}
}
// 获取有库存的物品(数量>0
public Dictionary<string, InventoryItem> GetAvailableItems()
{
lock (inventoryLock)
{
var result = new Dictionary<string, InventoryItem>();
foreach (var item in inventory)
{
if (item.Value.Quantity > 0)
{
result[item.Key] = new InventoryItem(item.Value.ItemId, item.Value.Quantity);
}
}
return result;
}
}
// 获取指定分类的库存物品
public Dictionary<string, InventoryItem> GetInventoryByCategory(GameData.ItemCategory category)
{
lock (inventoryLock)
{
var result = new Dictionary<string, InventoryItem>();
foreach (var item in inventory)
{
var itemData = GameData.Instance?.GetItem(item.Key);
if (itemData != null && itemData.Category == category)
{
result[item.Key] = new InventoryItem(item.Value.ItemId, item.Value.Quantity);
}
}
return result;
}
}
// 清空库存
public void ClearInventory()
{
Dictionary<string, int> oldQuantities;
lock (inventoryLock)
{
oldQuantities = new Dictionary<string, int>();
foreach (var item in inventory)
{
oldQuantities[item.Key] = item.Value.Quantity;
item.Value.Quantity = 0;
}
GD.Print("清空所有库存");
}
// 在锁外触发事件
foreach (var item in oldQuantities)
{
if (item.Value > 0)
{
CallDeferred(nameof(TriggerInventoryChanged), item.Key, item.Value, 0);
}
}
}
// 获取库存总数量
public int GetTotalItemCount()
{
lock (inventoryLock)
{
return inventory.Values.Sum(item => item.Quantity);
}
}
// 调试:打印所有库存
public void PrintInventory()
{
lock (inventoryLock)
{
GD.Print("=== 当前库存 ===");
foreach (var item in inventory)
{
if (item.Value.Quantity > 0)
{
var itemData = GameData.Instance?.GetItem(item.Key);
string itemName = itemData?.Name ?? item.Key;
GD.Print($"{itemName}: {item.Value.Quantity}");
}
}
GD.Print("===============");
}
}
// 触发库存变化事件(在主线程中调用)
private void TriggerInventoryChanged(string itemId, int oldQuantity, int newQuantity)
{
InventoryChanged?.Invoke(itemId, oldQuantity, newQuantity);
}
// 清理单例
public override void _ExitTree()
{
if (instance == this)
{
instance = null;
}
}
}

View File

@ -0,0 +1,265 @@
using Godot;
using System.Collections.Generic;
public partial class InventoryTableManager : VBoxContainer
{
private PackedScene inventoryItemScene;
private Dictionary<string, GridContainer> categoryContainers = new Dictionary<string, GridContainer>();
public override void _Ready()
{
GD.Print("InventoryTableManager _Ready 开始");
// 加载库存物品场景
inventoryItemScene = GD.Load<PackedScene>("res://scenes/InventoryItem.tscn");
if (inventoryItemScene == null)
{
GD.PrintErr("无法加载InventoryItem场景");
return;
}
// 初始化库存显示
InitializeInventoryDisplay();
// 订阅库存变化事件
if (InventoryManager.Instance != null)
{
InventoryManager.Instance.InventoryChanged += OnInventoryChanged;
}
GD.Print("InventoryTableManager 初始化完成");
}
private void InitializeInventoryDisplay()
{
GD.Print("初始化库存Table显示");
// 等待InventoryCategoryManager初始化
if (InventoryCategoryManager.Instance == null)
{
CallDeferred(nameof(InitializeInventoryDisplay));
return;
}
CreateCategoryBlocks();
UpdateInventoryDisplay();
}
private void CreateCategoryBlocks()
{
// 清空现有内容
foreach (Node child in GetChildren())
{
child.QueueFree();
}
categoryContainers.Clear();
var categories = InventoryCategoryManager.Instance.GetAllCategories();
foreach (var category in categories)
{
CreateCategoryBlock(category);
}
}
private void CreateCategoryBlock(InventoryCategoryManager.InventoryCategory category)
{
GD.Print($"创建库存分类块: {category.CategoryName}");
// 创建分类容器
var categoryContainer = new VBoxContainer();
categoryContainer.Name = $"{category.CategoryName}Block";
// 添加顶部间距
var topSpacer = new Control();
topSpacer.CustomMinimumSize = new Vector2(0, 10);
categoryContainer.AddChild(topSpacer);
// 创建标题行
var titleContainer = new HBoxContainer();
// 分类名称标签
var titleLabel = new Label();
titleLabel.Text = category.CategoryName;
titleLabel.HorizontalAlignment = HorizontalAlignment.Left;
titleLabel.AddThemeFontSizeOverride("font_size", 14);
titleLabel.Modulate = new Color(0.9f, 0.9f, 0.9f, 1.0f);
titleContainer.AddChild(titleLabel);
// 添加小间距
var labelSpacer = new Control();
labelSpacer.CustomMinimumSize = new Vector2(10, 0);
titleContainer.AddChild(labelSpacer);
// HSeparator 横线分隔符
var separator = new HSeparator();
separator.SizeFlagsHorizontal = Control.SizeFlags.ExpandFill;
separator.SizeFlagsVertical = Control.SizeFlags.ShrinkCenter;
titleContainer.AddChild(separator);
categoryContainer.AddChild(titleContainer);
// 添加小间距
var spacer = new Control();
spacer.CustomMinimumSize = new Vector2(0, 5);
categoryContainer.AddChild(spacer);
// 创建物品列表容器 - 使用GridContainer实现每行2个物品
var itemsContainer = new GridContainer();
itemsContainer.Name = $"{category.CategoryName}Items";
itemsContainer.Columns = 2; // 每行2列
itemsContainer.SizeFlagsHorizontal = Control.SizeFlags.ExpandFill;
itemsContainer.AddThemeConstantOverride("h_separation", 4); // 减少水平间距
itemsContainer.AddThemeConstantOverride("v_separation", 4); // 垂直间距
categoryContainer.AddChild(itemsContainer);
// 保存容器引用
categoryContainers[category.CategoryName] = itemsContainer;
// 添加到主容器
AddChild(categoryContainer);
}
private void OnInventoryChanged(string itemId, int oldQuantity, int newQuantity)
{
GD.Print($"库存Table变化: {itemId} {oldQuantity} -> {newQuantity}");
UpdateInventoryDisplay();
}
private void UpdateInventoryDisplay()
{
if (InventoryManager.Instance == null || InventoryCategoryManager.Instance == null)
{
return;
}
// 清空所有分类的物品显示
foreach (var container in categoryContainers.Values)
{
foreach (Node child in container.GetChildren())
{
child.QueueFree();
}
}
// 获取所有分类并更新显示
var categories = InventoryCategoryManager.Instance.GetAllCategories();
foreach (var category in categories)
{
UpdateCategoryDisplay(category);
}
}
private void UpdateCategoryDisplay(InventoryCategoryManager.InventoryCategory category)
{
if (!categoryContainers.ContainsKey(category.CategoryName))
{
return;
}
var container = categoryContainers[category.CategoryName];
var items = InventoryCategoryManager.Instance.GetItemsByCategory(category.CategoryName);
foreach (var kvp in items)
{
var itemId = kvp.Key;
var itemData = kvp.Value;
// 获取库存数量
int quantity = InventoryManager.Instance.GetItemQuantity(itemId);
// 只显示有库存的物品或者显示所有物品包括0数量
// 这里我们选择显示所有物品0数量的显示为灰色
CreateInventoryItemDisplay(container, itemData, quantity);
}
}
private void CreateInventoryItemDisplay(GridContainer container, GameData.ItemData itemData, int quantity)
{
// 实例化库存物品UI
var inventoryItem = inventoryItemScene.Instantiate<Control>();
if (inventoryItem == null)
{
GD.PrintErr($"无法实例化InventoryItem for {itemData.Name}");
return;
}
// 设置尺寸标志,让物品能够填充分配的空间
inventoryItem.SizeFlagsHorizontal = Control.SizeFlags.ExpandFill;
inventoryItem.SizeFlagsVertical = Control.SizeFlags.ShrinkCenter;
// 设置图标
var iconTexture = inventoryItem.GetNode<TextureRect>("MarginContainer/HBoxContainer/IconTexture");
if (iconTexture != null && !string.IsNullOrEmpty(itemData.IconPath))
{
// 检查文件是否存在
if (Godot.FileAccess.FileExists(itemData.IconPath))
{
var texture = GD.Load<Texture2D>(itemData.IconPath);
if (texture != null)
{
iconTexture.Texture = texture;
}
}
else
{
// 使用默认图标
var defaultIcon = GD.Load<Texture2D>("res://assets/textures/icon.svg");
if (defaultIcon != null)
{
iconTexture.Texture = defaultIcon;
}
}
}
// 设置名称
var nameLabel = inventoryItem.GetNode<Label>("MarginContainer/HBoxContainer/NameLabel");
if (nameLabel != null)
{
nameLabel.Text = itemData.Name;
// 如果数量为0设置为灰色
if (quantity == 0)
{
nameLabel.Modulate = new Color(0.6f, 0.6f, 0.6f, 1.0f);
}
else
{
nameLabel.Modulate = new Color(1.0f, 1.0f, 1.0f, 1.0f);
}
}
// 设置数量
var quantityLabel = inventoryItem.GetNode<Label>("MarginContainer/HBoxContainer/QuantityLabel");
if (quantityLabel != null)
{
quantityLabel.Text = quantity.ToString();
// 根据数量设置颜色
if (quantity == 0)
{
quantityLabel.Modulate = new Color(0.6f, 0.6f, 0.6f, 1.0f);
}
else if (quantity < 10)
{
quantityLabel.Modulate = new Color(1.0f, 0.8f, 0.4f, 1.0f); // 橙色 - 库存较低
}
else
{
quantityLabel.Modulate = new Color(0.8f, 1.0f, 0.8f, 1.0f); // 绿色 - 库存充足
}
}
container.AddChild(inventoryItem);
}
public override void _ExitTree()
{
// 取消订阅事件
if (InventoryManager.Instance != null)
{
InventoryManager.Instance.InventoryChanged -= OnInventoryChanged;
}
}
}