Adjust the logic of how the player picks up items.

调整玩家捡起物品的逻辑。
This commit is contained in:
Cold-Mint 2024-05-06 18:59:39 +08:00
parent 801b3dd93b
commit 9435f76fc0
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
7 changed files with 423 additions and 217 deletions

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using ColdMint.scripts.camp; using ColdMint.scripts.camp;
using ColdMint.scripts.damage; using ColdMint.scripts.damage;
using ColdMint.scripts.health; using ColdMint.scripts.health;
using ColdMint.scripts.inventory;
using ColdMint.scripts.weapon; using ColdMint.scripts.weapon;
using Godot; using Godot;
@ -29,6 +30,16 @@ public partial class CharacterTemplate : CharacterBody2D
protected string? CharacterName; protected string? CharacterName;
//Item containers are used to store items.
//物品容器用于存储物品。
protected IItemContainer? _itemContainer;
public IItemContainer? ItemContainer
{
get => _itemContainer;
set => _itemContainer = value;
}
//Items currently held //Items currently held
//当前持有的物品 //当前持有的物品
@ -167,17 +178,53 @@ public partial class CharacterTemplate : CharacterBody2D
///<para>Whether successfully picked up</para> ///<para>Whether successfully picked up</para>
///<para>是否成功拾起</para> ///<para>是否成功拾起</para>
/// </returns> /// </returns>
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; 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.Owner = this;
weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, false); weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, false);
weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, false); weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, false);
@ -185,13 +232,21 @@ public partial class CharacterTemplate : CharacterBody2D
weaponTemplate.Sleeping = true; weaponTemplate.Sleeping = true;
} }
pickAbleItem.Reparent(ItemMarker2D); if (itemSlotNode.GetItem() != null && itemSlotNode.GetItem() == item && CurrentItem == null)
pickAbleItem.Position = Vector2.Zero; {
//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; CurrentItem = pickAbleItem;
return true; }
else
{
pickAbleItem.Visible = false;
pickAbleItem.ProcessMode = ProcessModeEnum.Disabled;
} }
return false; pickAbleItem.Reparent(ItemMarker2D);
pickAbleItem.Position = Vector2.Zero;
return true;
} }

View File

@ -20,6 +20,9 @@ public partial class Player : CharacterTemplate
protected Control FloatLabel; protected Control FloatLabel;
//Empty object projectile
//空的物品抛射线
private Vector2[] emptyVector2Array = new[] { Vector2.Zero };
//抛物线 //抛物线
private Line2D Parabola; private Line2D Parabola;
@ -172,39 +175,40 @@ public partial class Player : CharacterTemplate
protected override void HookPhysicsProcess(ref Vector2 velocity, double delta) 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) if (PlatformDetectionRayCast2D.IsColliding() != CollidingWithPlatform)
{ {
//When the state changes, update the action hint
//当状态改变时,更新操作提示 //当状态改变时,更新操作提示
CollidingWithPlatform = PlatformDetectionRayCast2D.IsColliding(); CollidingWithPlatform = PlatformDetectionRayCast2D.IsColliding();
UpdateOperationTip(); UpdateOperationTip();
} }
//If the character is on the ground, give an upward velocity when the jump button is pressed
//如果角色正在地面上,按下跳跃键时,给予一个向上的速度 //如果角色正在地面上,按下跳跃键时,给予一个向上的速度
if (Input.IsActionJustPressed("ui_up") && IsOnFloor()) if (Input.IsActionJustPressed("ui_up") && IsOnFloor())
velocity.Y = JumpVelocity; velocity.Y = JumpVelocity;
//Moving left and right
//左右移动 //左右移动
var axis = Input.GetAxis("ui_left", "ui_right"); var axis = Input.GetAxis("ui_left", "ui_right");
velocity.X = axis * Speed; velocity.X = axis * Speed;
//Use items
//使用物品 //使用物品
if (Input.IsActionPressed("use_item")) if (Input.IsActionPressed("use_item"))
{ {
UseItem(GetGlobalMousePosition()); UseItem(GetGlobalMousePosition());
} }
//Pick up an item
//捡起物品 //捡起物品
if (Input.IsActionJustPressed("pick_up")) if (Input.IsActionJustPressed("pick_up"))
{ {
var success = PickItem(PickAbleItem); var success = PickItem(PickAbleItem);
if (success) if (success)
{ {
if (PickAbleItem is WeaponTemplate weaponTemplate)
{
GameSceneNodeHolder.HotBar.AddItem(weaponTemplate);
}
PickAbleItem = null; PickAbleItem = null;
TotalNumberOfPickups--; TotalNumberOfPickups--;
if (FloatLabel != null) if (FloatLabel != null)
@ -221,6 +225,7 @@ public partial class Player : CharacterTemplate
{ {
if (CollidingWithPlatform) 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 键时,我们取消角色与平台的碰撞 //当角色站在平台上按下 ui_down 键时,我们取消角色与平台的碰撞
var timer = new Timer(); var timer = new Timer();
AddChild(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 (Input.IsActionPressed("throw"))
{ {
if (CurrentItem != null) if (CurrentItem == null)
{
Parabola.Points = emptyVector2Array;
}
else
{ {
Parabola.Points = Parabola.Points =
ParabolicUtils.ComputeParabolic(ItemMarker2D.Position, GetThrowVelocity(), Gravity, 0.1f); ParabolicUtils.ComputeParabolic(ItemMarker2D.Position, GetThrowVelocity(), Gravity, 0.1f);
} }
} }
//When you raise your hand, throw the object
//抬起手时,抛出物品 //抬起手时,抛出物品
if (Input.IsActionJustReleased("throw")) if (Input.IsActionJustReleased("throw"))
{ {
@ -295,6 +307,7 @@ public partial class Player : CharacterTemplate
private Vector2 GetThrowVelocity() 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; return GetLocalMousePosition().Normalized() * throwingVelocity;
} }
@ -307,18 +320,21 @@ public partial class Player : CharacterTemplate
switch (axis) switch (axis)
{ {
case -1: case -1:
//Minus 1, we move to the left
//-1向左移动 //-1向左移动
FacingLeft = true; FacingLeft = true;
itemMarker2DPosition.X = -ReadOnlyItemMarkerOriginalX; itemMarker2DPosition.X = -ReadOnlyItemMarkerOriginalX;
Flip(); Flip();
break; break;
case 1: case 1:
//1, move to the right
//1向右移动 //1向右移动
FacingLeft = false; FacingLeft = false;
itemMarker2DPosition.X = ReadOnlyItemMarkerOriginalX; itemMarker2DPosition.X = ReadOnlyItemMarkerOriginalX;
Flip(); Flip();
break; break;
default: default:
//0, when it's not pressed
//0没有按下时 //0没有按下时
break; break;
} }
@ -329,6 +345,7 @@ public partial class Player : CharacterTemplate
protected override void Flip() protected override void Flip()
{ {
base.Flip(); base.Flip();
//If there is a weapon, flip it too
//如果有武器的话,也要翻转 //如果有武器的话,也要翻转
if (CurrentItem != null) if (CurrentItem != null)
{ {
@ -341,6 +358,13 @@ public partial class Player : CharacterTemplate
protected override void EnterThePickingRangeBody(Node node) 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) if (node is not Node2D)
{ {
return; return;
@ -398,6 +422,7 @@ public partial class Player : CharacterTemplate
TotalNumberOfPickups--; TotalNumberOfPickups--;
if (TotalNumberOfPickups == 0) if (TotalNumberOfPickups == 0)
{ {
//Set to null if there are no more items to pick up
//如果没有可捡的物品了设置为null //如果没有可捡的物品了设置为null
PickAbleItem = null; PickAbleItem = null;
} }

View File

@ -10,7 +10,7 @@ namespace ColdMint.scripts.inventory;
/// <para>HotBar</para> /// <para>HotBar</para>
/// <para>快捷物品栏</para> /// <para>快捷物品栏</para>
/// </summary> /// </summary>
public partial class HotBar : HBoxContainer public partial class HotBar : HBoxContainer, IItemContainer
{ {
private PackedScene _itemSlotPackedScene; private PackedScene _itemSlotPackedScene;
private List<ItemSlotNode> _itemSlotNodes; private List<ItemSlotNode> _itemSlotNodes;
@ -30,13 +30,6 @@ public partial class HotBar : HBoxContainer
public override void _Process(double delta) public override void _Process(double delta)
{ {
base._Process(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")) if (Input.IsActionJustPressed("hotbar_next"))
{ {
var count = _itemSlotNodes.Count; var count = _itemSlotNodes.Count;
@ -198,26 +191,38 @@ public partial class HotBar : HBoxContainer
var item = _itemSlotNodes[newSelectIndex].GetItem(); var item = _itemSlotNodes[newSelectIndex].GetItem();
if (item == null) if (item == null)
{ {
LogCat.Log("选择" + oldSelectIndex + "新的为" + newSelectIndex + "空对象"); if (GameSceneNodeHolder.Player != null)
{
GameSceneNodeHolder.Player.CurrentItem = null; GameSceneNodeHolder.Player.CurrentItem = null;
LogCat.Log("我是空吗" + (GameSceneNodeHolder.Player.CurrentItem == null)); }
} }
else else
{ {
if (item is Node2D node2D) if (item is Node2D node2D)
{ {
LogCat.Log("我是空吗" + (GameSceneNodeHolder.Player.CurrentItem == null));
LogCat.Log("选择" + oldSelectIndex + "新的为" + newSelectIndex + "已赋值" + node2D);
node2D.ProcessMode = ProcessModeEnum.Inherit; node2D.ProcessMode = ProcessModeEnum.Inherit;
node2D.Show(); node2D.Show();
if (GameSceneNodeHolder.Player != null)
{
GameSceneNodeHolder.Player.CurrentItem = node2D; GameSceneNodeHolder.Player.CurrentItem = node2D;
} }
}
else else
{
if (GameSceneNodeHolder.Player != null)
{ {
GameSceneNodeHolder.Player.CurrentItem = null; GameSceneNodeHolder.Player.CurrentItem = null;
} }
} }
} }
}
public bool CanAddItem(IItem item)
{
return Matching(item) != null;
}
/// <summary> /// <summary>
/// <para>Add an item to the HotBar</para> /// <para>Add an item to the HotBar</para>
@ -227,9 +232,54 @@ public partial class HotBar : HBoxContainer
/// <returns></returns> /// <returns></returns>
public bool AddItem(IItem item) 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;
}
/// <summary> /// <summary>
/// <para>Add items tank</para> /// <para>Add items tank</para>

View File

@ -27,7 +27,7 @@ public interface IItem
/// <para>Items can be set with Icon</para> /// <para>Items can be set with Icon</para>
/// <para>物品可以设置图标</para> /// <para>物品可以设置图标</para>
/// </summary> /// </summary>
Texture2D Icon { get; set; } Texture2D? Icon { get; set; }
/// <summary> /// <summary>
/// <para>Item has a name</para> /// <para>Item has a name</para>

View File

@ -0,0 +1,46 @@
namespace ColdMint.scripts.inventory;
/// <summary>
/// <para>item container</para>
/// <para>物品容器</para>
/// </summary>
/// <remarks>
///<para>Item containers can store items. Things like backpacks and Hotbars are containers with visual pages.</para>
///<para>物品容器可以储存物品。像背包和hotbar是具有可视化页面的容器。</para>
/// </remarks>
public interface IItemContainer
{
/// <summary>
/// <para>Can the specified item be added to the container?</para>
/// <para>指定的物品是否可添加到容器内?</para>
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
bool CanAddItem(IItem item);
/// <summary>
/// <para>Implement methods for adding items</para>
/// <para>实现添加物品的方法</para>
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
bool AddItem(IItem item);
/// <summary>
/// <para>Gets the currently selected node</para>
/// <para>获取当前选中的节点</para>
/// </summary>
/// <returns></returns>
ItemSlotNode? GetSelectItemSlotNode();
/// <summary>
/// <para>Based on the given item, match the item slots where it can be placed</para>
/// <para>根据给定的物品,匹配可放置它的物品槽</para>
/// </summary>
/// <param name="item"></param>
/// <returns>
///<para>Return null if there is no slot to place the item in</para>
///<para>若没有槽可放置此物品则返回null</para>
/// </returns>
ItemSlotNode? Matching(IItem item);
}

View File

@ -93,28 +93,20 @@ public partial class ItemSlotNode : MarginContainer
_quantityLabel.Visible = false; _quantityLabel.Visible = false;
} }
/// <summary> /// <summary>
/// <para>Sets items for the item slot</para> /// <para>Can the specified item be placed in the item slot?</para>
/// <para>为物品槽设置物品</para> /// <para>指定的物品是否可设置在物品槽内?</para>
/// </summary> /// </summary>
/// <param name="item"></param> /// <param name="item"></param>
/// <returns></returns> /// <returns></returns>
public bool SetItem(IItem item) public bool CanSetItem(IItem item)
{ {
if (_item == null) if (_item == null)
{ {
if (item.Icon != null)
{
_iconTextureRect.Texture = item.Icon;
}
_item = item;
UpdateTooltipText(item);
UpdateQuantityLabel(item.Quantity);
return true; return true;
} }
else
{
//This inventory already has items, but the items in this inventory are not the same as the incoming items //This inventory already has items, but the items in this inventory are not the same as the incoming items
//这个物品栏已经有物品了,但是这个物品栏的物品和传入的物品不一样 //这个物品栏已经有物品了,但是这个物品栏的物品和传入的物品不一样
if (_item.Id != item.Id) if (_item.Id != item.Id)
@ -130,6 +122,37 @@ public partial class ItemSlotNode : MarginContainer
return false; return false;
} }
return true;
}
/// <summary>
/// <para>Sets items for the item slot</para>
/// <para>为物品槽设置物品</para>
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool SetItem(IItem item)
{
if (!CanSetItem(item))
{
return false;
}
if (_item == null)
{
if (item.Icon != null)
{
_iconTextureRect.Texture = item.Icon;
}
_item = item;
UpdateTooltipText(item);
UpdateQuantityLabel(item.Quantity);
return true;
}
else
{
var newQuantity = _item.Quantity + item.Quantity;
_item.Quantity = newQuantity; _item.Quantity = newQuantity;
UpdateTooltipText(item); UpdateTooltipText(item);
UpdateQuantityLabel(newQuantity); UpdateQuantityLabel(newQuantity);

View File

@ -66,7 +66,14 @@ public partial class GameSceneLoader : SceneLoaderTemplate
//Register players in the holder //Register players in the holder
//在持有者内注册玩家 //在持有者内注册玩家
var node2D = (Node2D)packedScene.Instantiate(); var node2D = (Node2D)packedScene.Instantiate();
GameSceneNodeHolder.Player = node2D as Player; if (node2D is Player player)
{
GameSceneNodeHolder.Player = player;
//Allow the player to pick up items.
//使玩家可以捡起物品。
player.ItemContainer = GameSceneNodeHolder.HotBar;
}
var gameRoot = GetNode<Node2D>("."); var gameRoot = GetNode<Node2D>(".");
gameRoot.AddChild(node2D); gameRoot.AddChild(node2D);
node2D.Position = new Vector2(55, 70); node2D.Position = new Vector2(55, 70);