Add item types. Item containers now support limiting added item types. Added spell classes for projectile weapons.

加入物品类型,物品容器支持限制添加的物品类型了。加入适用于抛射体武器的法术类。
This commit is contained in:
Cold-Mint 2024-10-04 10:21:09 +08:00
parent bb0f582fed
commit 2d7985010d
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
14 changed files with 183 additions and 166 deletions

View File

@ -4,7 +4,6 @@ log_missing_parameters,缺少参数。,Missing parameters.,パラメータが不
log_room_root_node_must_be_node2d,房间根节点必须是 Node2D。,Room root node must be an instance of Node2D.,ルートードはNode2Dでなければなりません。
log_width_or_height_of_room_slot_must_be_1,房间槽的宽度或高度必须为1。,The width or height of the room slot must be 1.,部屋の溝の幅または高さは1でなければなりません。
log_connected_room_timeout,连接房间超时。,Timeout when connecting rooms.,接続部屋はタイムアウトです。
log_projectiles_is_empty,未设置抛射体。,The projectile is not set.,射出体は設置されていません。
log_map_generator_missing_parameters,地图生成器缺少参数。,Map generator missing parameters.,マップジェネレータが不足しています。
log_map_generator_attempts_to_parse_empty_layout_diagrams,地图生成器尝试解析空的布局图。,Map generator attempts to parse empty layout diagrams.,マップジェネレータは空のレイアウト図を解析しようとしています。
log_map_generator_has_no_starting_room_data,地图生成器没有起点房间数据。,Map generator has no starting room data.,マップ生成器に起点部屋データはありません。

1 id zh en ja
4 log_room_root_node_must_be_node2d 房间根节点必须是 Node2D。 Room root node must be an instance of Node2D. ルートノードはNode2Dでなければなりません。
5 log_width_or_height_of_room_slot_must_be_1 房间槽的宽度或高度必须为1。 The width or height of the room slot must be 1. 部屋の溝の幅または高さは1でなければなりません。
6 log_connected_room_timeout 连接房间超时。 Timeout when connecting rooms. 接続部屋はタイムアウトです。
log_projectiles_is_empty 未设置抛射体。 The projectile is not set. 射出体は設置されていません。
7 log_map_generator_missing_parameters 地图生成器缺少参数。 Map generator missing parameters. マップジェネレータが不足しています。
8 log_map_generator_attempts_to_parse_empty_layout_diagrams 地图生成器尝试解析空的布局图。 Map generator attempts to parse empty layout diagrams. マップジェネレータは空のレイアウト図を解析しようとしています。
9 log_map_generator_has_no_starting_room_data 地图生成器没有起点房间数据。 Map generator has no starting room data. マップ生成器に起点部屋データはありません。

View File

@ -1,8 +1,7 @@
[gd_scene load_steps=8 format=3 uid="uid://dnnn2xyayiehk"]
[gd_scene load_steps=7 format=3 uid="uid://dnnn2xyayiehk"]
[ext_resource type="Texture2D" uid="uid://wt50kx6bup51" path="res://sprites/weapon/StaffNecromancy.png" id="1_ms3us"]
[ext_resource type="Script" path="res://scripts/weapon/ProjectileWeapon.cs" id="1_w8hhv"]
[ext_resource type="PackedScene" uid="uid://c01av43yk1q71" path="res://prefab/projectile/curseOfTheUndead.tscn" id="2_mwli5"]
[ext_resource type="Texture2D" uid="uid://dg5vwprt66w4j" path="res://sprites/weapon/StaffNecromancy_Icon.png" id="3_31iau"]
[ext_resource type="AudioStream" uid="uid://cak6chjjsu7wo" path="res://sounds/fire.wav" id="4_ffr2k"]
@ -17,9 +16,7 @@ collision_layer = 8
collision_mask = 34
angular_damp = -1.0
script = ExtResource("1_w8hhv")
OffsetAngle = 0.087
ProjectileScenes = [ExtResource("2_mwli5")]
Sequentially = true
NumberSlots = 5
FiringIntervalAsMillisecond = 300
_recoilStrength = 5
UniqueIcon = ExtResource("3_31iau")

View File

@ -197,6 +197,45 @@ public static class Config
{
return OS.HasFeature("editor");
}
/// <summary>
/// <para>ItemType</para>
/// <para>物品类型</para>
/// </summary>
public static class ItemType
{
/// <summary>
/// <para>Unknown</para>
/// <para>未知的</para>
/// </summary>
public const int Unknown = 0;
/// <summary>
/// <para>Placeholder</para>
/// <para>占位符</para>
/// </summary>
public const int Placeholder = 1;
/// <summary>
/// <para>Packsack</para>
/// <para>背包</para>
/// </summary>
public const int Packsack = 2;
/// <summary>
/// <para>ProjectileWeapon</para>
/// <para>远程武器</para>
/// </summary>
public const int ProjectileWeapon = 3;
/// <summary>
/// <para>Magic</para>
/// <para>法术</para>
/// </summary>
/// <remarks>
///<para>Type of special item used in Projectile weapons</para>
///<para>用于远程武器内的特殊物品类型</para>
/// </remarks>
public const int Magic = 4;
}
/// <summary>
/// <para>Room Injector ID</para>

View File

@ -11,13 +11,13 @@ public partial class HotBar : HBoxContainer
{
private IItemContainer? _itemContainer;
private IItemContainerDisplay? _itemContainerDisplay;
public override void _Ready()
{
base._Ready();
var universalItemContainer = new UniversalItemContainer(Config.HotBarSize);
_itemContainer = universalItemContainer;
_itemContainer.CanContainContainer = true;
universalItemContainer.AllowItemTypesExceptPlaceholder();
_itemContainer.SupportSelect = true;
_itemContainerDisplay = new ItemSlotContainerDisplay(this);
_itemContainerDisplay.BindItemContainer(_itemContainer);

View File

@ -62,6 +62,12 @@ public interface IItem
/// <para>最大物品数量</para>
/// </summary>
int MaxQuantity { get; }
/// <summary>
/// <para>ItemType</para>
/// <para>获取物品类型</para>
/// </summary>
int ItemType { get; }
/// <summary>
/// <para>Check or not</para>

View File

@ -20,6 +20,20 @@ public interface IItemContainer
/// <para>当物品的数据发生改变时,例如数量增加,减少,或者新物品被添加到容器内触发此事件</para>
/// </summary>
Action<ItemDataChangeEvent>? ItemDataChangeEvent { get; set; }
/// <summary>
/// <para>Allow Adding Item By Type</para>
/// <para>允许添加指定类型的物品</para>
/// </summary>
/// <param name="itemType"></param>
void AllowAddingItemByType(int itemType);
/// <summary>
/// <para>Disallow Adding Item By Type</para>
/// <para>禁止添加指定类型的物品</para>
/// </summary>
/// <param name="itemType"></param>
void DisallowAddingItemByType(int itemType);
/// <summary>
/// <para>Can the specified item be added to the container?</para>
@ -45,13 +59,7 @@ public interface IItemContainer
/// <para>此物品容器是否支持选中</para>
/// </summary>
bool SupportSelect { get; set; }
/// <summary>
/// <para>Whether this item container supports items that CanContainItems=true, such as backpacks</para>
/// <para>此物品容器是否支持容纳CanContainItems=true的物品例如背包</para>
/// </summary>
bool CanContainContainer { get; set; }
/// <summary>
/// <para>Gets a placeholder object</para>
/// <para>获取占位符对象</para>

View File

@ -117,7 +117,7 @@ public partial class ItemSlotNode : MarginContainer, IItemDisplay
return;
}
if (Item.SelfItemContainer != null)
if (Item.SelfItemContainer != null && Item.SelfItemContainer.CanAddItem(sourceItem))
{
//Use items and place them on the container.
//用物品,在物品容器上放置。
@ -138,7 +138,7 @@ public partial class ItemSlotNode : MarginContainer, IItemDisplay
return;
}
if (sourceItem.SelfItemContainer != null)
if (sourceItem.SelfItemContainer != null && sourceItem.SelfItemContainer.CanAddItem(Item))
{
//Use containers and place on top of items.
//用容器物品,在物品上放置。

View File

@ -12,6 +12,11 @@ namespace ColdMint.scripts.inventory;
public partial class Packsack : PickAbleTemplate
{
private const string Path = "res://prefab/ui/packsackUI.tscn";
public override int ItemType
{
get => Config.ItemType.Packsack;
}
[Export] public int NumberSlots { get; set; }
public override void Use(Node2D? owner, Vector2 targetGlobalPosition)
{
@ -54,7 +59,10 @@ public partial class Packsack : PickAbleTemplate
base._Ready();
if (SelfItemContainer == null)
{
SelfItemContainer = new UniversalItemContainer(NumberSlots);
var universalItemContainer = new UniversalItemContainer(NumberSlots);
universalItemContainer.AllowItemTypesExceptPlaceholder();
universalItemContainer.DisallowAddingItemByType(Config.ItemType.Packsack);
SelfItemContainer = universalItemContainer;
SelfItemContainer.SupportSelect = false;
}
GameSceneDepend.DynamicUiGroup?.RegisterControl(Path, () =>

View File

@ -13,17 +13,17 @@ public class PlaceholderItem : IItem
public void ShowSelf()
{
}
public void QueueFreeSelf()
{
}
public void HideSelf()
{
}
public Texture2D Icon { get; }
@ -31,6 +31,10 @@ public class PlaceholderItem : IItem
public string? Description { get; } = null;
public int Quantity { get; set; } = 1;
public int MaxQuantity { get; } = 1;
public int ItemType
{
get => Config.ItemType.Placeholder;
}
public bool IsSelect { get; set; }
public bool CanContainItems { get; set; } = false;
public IItemContainer? ItemContainer { get; set; }

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using ColdMint.scripts.map.events;
namespace ColdMint.scripts.inventory;
@ -32,15 +33,59 @@ public class UniversalItemContainer(int totalCapacity) : IItemContainer
/// </remarks>
private int _nextAvailableIndex;
/// <summary>
/// <para>The type of item that can be added to the item container</para>
/// <para>物品容器允许添加的物品类型</para>
/// </summary>
private readonly HashSet<int> _allowedItemTypes = new();
public Action<SelectedItemChangeEvent>? SelectedItemChangeEvent { get; set; }
public Action<ItemDataChangeEvent>? ItemDataChangeEvent { get; set; }
/// <summary>
/// <para>Allow Item Types Except Placeholder</para>
/// <para>允许添加除占位符以外的所有物品类型</para>
/// </summary>
public void AllowItemTypesExceptPlaceholder()
{
var itemTypeType = typeof(Config.ItemType);
//Get all fields
//获取所有字段
var fields = itemTypeType.GetFields(BindingFlags.Public | BindingFlags.Static);
//Traversal field
//遍历字段
foreach (var field in fields)
{
//Gets the value of the field
//获取字段的值
var value = field.GetValue(null);
if (value == null)
{
continue;
}
var intValue = (int)value;
if (intValue == Config.ItemType.Placeholder)
{
continue;
}
_allowedItemTypes.Add(intValue);
}
}
public void AllowAddingItemByType(int itemType)
{
_allowedItemTypes.Add(itemType);
}
public void DisallowAddingItemByType(int itemType)
{
_allowedItemTypes.Remove(itemType);
}
public bool CanAddItem(IItem item)
{
if (item.SelfItemContainer != null && !CanContainContainer)
if (!_allowedItemTypes.Contains(item.ItemType))
{
//The item to be added can hold other items, and this item container does not allow item containers.
//要添加的物品能够容纳其他物品,且此物品容器不允许放置物品容器。
return false;
}
//If the capacity is not full, directly return to add items
@ -200,8 +245,6 @@ public class UniversalItemContainer(int totalCapacity) : IItemContainer
}
public bool SupportSelect { get; set; }
public bool CanContainContainer { get; set; }
public IItem GetPlaceHolderItem(int index)
{
@ -247,7 +290,7 @@ public class UniversalItemContainer(int totalCapacity) : IItemContainer
public bool CanReplaceItem(int index, IItem item)
{
if (item.SelfItemContainer != null && !CanContainContainer)
if (!_allowedItemTypes.Contains(item.ItemType))
{
return false;
}

View File

@ -0,0 +1,15 @@
namespace ColdMint.scripts.pickable;
/// <summary>
/// <para>法术</para>
/// </summary>
/// <remarks>
///<para>用于抛射体武器</para>
/// </remarks>
public partial class MagicPickAble : PickAbleTemplate
{
public override int ItemType
{
get => Config.ItemType.Magic;
}
}

View File

@ -92,6 +92,10 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
public bool Picked { get; set; }
public int MaxQuantity { get; set; } = 1;
public virtual int ItemType
{
get => Config.ItemType.Unknown;
}
private bool _isSelected;
@ -108,7 +112,7 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
OnSelectChange(value);
}
}
public IItemContainer? ItemContainer { get; set; }
public IItemContainer? SelfItemContainer { get; set; }
@ -121,7 +125,7 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
/// <param name="isSelected"></param>
protected virtual void OnSelectChange(bool isSelected)
{
}
public IItem? CreateItem(int number)
@ -348,11 +352,4 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
pickAbleTemplate.Id = Id;
}
public virtual void Destroy()
{
QueueFree();
}
public bool CanStackWith(IItem item) => false;
}

View File

@ -0,0 +1,14 @@
namespace ColdMint.scripts.projectile;
/// <summary>
/// <para>Magic</para>
/// <para>法术</para>
/// </summary>
/// <remarks>
///<para>For projectile weapons</para>
///<para>用于抛射体武器</para>
/// </remarks>
public interface IMagic
{
}

View File

@ -1,7 +1,5 @@
using ColdMint.scripts.debug;
using ColdMint.scripts.projectile;
using ColdMint.scripts.projectile.decorator;
using ColdMint.scripts.utils;
using ColdMint.scripts.inventory;
using Godot;
namespace ColdMint.scripts.weapon;
@ -21,106 +19,28 @@ public partial class ProjectileWeapon : WeaponTemplate
/// <para>抛射体的生成位置</para>
/// </summary>
private Marker2D? _marker2D;
/// <summary>
/// <para>Scattering radians</para>
/// <para>散射弧度</para>
/// </summary>
[Export] protected float OffsetAngle;
/// <summary>
/// <para>Offset angle mode</para>
/// <para>偏移角度模式</para>
/// </summary>
[Export] protected int OffsetAngleMode = Config.OffsetAngleMode.Random;
/// <summary>
/// <para>Whether the last offset angle is positive</para>
/// <para>上次的偏移角度是否为正向的</para>
/// </summary>
private bool _positiveOffsetAngle = true;
/// <summary>
/// <para>The number of projectiles fired at once</para>
/// <para>一次可以发射多少个子弹</para>
/// </summary>
[Export] protected float NumberOfProjectiles = 1;
[Export] protected PackedScene[] ProjectileScenes { get; set; } = [];
/// <summary>
/// <para>Whether to launch in the order of the projectile list</para>
/// <para>是否按照抛射体列表的循序发射</para>
/// </summary>
[Export]
protected bool Sequentially { get; set; }
private int _projectileIndex;
/// <summary>
/// <para>Number of slots for ranged weapons</para>
/// <para>远程武器的槽位数量</para>
/// </summary>
[Export] public int NumberSlots { get; set; }
public override int ItemType
{
get => Config.ItemType.ProjectileWeapon;
}
public override void _Ready()
{
base._Ready();
_marker2D = GetNode<Marker2D>("Marker2D");
SelfItemContainer = new UniversalItemContainer(NumberSlots);
SelfItemContainer.AllowAddingItemByType(Config.ItemType.Magic);
}
/// <summary>
/// <para>GetNextProjectileScene</para>
/// <para>获取下一个抛射体</para>
/// </summary>
/// <returns></returns>
private PackedScene GetNextProjectileScene()
{
if (Sequentially)
{
_projectileIndex = (_projectileIndex + 1) % ProjectileScenes.Length;
return ProjectileScenes[_projectileIndex];
}
else
{
return ProjectileScenes[RandomUtils.Instance.Next(ProjectileScenes.Length)];
}
}
/// <summary>
/// <para>GetRandomAngle</para>
/// <para>获取随机的偏移弧度</para>
/// </summary>
/// <returns></returns>
private float GetRandomAngle()
{
if (OffsetAngle == 0)
{
//If the offset angle is 0, then return 0
//弧度为0,不用偏移。
return 0;
}
if (OffsetAngleMode == Config.OffsetAngleMode.Cross)
{
float result;
if (_positiveOffsetAngle)
{
result = -OffsetAngle / 2;
}
else
{
result = OffsetAngle / 2;
}
_positiveOffsetAngle = !_positiveOffsetAngle;
return result;
}
if (OffsetAngleMode == Config.OffsetAngleMode.AlwaysSame)
{
return OffsetAngle;
}
var min = -OffsetAngle / 2;
return min + RandomUtils.Instance.NextSingle() * OffsetAngle;
}
protected override void DoFire(Node2D? owner, Vector2 enemyGlobalPosition)
{
@ -141,39 +61,6 @@ public partial class ProjectileWeapon : WeaponTemplate
LogCat.LogError("projectile_container_is_null");
return;
}
//Empty list check
//空列表检查
if (ProjectileScenes is [])
{
LogCat.LogError("projectiles_is_empty");
return;
}
//Get the first projectile
//获取第一个抛射体
var projectileScene = GetNextProjectileScene();
for (int i = 0; i < NumberOfProjectiles; i++)
{
var projectile = NodeUtils.InstantiatePackedScene<Projectile>(projectileScene);
if (projectile == null) return;
if (Config.IsDebug())
{
var nodeSpawnOnKillCharacterDecorator = new NodeSpawnOnKillCharacterDecorator
{
DefaultParentNode = this,
PackedScenePath = "res://prefab/entitys/BlackenedAboriginalWarrior.tscn"
};
projectile.AddProjectileDecorator(nodeSpawnOnKillCharacterDecorator);
}
NodeUtils.CallDeferredAddChild(GameSceneDepend.ProjectileContainer, projectile);
projectile.Owner = owner;
projectile.TargetNode = GameSceneDepend.TemporaryTargetNode;
projectile.Velocity =
(_marker2D.GlobalPosition.DirectionTo(enemyGlobalPosition) * projectile.Speed)
.Rotated(GetRandomAngle());
projectile.Position = _marker2D.GlobalPosition;
}
}
}