diff --git a/scripts/character/CharacterTemplate.cs b/scripts/character/CharacterTemplate.cs index dd7ea07..d2e1251 100644 --- a/scripts/character/CharacterTemplate.cs +++ b/scripts/character/CharacterTemplate.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using ColdMint.scripts.camp; using ColdMint.scripts.damage; using ColdMint.scripts.health; +using ColdMint.scripts.inventory; using ColdMint.scripts.weapon; using Godot; @@ -29,6 +30,16 @@ public partial class CharacterTemplate : CharacterBody2D protected string? CharacterName; + //Item containers are used to store items. + //物品容器用于存储物品。 + protected IItemContainer? _itemContainer; + + + public IItemContainer? ItemContainer + { + get => _itemContainer; + set => _itemContainer = value; + } //Items currently held //当前持有的物品 @@ -167,31 +178,75 @@ public partial class CharacterTemplate : CharacterBody2D ///Whether successfully picked up ///是否成功拾起 /// - public bool PickItem(Node2D pickAbleItem) + public bool PickItem(Node2D? pickAbleItem) { - if (CurrentItem == null) + if (pickAbleItem == null) { - if (pickAbleItem is WeaponTemplate weaponTemplate) - { - if (weaponTemplate.Owner != null && weaponTemplate.Owner != this) - { - return false; - } - - weaponTemplate.Owner = this; - weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, false); - weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, false); - weaponTemplate.EnableContactInjury = false; - weaponTemplate.Sleeping = true; - } - - pickAbleItem.Reparent(ItemMarker2D); - pickAbleItem.Position = Vector2.Zero; - CurrentItem = pickAbleItem; - return true; + return false; } - return false; + if (_itemContainer == null) + { + return false; + } + + //Get the currently selected node + //拿到当前选择的节点 + var itemSlotNode = _itemContainer.GetSelectItemSlotNode(); + if (itemSlotNode == null) + { + return false; + } + + if (pickAbleItem is not IItem item) + { + return false; + } + + //First check if we can pick up the item. + //先检查我们能否拾起此物品。 + var canPick = _itemContainer.CanAddItem(item); + if (!canPick) + { + return false; + } + + //Is it successfully added to the container? + //再检查是否成功的添加到容器内了? + var addSuccess = _itemContainer.AddItem(item); + if (!addSuccess) + { + return false; + } + + //Set up routine handling of picked up items. + //设置捡起物品的常规处理。 + //You can supplement picking up state handling for more types of objects here. + //您可以在这里补充更多类型对象的捡起状态处理。 + if (pickAbleItem is WeaponTemplate weaponTemplate) + { + weaponTemplate.Owner = this; + weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, false); + weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, false); + weaponTemplate.EnableContactInjury = false; + weaponTemplate.Sleeping = true; + } + + if (itemSlotNode.GetItem() != null && itemSlotNode.GetItem() == item && CurrentItem == null) + { + //If the selected item slot in the item container is a newly picked item, and there is no item in the hand, then we put the selected item into the hand. + //如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。 + CurrentItem = pickAbleItem; + } + else + { + pickAbleItem.Visible = false; + pickAbleItem.ProcessMode = ProcessModeEnum.Disabled; + } + + pickAbleItem.Reparent(ItemMarker2D); + pickAbleItem.Position = Vector2.Zero; + return true; } diff --git a/scripts/character/Player.cs b/scripts/character/Player.cs index b4f600c..6063f68 100644 --- a/scripts/character/Player.cs +++ b/scripts/character/Player.cs @@ -20,6 +20,9 @@ public partial class Player : CharacterTemplate protected Control FloatLabel; + //Empty object projectile + //空的物品抛射线 + private Vector2[] emptyVector2Array = new[] { Vector2.Zero }; //抛物线 private Line2D Parabola; @@ -172,39 +175,40 @@ public partial class Player : CharacterTemplate protected override void HookPhysicsProcess(ref Vector2 velocity, double delta) { + //When the collision state between the platform detection ray and the platform changes //在平台检测射线与平台碰撞状态改变时 if (PlatformDetectionRayCast2D.IsColliding() != CollidingWithPlatform) { + //When the state changes, update the action hint //当状态改变时,更新操作提示 CollidingWithPlatform = PlatformDetectionRayCast2D.IsColliding(); UpdateOperationTip(); } + //If the character is on the ground, give an upward velocity when the jump button is pressed //如果角色正在地面上,按下跳跃键时,给予一个向上的速度 if (Input.IsActionJustPressed("ui_up") && IsOnFloor()) velocity.Y = JumpVelocity; + //Moving left and right //左右移动 var axis = Input.GetAxis("ui_left", "ui_right"); velocity.X = axis * Speed; + //Use items //使用物品 if (Input.IsActionPressed("use_item")) { UseItem(GetGlobalMousePosition()); } + //Pick up an item //捡起物品 if (Input.IsActionJustPressed("pick_up")) { var success = PickItem(PickAbleItem); if (success) { - if (PickAbleItem is WeaponTemplate weaponTemplate) - { - GameSceneNodeHolder.HotBar.AddItem(weaponTemplate); - } - PickAbleItem = null; TotalNumberOfPickups--; if (FloatLabel != null) @@ -221,6 +225,7 @@ public partial class Player : CharacterTemplate { if (CollidingWithPlatform) { + //When the character stands on the platform and presses the ui_down key, we cancel the collision between the character and the platform //当角色站在平台上按下 ui_down 键时,我们取消角色与平台的碰撞 var timer = new Timer(); AddChild(timer); @@ -237,16 +242,23 @@ public partial class Player : CharacterTemplate } + //Display a parabola when an item is thrown //抛出物品时,显示抛物线 if (Input.IsActionPressed("throw")) { - if (CurrentItem != null) + if (CurrentItem == null) + { + Parabola.Points = emptyVector2Array; + } + else { Parabola.Points = ParabolicUtils.ComputeParabolic(ItemMarker2D.Position, GetThrowVelocity(), Gravity, 0.1f); } } + + //When you raise your hand, throw the object //抬起手时,抛出物品 if (Input.IsActionJustReleased("throw")) { @@ -295,6 +307,7 @@ public partial class Player : CharacterTemplate private Vector2 GetThrowVelocity() { + //We take the mouse position, normalize it, and then multiply it by the distance the player can throw //我们拿到鼠标的位置,将其归一化处理,然后乘以玩家可扔出的距离 return GetLocalMousePosition().Normalized() * throwingVelocity; } @@ -307,18 +320,21 @@ public partial class Player : CharacterTemplate switch (axis) { case -1: + //Minus 1, we move to the left //-1,向左移动 FacingLeft = true; itemMarker2DPosition.X = -ReadOnlyItemMarkerOriginalX; Flip(); break; case 1: + //1, move to the right //1,向右移动 FacingLeft = false; itemMarker2DPosition.X = ReadOnlyItemMarkerOriginalX; Flip(); break; default: + //0, when it's not pressed //0,没有按下时 break; } @@ -329,6 +345,7 @@ public partial class Player : CharacterTemplate protected override void Flip() { base.Flip(); + //If there is a weapon, flip it too //如果有武器的话,也要翻转 if (CurrentItem != null) { @@ -341,6 +358,13 @@ public partial class Player : CharacterTemplate protected override void EnterThePickingRangeBody(Node node) { + if (CurrentItem == node) + { + //If the node entering the pick range is the node held by the player, then return. + //如果说进入拾捡范围的节点是玩家所持有的节点,那么返回。 + return; + } + if (node is not Node2D) { return; @@ -398,6 +422,7 @@ public partial class Player : CharacterTemplate TotalNumberOfPickups--; if (TotalNumberOfPickups == 0) { + //Set to null if there are no more items to pick up //如果没有可捡的物品了,设置为null PickAbleItem = null; } diff --git a/scripts/inventory/HotBar.cs b/scripts/inventory/HotBar.cs index d961cfa..b24d7ab 100644 --- a/scripts/inventory/HotBar.cs +++ b/scripts/inventory/HotBar.cs @@ -10,7 +10,7 @@ namespace ColdMint.scripts.inventory; /// HotBar /// 快捷物品栏 /// -public partial class HotBar : HBoxContainer +public partial class HotBar : HBoxContainer, IItemContainer { private PackedScene _itemSlotPackedScene; private List _itemSlotNodes; @@ -30,13 +30,6 @@ public partial class HotBar : HBoxContainer public override void _Process(double delta) { base._Process(delta); - if (Input.IsActionPressed("throw")) - { - //Players are not allowed to switch current items while throwing them. - //玩家在抛物品时禁止切换当前物品。 - return; - } - if (Input.IsActionJustPressed("hotbar_next")) { var count = _itemSlotNodes.Count; @@ -198,27 +191,39 @@ public partial class HotBar : HBoxContainer var item = _itemSlotNodes[newSelectIndex].GetItem(); if (item == null) { - LogCat.Log("选择" + oldSelectIndex + "新的为" + newSelectIndex + "空对象"); - GameSceneNodeHolder.Player.CurrentItem = null; - LogCat.Log("我是空吗" + (GameSceneNodeHolder.Player.CurrentItem == null)); + if (GameSceneNodeHolder.Player != null) + { + GameSceneNodeHolder.Player.CurrentItem = null; + } } else { if (item is Node2D node2D) { - LogCat.Log("我是空吗" + (GameSceneNodeHolder.Player.CurrentItem == null)); - LogCat.Log("选择" + oldSelectIndex + "新的为" + newSelectIndex + "已赋值" + node2D); node2D.ProcessMode = ProcessModeEnum.Inherit; node2D.Show(); - GameSceneNodeHolder.Player.CurrentItem = node2D; + if (GameSceneNodeHolder.Player != null) + { + GameSceneNodeHolder.Player.CurrentItem = node2D; + } } else { - GameSceneNodeHolder.Player.CurrentItem = null; + if (GameSceneNodeHolder.Player != null) + { + GameSceneNodeHolder.Player.CurrentItem = null; + } } } } + + public bool CanAddItem(IItem item) + { + return Matching(item) != null; + } + + /// /// Add an item to the HotBar /// 在HotBar内添加一个物品 @@ -227,9 +232,54 @@ public partial class HotBar : HBoxContainer /// public bool AddItem(IItem item) { - return _itemSlotNodes.Count != 0 && _itemSlotNodes.Any(itemSlotNode => itemSlotNode.SetItem(item)); + var itemSlotNode = Matching(item); + if (itemSlotNode == null) + { + return false; + } + else + { + return itemSlotNode.SetItem(item); + } } + public ItemSlotNode? GetSelectItemSlotNode() + { + if (_itemSlotNodes.Count == 0) + { + return null; + } + + if (selectIndex < _itemSlotNodes.Count) + { + //Prevent subscripts from going out of bounds. + //防止下标越界。 + return _itemSlotNodes[selectIndex]; + } + + return null; + } + + public ItemSlotNode? Matching(IItem item) + { + if (_itemSlotNodes.Count == 0) + { + return null; + } + + + foreach (var itemSlotNode in _itemSlotNodes) + { + if (itemSlotNode.CanSetItem(item)) + { + //If there is an item slot to put this item in, then we return it. + //如果有物品槽可放置此物品,那么我们返回它。 + return itemSlotNode; + } + } + + return null; + } /// /// Add items tank diff --git a/scripts/inventory/IItem.cs b/scripts/inventory/IItem.cs index 60ad524..09811e9 100644 --- a/scripts/inventory/IItem.cs +++ b/scripts/inventory/IItem.cs @@ -27,7 +27,7 @@ public interface IItem /// Items can be set with Icon /// 物品可以设置图标 /// - Texture2D Icon { get; set; } + Texture2D? Icon { get; set; } /// /// Item has a name diff --git a/scripts/inventory/IItemContainer.cs b/scripts/inventory/IItemContainer.cs new file mode 100644 index 0000000..01d2bff --- /dev/null +++ b/scripts/inventory/IItemContainer.cs @@ -0,0 +1,46 @@ +namespace ColdMint.scripts.inventory; + +/// +/// item container +/// 物品容器 +/// +/// +///Item containers can store items. Things like backpacks and Hotbars are containers with visual pages. +///物品容器可以储存物品。像背包和hotbar是具有可视化页面的容器。 +/// +public interface IItemContainer +{ + /// + /// Can the specified item be added to the container? + /// 指定的物品是否可添加到容器内? + /// + /// + /// + bool CanAddItem(IItem item); + + /// + /// Implement methods for adding items + /// 实现添加物品的方法 + /// + /// + /// + bool AddItem(IItem item); + + /// + /// Gets the currently selected node + /// 获取当前选中的节点 + /// + /// + ItemSlotNode? GetSelectItemSlotNode(); + + /// + /// Based on the given item, match the item slots where it can be placed + /// 根据给定的物品,匹配可放置它的物品槽 + /// + /// + /// + ///Return null if there is no slot to place the item in + ///若没有槽可放置此物品,则返回null + /// + ItemSlotNode? Matching(IItem item); +} \ No newline at end of file diff --git a/scripts/inventory/ItemSlotNode.cs b/scripts/inventory/ItemSlotNode.cs index 11c069a..6f4e5ab 100644 --- a/scripts/inventory/ItemSlotNode.cs +++ b/scripts/inventory/ItemSlotNode.cs @@ -8,189 +8,212 @@ namespace ColdMint.scripts.inventory; /// public partial class ItemSlotNode : MarginContainer { - private IItem? _item; - private TextureRect _backgroundTextureRect; - private TextureRect _iconTextureRect; - private Label _quantityLabel; - private Control _control; - private bool _isSelect; - private Texture2D _backgroundTexture; - private Texture2D _backgroundTextureWhenSelect; + private IItem? _item; + private TextureRect _backgroundTextureRect; + private TextureRect _iconTextureRect; + private Label _quantityLabel; + private Control _control; + private bool _isSelect; + private Texture2D _backgroundTexture; + private Texture2D _backgroundTextureWhenSelect; - public bool IsSelect - { - get => _isSelect; - set - { - if (value) - { - _backgroundTextureRect.Texture = _backgroundTextureWhenSelect; - } - else - { - _backgroundTextureRect.Texture = _backgroundTexture; - } + public bool IsSelect + { + get => _isSelect; + set + { + if (value) + { + _backgroundTextureRect.Texture = _backgroundTextureWhenSelect; + } + else + { + _backgroundTextureRect.Texture = _backgroundTexture; + } - _isSelect = value; - } - } + _isSelect = value; + } + } - public TextureRect BackgroundTextureRect => _backgroundTextureRect; + public TextureRect BackgroundTextureRect => _backgroundTextureRect; - /// - /// Get the items in the item slot - /// 获取物品槽内的物品 - /// - /// - public IItem? GetItem() - { - return _item; - } + /// + /// Get the items in the item slot + /// 获取物品槽内的物品 + /// + /// + public IItem? GetItem() + { + return _item; + } - /// - /// Removes the specified number of items from the item slot - /// 在物品槽内移除指定数量的物品 - /// - /// - /// - public bool RemoveItem(int number) - { - if (_item == null) - { - return false; - } + /// + /// Removes the specified number of items from the item slot + /// 在物品槽内移除指定数量的物品 + /// + /// + /// + public bool RemoveItem(int number) + { + if (_item == null) + { + return false; + } - var newNumber = _item.Quantity - number; - if (newNumber <= 0) - { - //If the specified number of items is removed, the number of items is less than or equal to 0. Then we return the removal successful and empty the inventory. - //如果移除指定数量的物品后,物品数量小于或等于0。那么我们返回移除成功,并清空物品栏。 - ClearItem(); - return true; - } - else - { - _item.Quantity = newNumber; - UpdateTooltipText(_item); - UpdateQuantityLabel(_item.Quantity); - return true; - } - } + var newNumber = _item.Quantity - number; + if (newNumber <= 0) + { + //If the specified number of items is removed, the number of items is less than or equal to 0. Then we return the removal successful and empty the inventory. + //如果移除指定数量的物品后,物品数量小于或等于0。那么我们返回移除成功,并清空物品栏。 + ClearItem(); + return true; + } + else + { + _item.Quantity = newNumber; + UpdateTooltipText(_item); + UpdateQuantityLabel(_item.Quantity); + return true; + } + } - /// - /// Empty the items in the item slot - /// 清空物品槽内的物品 - /// - /// - ///This method does not calculate how many items should be left. If you want to remove a specified number of items, call the RemoveItem method. - ///此方法不会计算物品应该剩余多少个。若您希望移除指定数量的物品,请调用RemoveItem方法。 - /// - public void ClearItem() - { - _item = null; - _iconTextureRect.Texture = null; - _control.TooltipText = null; - _quantityLabel.Visible = false; - } + /// + /// Empty the items in the item slot + /// 清空物品槽内的物品 + /// + /// + ///This method does not calculate how many items should be left. If you want to remove a specified number of items, call the RemoveItem method. + ///此方法不会计算物品应该剩余多少个。若您希望移除指定数量的物品,请调用RemoveItem方法。 + /// + public void ClearItem() + { + _item = null; + _iconTextureRect.Texture = null; + _control.TooltipText = null; + _quantityLabel.Visible = false; + } - /// - /// Sets items for the item slot - /// 为物品槽设置物品 - /// - /// - /// - public bool SetItem(IItem item) - { - if (_item == null) - { - if (item.Icon != null) - { - _iconTextureRect.Texture = item.Icon; - } - _item = item; - UpdateTooltipText(item); - UpdateQuantityLabel(item.Quantity); - return true; - } - else - { - //This inventory already has items, but the items in this inventory are not the same as the incoming items - //这个物品栏已经有物品了,但是这个物品栏的物品和传入的物品不一样 - if (_item.Id != item.Id) - { - return false; - } + /// + /// Can the specified item be placed in the item slot? + /// 指定的物品是否可设置在物品槽内? + /// + /// + /// + public bool CanSetItem(IItem item) + { + if (_item == null) + { + return true; + } - var newQuantity = _item.Quantity + item.Quantity; - if (newQuantity > item.MaxStackQuantity) - { - //If the amount of the current item exceeds the maximum stack amount after placing it in this inventory - //如果将当前物品放置到这个物品栏后,数量超过了最大叠加数量 - return false; - } + //This inventory already has items, but the items in this inventory are not the same as the incoming items + //这个物品栏已经有物品了,但是这个物品栏的物品和传入的物品不一样 + if (_item.Id != item.Id) + { + return false; + } - _item.Quantity = newQuantity; - UpdateTooltipText(item); - UpdateQuantityLabel(newQuantity); - return true; - } - } + var newQuantity = _item.Quantity + item.Quantity; + if (newQuantity > item.MaxStackQuantity) + { + //If the amount of the current item exceeds the maximum stack amount after placing it in this inventory + //如果将当前物品放置到这个物品栏后,数量超过了最大叠加数量 + return false; + } - /// - /// Update item tips - /// 更新物品的提示内容 - /// - /// - private void UpdateTooltipText(IItem item) - { - if (Config.IsDebug()) - { - _control.TooltipText = string.Format(TranslationServer.Translate("item_prompt_debug"), item.Id, - TranslationServer.Translate(item.Name), - item.Quantity, item.MaxStackQuantity, item.GetType().Name, - TranslationServer.Translate(item.Description)); - } - else - { - _control.TooltipText = TranslationServer.Translate(item.Name) + "\n" + - TranslationServer.Translate(item.Description); - } - } + return true; + } - /// - /// Update quantity label - /// 更新数量标签 - /// - /// - private void UpdateQuantityLabel(int? quantity) - { - switch (quantity) - { - case null: - _quantityLabel.Visible = false; - return; - case > 1: - //When the quantity is greater than 1, we display the quantity. - //当数量大于1时,我们显示数量 - _quantityLabel.Text = quantity.ToString(); - _quantityLabel.Visible = true; - break; - default: - _quantityLabel.Visible = false; - break; - } - } + /// + /// Sets items for the item slot + /// 为物品槽设置物品 + /// + /// + /// + public bool SetItem(IItem item) + { + if (!CanSetItem(item)) + { + return false; + } - public override void _Ready() - { - _backgroundTexture = GD.Load("res://sprites/ui/ItemBarEmpty.png"); - _backgroundTextureWhenSelect = GD.Load("res://sprites/ui/ItemBarFocus.png"); - _backgroundTextureRect = - GetNode("BackgroundTexture"); - _iconTextureRect = GetNode("BackgroundTexture/CenterContainer/IconTextureRect"); - _quantityLabel = GetNode