Compare commits
30 Commits
2c6f8804ba
...
bfc5ee6ac9
Author | SHA1 | Date | |
---|---|---|---|
bfc5ee6ac9 | |||
4a4d4a0c37 | |||
4e83bca579 | |||
6d9b9ca7ec | |||
3f96519ac0 | |||
407798b13b | |||
e718d78137 | |||
b7805c5804 | |||
839d60b62a | |||
8a7a9bcf5d | |||
124c6ce0ba | |||
2d7985010d | |||
bb0f582fed | |||
81344faa3e | |||
77f2bac36e | |||
67706ca9d7 | |||
bfec07a0de | |||
c1c3fce58a | |||
2d92a92faf | |||
31a1d292d8 | |||
4f2208bd60 | |||
617164a4bc | |||
efff63ddd4 | |||
f881d43c3b | |||
b53f5bc2df | |||
b9a9349596 | |||
b7c3651462 | |||
63680a9410 | |||
234241b74a | |||
32299877c6 |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,6 +3,9 @@
|
||||||
.idea/
|
.idea/
|
||||||
.vs/
|
.vs/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.fleet/
|
||||||
|
# Ignore the Android build directory
|
||||||
|
android/
|
||||||
export_presets.cfg
|
export_presets.cfg
|
||||||
*.translation
|
*.translation
|
||||||
*.user
|
*.user
|
||||||
|
|
14
data/itemRegs/magics.yaml
Normal file
14
data/itemRegs/magics.yaml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#Register spells used by the projectile here.
|
||||||
|
#在这里注册抛射体使用的法术。
|
||||||
|
#Note: The id must be the same as the item id in the scene. Otherwise, an ArgumentException will be thrown.
|
||||||
|
#备注:id必须和场景内的物品id保持一致。否则会抛出ArgumentException。
|
||||||
|
#After you declare the id of the item, add the corresponding localized text to the csv file in the locals' folder. For example, if the id is a corresponding name is item_a and the corresponding description is item_a_desc.
|
||||||
|
#当您声明物品的id后,请在locals文件夹中的csv文件中添加相应的本地化文本。例如:id为a,则对应的名称为item_a,对应的描述为item_a_desc。
|
||||||
|
- id: necromancy
|
||||||
|
scene_path: res://prefab/magics/curseOfTheUndead.tscn
|
||||||
|
icon_path: res://sprites/projectile/curseOfTheUndead.png
|
||||||
|
max_stack_value: 1
|
||||||
|
- id: x3
|
||||||
|
scene_path: res://prefab/magics/x3.tscn
|
||||||
|
icon_path: res://sprites/projectile/x3.png
|
||||||
|
max_stack_value: 1
|
|
@ -2,4 +2,8 @@ id,zh,en,ja
|
||||||
item_staff_necromancy,死灵法杖,staffNecromancy,ネクロポリスの杖です
|
item_staff_necromancy,死灵法杖,staffNecromancy,ネクロポリスの杖です
|
||||||
item_staff_necromancy_desc,发射诅咒,可将敌人转化为邪恶的怪物。,Cast a curse that transforms enemies into evil monsters.,呪いを発射して、敵を邪悪な怪物に変えることができます。
|
item_staff_necromancy_desc,发射诅咒,可将敌人转化为邪恶的怪物。,Cast a curse that transforms enemies into evil monsters.,呪いを発射して、敵を邪悪な怪物に変えることができます。
|
||||||
item_portable_backpacks,便携式背包,PortableBackpacks,ポータブルバックパック
|
item_portable_backpacks,便携式背包,PortableBackpacks,ポータブルバックパック
|
||||||
item_portable_backpacks_desc,为玩家提供9个物品槽。,Provides 9 item slots for the player.,プレイヤーに9つのアイテムスロットを提供します。
|
item_portable_backpacks_desc,为玩家提供9个物品槽。,Provides 9 item slots for the player.,プレイヤーに9つのアイテムスロットを提供します。
|
||||||
|
item_necromancy,死灵法术,necromancy,ネクロマンシー
|
||||||
|
item_necromancy_desc,法术的实体化弹丸。,The materialized projectile of a spell.,術の実体化した弾丸です。
|
||||||
|
item_x3,三重射击法术,Triple shot spell,三重射撃術です
|
||||||
|
item_x3_desc,使发射器一次射出三颗弹丸。,Make the launcher shoot three pellets at a time.,発射機から一度に三つの弾丸を発射させます。
|
|
|
@ -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_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_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_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_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_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.,マップ生成器に起点部屋データはありません。
|
log_map_generator_has_no_starting_room_data,地图生成器没有起点房间数据。,Map generator has no starting room data.,マップ生成器に起点部屋データはありません。
|
||||||
|
@ -51,13 +50,7 @@ log_patrol_to_next_point,下一个点{0},当前位置{1},偏移量{2},距
|
||||||
log_patrol_arrival_point,到达巡逻点{0}。,Arrival at patrol point {0}.,巡回ポイント{0}に到着します。
|
log_patrol_arrival_point,到达巡逻点{0}。,Arrival at patrol point {0}.,巡回ポイント{0}に到着します。
|
||||||
log_patrol_origin_position,巡逻路径的起始位置是{0}。,The starting position of the patrol path is {0}.,巡回路の開始位置は{0}です。
|
log_patrol_origin_position,巡逻路径的起始位置是{0}。,The starting position of the patrol path is {0}.,巡回路の開始位置は{0}です。
|
||||||
log_patrol_not_on_floor,不能将初始路径设置在空中。,The initial path cannot be set in the air.,初期パスを空中に設定できません。
|
log_patrol_not_on_floor,不能将初始路径设置在空中。,The initial path cannot be set in the air.,初期パスを空中に設定できません。
|
||||||
log_item_container_is_null,物品容器为空。,Item container is null.,アイテム・コンテナが空です。
|
|
||||||
log_can_add_item,可以添加物品{0}。,Can add item {0}.,アイテム{0}を追加できます。
|
log_can_add_item,可以添加物品{0}。,Can add item {0}.,アイテム{0}を追加できます。
|
||||||
log_backpack_not_allowed,不允许添加到背包。,Not allowed to add to backpack.,バックパックに追加することは許可されていません。
|
|
||||||
log_item_is_null,物品为空。,Item is null.,アイテムが空です。
|
|
||||||
log_item_id_not_same,物品ID不同。,Item ID is different.,アイテムIDが異なります。
|
|
||||||
log_max_quantity_exceeded,超过最大数量。,Exceeded maximum quantity.,最大数量を超えました。
|
|
||||||
log_item_slot_is_selected_and_not_allowed,已选择物品槽,不允许添加。,"Item slot is selected, not allowed to add.",アイテムスロットが選択されており、追加は許可されていません。
|
|
||||||
log_patrol_enemy_detected,检测到敌人。,Enemy detected.,敵を検出しました。
|
log_patrol_enemy_detected,检测到敌人。,Enemy detected.,敵を検出しました。
|
||||||
log_attacker_or_target_is_null,攻击者或目标为空。,Attacker or target is null.,攻撃者またはターゲットが空です。
|
log_attacker_or_target_is_null,攻击者或目标为空。,Attacker or target is null.,攻撃者またはターゲットが空です。
|
||||||
log_in_the_same_camp,在同一阵营。,In the same camp.,同じ陣営です。
|
log_in_the_same_camp,在同一阵营。,In the same camp.,同じ陣営です。
|
||||||
|
@ -118,4 +111,9 @@ log_hide_all_node,隐藏{0}个节点。,Hide {0} nodes.,{0}ノードを非表示
|
||||||
log_show_all_node,显示{0}个节点。,Show {0} nodes.,{0}ノードが表示されます。
|
log_show_all_node,显示{0}个节点。,Show {0} nodes.,{0}ノードが表示されます。
|
||||||
log_enter_the_screen,进入屏幕。,Enter screen,画面に移動。
|
log_enter_the_screen,进入屏幕。,Enter screen,画面に移動。
|
||||||
log_exit_the_screen,退出屏幕。,Exit screen,画面を終了します。
|
log_exit_the_screen,退出屏幕。,Exit screen,画面を終了します。
|
||||||
log_failed_to_create_room_preview,创建{0}的房间预览图失败。,Failed to create a room preview of the {0}.,{0}の部屋のプレビューを作成できませんでした。
|
log_failed_to_create_room_preview,创建{0}的房间预览图失败。,Failed to create a room preview of the {0}.,{0}の部屋のプレビューを作成できませんでした。
|
||||||
|
log_generated_item_is_empty,生成的物品{0}是空的吗{1}。,Generated item {0} is empty {1}.,生成したアイテム{0}は空ですか{1}。
|
||||||
|
log_projectile_generate_magic_is_null,没有装填可提供抛射体的法术。,There is no reload spell that provides projectiles.,射出体を提供するスペルを装填していません。
|
||||||
|
log_projectile_scene_is_null,抛射体场景为空。,Projectile scene is empty.,射出体は空です。
|
||||||
|
log_projectile_is_null,抛射体为空。,Projectile scene is empty.,射出シーンは空です。
|
||||||
|
log_projectile_weapon_range,加载法术范围{0}顺序模式吗{1}上次发射法术时采用的索引{2}。,Load spell range {0} Sequential mode {1} Index used when the spell was last fired {2}.,スペル範囲{0}順序モードですか{1}前回スペルを送信した時のインデックス{2}。
|
|
|
@ -4,3 +4,8 @@ slogan_1,Kawaii!,Kawaii!,Kawaii!
|
||||||
slogan_2,魔法是想象的世界。,Magic is an imaginary world.,魔法は想像の世界です。
|
slogan_2,魔法是想象的世界。,Magic is an imaginary world.,魔法は想像の世界です。
|
||||||
slogan_3,也试试《Minecraft》!,Also try 'Minecraft'!,「Minecraft」もやってみて!
|
slogan_3,也试试《Minecraft》!,Also try 'Minecraft'!,「Minecraft」もやってみて!
|
||||||
slogan_4,也试试《Terraria》!,Also try 'Terraria'!,「Terraria」もやってみて!
|
slogan_4,也试试《Terraria》!,Also try 'Terraria'!,「Terraria」もやってみて!
|
||||||
|
slogan_5,也试试《Terraria》!,Also try 'Terraria'!,「Terraria」もやってみて!
|
||||||
|
slogan_6,游戏在bug上运行。,The game runs on a bug.,ゲームはバグで動作します。
|
||||||
|
slogan_7,こんにちは!,こんにちは!,こんにちは!
|
||||||
|
slogan_7,你好!,你好!,你好!
|
||||||
|
slogan_7,Hello!,Hello!,Hello!
|
|
|
@ -34,3 +34,4 @@ ui_character_voice,角色配音,character_voice,キャラクターボイスで
|
||||||
ui_translator,翻译,translator,翻訳
|
ui_translator,翻译,translator,翻訳
|
||||||
ui_unordered_list_tip,排名不分先后,Ranking is not in order,順位は関係ありません
|
ui_unordered_list_tip,排名不分先后,Ranking is not in order,順位は関係ありません
|
||||||
ui_loading,正在加载...,Loading...,読み込み中...
|
ui_loading,正在加载...,Loading...,読み込み中...
|
||||||
|
ui_spell_editor,法术编辑器,Spell editor,スペルエディター
|
||||||
|
|
|
|
@ -1,28 +0,0 @@
|
||||||
[gd_scene load_steps=5 format=3 uid="uid://djsh4unystlf0"]
|
|
||||||
|
|
||||||
[ext_resource type="Texture2D" uid="uid://qowlv0viyqbb" path="res://sprites/ui/Null.png" id="1_346je"]
|
|
||||||
[ext_resource type="Script" path="res://scripts/furniture/GuiFurniture.cs" id="1_t1qdg"]
|
|
||||||
|
|
||||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_vx4tg"]
|
|
||||||
size = Vector2(31, 31)
|
|
||||||
|
|
||||||
[sub_resource type="CircleShape2D" id="CircleShape2D_in1f7"]
|
|
||||||
radius = 52.6118
|
|
||||||
|
|
||||||
[node name="RigidBody2D" type="RigidBody2D"]
|
|
||||||
collision_layer = 256
|
|
||||||
collision_mask = 160
|
|
||||||
script = ExtResource("1_t1qdg")
|
|
||||||
|
|
||||||
[node name="Null" type="Sprite2D" parent="."]
|
|
||||||
texture = ExtResource("1_346je")
|
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
|
||||||
position = Vector2(-0.5, -0.5)
|
|
||||||
shape = SubResource("RectangleShape2D_vx4tg")
|
|
||||||
|
|
||||||
[node name="OperateArea2D" type="Area2D" parent="."]
|
|
||||||
visible = false
|
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="OperateArea2D"]
|
|
||||||
shape = SubResource("CircleShape2D_in1f7")
|
|
110
prefab/furnitures/SpellEditor.tscn
Normal file
110
prefab/furnitures/SpellEditor.tscn
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
[gd_scene load_steps=16 format=3 uid="uid://djsh4unystlf0"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scripts/furniture/GuiFurniture.cs" id="1_t1qdg"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://cyciw4drjvrs8" path="res://sprites/furnitures/SpellEditor.png" id="2_sbshw"]
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_vx4tg"]
|
||||||
|
size = Vector2(31, 31)
|
||||||
|
|
||||||
|
[sub_resource type="CircleShape2D" id="CircleShape2D_in1f7"]
|
||||||
|
radius = 52.6118
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_yq6e3"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(0, 0, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_ljyfv"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(44, 0, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_46j2i"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(88, 0, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_q6a2v"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(132, 0, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_lyh2i"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(0, 43, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_84sml"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(44, 43, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_xncbb"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(88, 43, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_eaqhr"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(132, 43, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_bcdl4"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(0, 86, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_a4mgy"]
|
||||||
|
atlas = ExtResource("2_sbshw")
|
||||||
|
region = Rect2(44, 86, 44, 43)
|
||||||
|
|
||||||
|
[sub_resource type="SpriteFrames" id="SpriteFrames_mppe5"]
|
||||||
|
animations = [{
|
||||||
|
"frames": [{
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_yq6e3")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_ljyfv")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_46j2i")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_q6a2v")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_lyh2i")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_84sml")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_xncbb")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_eaqhr")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_bcdl4")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_a4mgy")
|
||||||
|
}],
|
||||||
|
"loop": true,
|
||||||
|
"name": &"default",
|
||||||
|
"speed": 5.0
|
||||||
|
}]
|
||||||
|
|
||||||
|
[node name="RigidBody2D" type="RigidBody2D"]
|
||||||
|
collision_layer = 256
|
||||||
|
collision_mask = 160
|
||||||
|
script = ExtResource("1_t1qdg")
|
||||||
|
Path = "res://prefab/ui/SpellEditorUI.tscn"
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||||
|
position = Vector2(-0.5, -0.5)
|
||||||
|
shape = SubResource("RectangleShape2D_vx4tg")
|
||||||
|
|
||||||
|
[node name="OperateArea2D" type="Area2D" parent="."]
|
||||||
|
visible = false
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="OperateArea2D"]
|
||||||
|
shape = SubResource("CircleShape2D_in1f7")
|
||||||
|
|
||||||
|
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
|
||||||
|
scale = Vector2(0.7, 0.7)
|
||||||
|
sprite_frames = SubResource("SpriteFrames_mppe5")
|
||||||
|
autoplay = "default"
|
||||||
|
frame_progress = 0.717769
|
44
prefab/magics/curseOfTheUndead.tscn
Normal file
44
prefab/magics/curseOfTheUndead.tscn
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
[gd_scene load_steps=6 format=3 uid="uid://crthy8a50a4t"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scripts/spell/SpellPickAble.cs" id="1_v0grp"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://cak6chjjsu7wo" path="res://sounds/fire.wav" id="4_ffr2k"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bbcjkyrsx88av" path="res://sprites/projectile/curseOfTheUndead.png" id="4_y6nkf"]
|
||||||
|
|
||||||
|
[sub_resource type="CircleShape2D" id="CircleShape2D_qgdry"]
|
||||||
|
|
||||||
|
[sub_resource type="CircleShape2D" id="CircleShape2D_akp3k"]
|
||||||
|
|
||||||
|
[node name="curseOfTheUndead" type="RigidBody2D"]
|
||||||
|
collision_layer = 8
|
||||||
|
collision_mask = 34
|
||||||
|
angular_damp = -1.0
|
||||||
|
script = ExtResource("1_v0grp")
|
||||||
|
_projectilePath = "res://prefab/projectile/curseOfTheUndead.tscn"
|
||||||
|
|
||||||
|
[node name="DamageArea2D" type="Area2D" parent="."]
|
||||||
|
collision_layer = 8
|
||||||
|
collision_mask = 102
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="DamageArea2D"]
|
||||||
|
position = Vector2(0, -0.5)
|
||||||
|
shape = SubResource("CircleShape2D_qgdry")
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||||
|
position = Vector2(0, -0.625)
|
||||||
|
shape = SubResource("CircleShape2D_akp3k")
|
||||||
|
|
||||||
|
[node name="Marker2D" type="Marker2D" parent="."]
|
||||||
|
position = Vector2(65, 0)
|
||||||
|
|
||||||
|
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Marker2D"]
|
||||||
|
stream = ExtResource("4_ffr2k")
|
||||||
|
bus = &"SoundEffect"
|
||||||
|
|
||||||
|
[node name="TipLabel" type="Label" parent="."]
|
||||||
|
offset_left = -19.0
|
||||||
|
offset_top = 23.0
|
||||||
|
offset_right = 21.0
|
||||||
|
offset_bottom = 48.0
|
||||||
|
|
||||||
|
[node name="CurseOfTheUndead" type="Sprite2D" parent="."]
|
||||||
|
texture = ExtResource("4_y6nkf")
|
46
prefab/magics/x3.tscn
Normal file
46
prefab/magics/x3.tscn
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
[gd_scene load_steps=6 format=3 uid="uid://cg75t3fw5c6er"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scripts/spell/MultipleFireSpell.cs" id="1_cnhod"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://mb5yijtw7sw5" path="res://sprites/projectile/x3.png" id="3_b3s8h"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://cak6chjjsu7wo" path="res://sounds/fire.wav" id="4_ffr2k"]
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_3eq4k"]
|
||||||
|
size = Vector2(30, 30)
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_i3lbq"]
|
||||||
|
size = Vector2(30, 31)
|
||||||
|
|
||||||
|
[node name="x3" type="RigidBody2D"]
|
||||||
|
collision_layer = 8
|
||||||
|
collision_mask = 34
|
||||||
|
angular_damp = -1.0
|
||||||
|
script = ExtResource("1_cnhod")
|
||||||
|
|
||||||
|
[node name="DamageArea2D" type="Area2D" parent="."]
|
||||||
|
collision_layer = 8
|
||||||
|
collision_mask = 102
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="DamageArea2D"]
|
||||||
|
shape = SubResource("RectangleShape2D_3eq4k")
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||||
|
position = Vector2(0, -0.5)
|
||||||
|
shape = SubResource("RectangleShape2D_i3lbq")
|
||||||
|
|
||||||
|
[node name="Marker2D" type="Marker2D" parent="."]
|
||||||
|
position = Vector2(65, 0)
|
||||||
|
|
||||||
|
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Marker2D"]
|
||||||
|
stream = ExtResource("4_ffr2k")
|
||||||
|
bus = &"SoundEffect"
|
||||||
|
|
||||||
|
[node name="TipLabel" type="Label" parent="."]
|
||||||
|
offset_left = -19.0
|
||||||
|
offset_top = 23.0
|
||||||
|
offset_right = 21.0
|
||||||
|
offset_bottom = 48.0
|
||||||
|
|
||||||
|
[node name="CurseOfTheUndead" type="Sprite2D" parent="."]
|
||||||
|
|
||||||
|
[node name="X3" type="Sprite2D" parent="."]
|
||||||
|
texture = ExtResource("3_b3s8h")
|
|
@ -15,7 +15,6 @@ _maxDamage = 10
|
||||||
_minDamage = 1
|
_minDamage = 1
|
||||||
_damageType = 2
|
_damageType = 2
|
||||||
Speed = 500.0
|
Speed = 500.0
|
||||||
_enableTracking = true
|
|
||||||
_targetDiesDestroyProjectile = true
|
_targetDiesDestroyProjectile = true
|
||||||
_repelStrength = 2.5
|
_repelStrength = 2.5
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
[ext_resource type="Script" path="res://scripts/map/PlayerSpawn.cs" id="2_6p8mv"]
|
[ext_resource type="Script" path="res://scripts/map/PlayerSpawn.cs" id="2_6p8mv"]
|
||||||
[ext_resource type="Script" path="res://scripts/map/ItemSpawn.cs" id="3_v1tlc"]
|
[ext_resource type="Script" path="res://scripts/map/ItemSpawn.cs" id="3_v1tlc"]
|
||||||
[ext_resource type="Texture2D" uid="uid://drw45jlmfo0su" path="res://sprites/light/White_100.png" id="5_4pssd"]
|
[ext_resource type="Texture2D" uid="uid://drw45jlmfo0su" path="res://sprites/light/White_100.png" id="5_4pssd"]
|
||||||
[ext_resource type="PackedScene" uid="uid://djsh4unystlf0" path="res://prefab/furnitures/MagicSeparator.tscn" id="5_7c8bh"]
|
[ext_resource type="PackedScene" uid="uid://djsh4unystlf0" path="res://prefab/furnitures/SpellEditor.tscn" id="5_7c8bh"]
|
||||||
|
|
||||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
|
||||||
size = Vector2(507, 251)
|
size = Vector2(507, 251)
|
||||||
|
@ -45,6 +45,21 @@ position = Vector2(142, 84)
|
||||||
script = ExtResource("3_v1tlc")
|
script = ExtResource("3_v1tlc")
|
||||||
ItemId = "staff_necromancy"
|
ItemId = "staff_necromancy"
|
||||||
|
|
||||||
|
[node name="ItemMarker2D3" type="Marker2D" parent="."]
|
||||||
|
position = Vector2(214, 83)
|
||||||
|
script = ExtResource("3_v1tlc")
|
||||||
|
ItemId = "x3"
|
||||||
|
|
||||||
|
[node name="ItemMarker2D4" type="Marker2D" parent="."]
|
||||||
|
position = Vector2(366, 90)
|
||||||
|
script = ExtResource("3_v1tlc")
|
||||||
|
ItemId = "necromancy"
|
||||||
|
|
||||||
|
[node name="ItemMarker2D2" type="Marker2D" parent="."]
|
||||||
|
position = Vector2(321, 118)
|
||||||
|
script = ExtResource("3_v1tlc")
|
||||||
|
ItemId = "necromancy"
|
||||||
|
|
||||||
[node name="NavigationRegion2D" type="NavigationRegion2D" parent="."]
|
[node name="NavigationRegion2D" type="NavigationRegion2D" parent="."]
|
||||||
navigation_polygon = SubResource("NavigationPolygon_064c7")
|
navigation_polygon = SubResource("NavigationPolygon_064c7")
|
||||||
|
|
||||||
|
|
53
prefab/ui/SpellEditorUI.tscn
Normal file
53
prefab/ui/SpellEditorUI.tscn
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
[gd_scene load_steps=3 format=3 uid="uid://h7lvaqqlsi4t"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scripts/loader/uiLoader/SpellEditorUi.cs" id="1_1pxjs"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://d2i4udh0hho41" path="res://prefab/ui/ItemSlot.tscn" id="2_3ut57"]
|
||||||
|
|
||||||
|
[node name="SpellEditorUi" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
script = ExtResource("1_1pxjs")
|
||||||
|
|
||||||
|
[node name="TitleLabel" type="Label" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 10
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_bottom = 25.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
text = "ui_spell_editor"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="ExitButton" type="Button" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -91.0
|
||||||
|
offset_top = 11.0
|
||||||
|
offset_right = -24.0
|
||||||
|
offset_bottom = 44.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
text = "ui_close"
|
||||||
|
|
||||||
|
[node name="HFlowContainer" type="HFlowContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 10
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = 32.0
|
||||||
|
offset_top = 127.0
|
||||||
|
offset_right = -20.0
|
||||||
|
offset_bottom = 629.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="ItemSlot" parent="." instance=ExtResource("2_3ut57")]
|
||||||
|
layout_mode = 1
|
||||||
|
offset_left = 34.0
|
||||||
|
offset_top = 73.0
|
||||||
|
offset_right = 72.0
|
||||||
|
offset_bottom = 111.0
|
|
@ -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="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="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="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"]
|
[ext_resource type="AudioStream" uid="uid://cak6chjjsu7wo" path="res://sounds/fire.wav" id="4_ffr2k"]
|
||||||
|
|
||||||
|
@ -17,9 +16,8 @@ collision_layer = 8
|
||||||
collision_mask = 34
|
collision_mask = 34
|
||||||
angular_damp = -1.0
|
angular_damp = -1.0
|
||||||
script = ExtResource("1_w8hhv")
|
script = ExtResource("1_w8hhv")
|
||||||
OffsetAngle = 0.087
|
_numberSlots = 5
|
||||||
ProjectileScenes = [ExtResource("2_mwli5")]
|
_fireSequentially = true
|
||||||
Sequentially = true
|
|
||||||
FiringIntervalAsMillisecond = 300
|
FiringIntervalAsMillisecond = 300
|
||||||
_recoilStrength = 5
|
_recoilStrength = 5
|
||||||
UniqueIcon = ExtResource("3_31iau")
|
UniqueIcon = ExtResource("3_31iau")
|
||||||
|
|
|
@ -84,13 +84,16 @@ text = "ui_re_create_map"
|
||||||
|
|
||||||
[node name="SeedLabel" type="Label" parent="CanvasLayer/Control"]
|
[node name="SeedLabel" type="Label" parent="CanvasLayer/Control"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 5
|
anchors_preset = 3
|
||||||
anchor_left = 0.5
|
anchor_left = 1.0
|
||||||
anchor_right = 0.5
|
anchor_top = 1.0
|
||||||
offset_left = -20.0
|
anchor_right = 1.0
|
||||||
offset_right = 20.0
|
anchor_bottom = 1.0
|
||||||
offset_bottom = 25.0
|
offset_left = -45.0
|
||||||
grow_horizontal = 2
|
offset_top = -16.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 0
|
||||||
|
theme_override_font_sizes/font_size = 10
|
||||||
|
|
||||||
[node name="MapContainer" parent="CanvasLayer/Control" instance=ExtResource("6_ljdj4")]
|
[node name="MapContainer" parent="CanvasLayer/Control" instance=ExtResource("6_ljdj4")]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
|
@ -115,6 +118,8 @@ visible = false
|
||||||
|
|
||||||
[node name="WeaponContainer" type="Node2D" parent="."]
|
[node name="WeaponContainer" type="Node2D" parent="."]
|
||||||
|
|
||||||
|
[node name="SpellContainer" type="Node2D" parent="."]
|
||||||
|
|
||||||
[node name="PlayerContainer" type="Node2D" parent="."]
|
[node name="PlayerContainer" type="Node2D" parent="."]
|
||||||
|
|
||||||
[node name="AICharacterContainer" type="Node2D" parent="."]
|
[node name="AICharacterContainer" type="Node2D" parent="."]
|
||||||
|
|
|
@ -171,6 +171,12 @@ public static class Config
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public const string DefaultVersionName = "Default";
|
public const string DefaultVersionName = "Default";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>EmptyVariant</para>
|
||||||
|
/// <para>空变量</para>
|
||||||
|
/// </summary>
|
||||||
|
public static readonly Variant EmptyVariant = new();
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>IsDebug</para>
|
/// <para>IsDebug</para>
|
||||||
|
@ -191,6 +197,45 @@ public static class Config
|
||||||
{
|
{
|
||||||
return OS.HasFeature("editor");
|
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>Spell</para>
|
||||||
|
/// <para>法术</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>Type of special item used in Projectile weapons</para>
|
||||||
|
///<para>用于远程武器内的特殊物品类型</para>
|
||||||
|
/// </remarks>
|
||||||
|
public const int Spell = 4;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Room Injector ID</para>
|
/// <para>Room Injector ID</para>
|
||||||
|
@ -210,6 +255,49 @@ public static class Config
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string TimeInterval = "TimeInterval";
|
public const string TimeInterval = "TimeInterval";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ZIndexManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Floating icon</para>
|
||||||
|
/// <para>悬浮图标</para>
|
||||||
|
/// </summary>
|
||||||
|
public const int FloatingIcon = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Item data changes the event type</para>
|
||||||
|
/// <para>物品数据改变事件类型</para>
|
||||||
|
/// </summary>
|
||||||
|
public enum ItemDataChangeEventType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para>add</para>
|
||||||
|
/// <para>添加</para>
|
||||||
|
/// </summary>
|
||||||
|
Add,
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Quantity Added</para>
|
||||||
|
/// <para>物品数量增加</para>
|
||||||
|
/// </summary>
|
||||||
|
QuantityAdded,
|
||||||
|
/// <summary>
|
||||||
|
/// <para>remove</para>
|
||||||
|
/// <para>移除</para>
|
||||||
|
/// </summary>
|
||||||
|
Remove,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Replace</para>
|
||||||
|
/// <para>被替换</para>
|
||||||
|
/// </summary>
|
||||||
|
Replace,
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Clear</para>
|
||||||
|
/// <para>被清空</para>
|
||||||
|
/// </summary>
|
||||||
|
Clear
|
||||||
|
}
|
||||||
|
|
||||||
public enum OsEnum
|
public enum OsEnum
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,6 +48,12 @@ public static class GameSceneDepend
|
||||||
/// <para>抛射体容器</para>
|
/// <para>抛射体容器</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Node2D? ProjectileContainer { get; set; }
|
public static Node2D? ProjectileContainer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>SpellContainer</para>
|
||||||
|
/// <para>法术容器</para>
|
||||||
|
/// </summary>
|
||||||
|
public static Node2D? SpellContainer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>WeaponContainer</para>
|
/// <para>WeaponContainer</para>
|
||||||
|
|
|
@ -179,9 +179,8 @@ public sealed partial class AiCharacter : CharacterTemplate
|
||||||
|
|
||||||
//You must create an item container for the character before you can pick up the item.
|
//You must create an item container for the character before you can pick up the item.
|
||||||
//必须为角色创建物品容器后才能拾起物品。
|
//必须为角色创建物品容器后才能拾起物品。
|
||||||
var universalItemContainer = new UniversalItemContainer();
|
var universalItemContainer = new UniversalItemContainer(1);
|
||||||
var itemSlotNode = universalItemContainer.AddItemSlot(this);
|
universalItemContainer.AllowAddingItemByType(Config.ItemType.ProjectileWeapon);
|
||||||
itemSlotNode?.Hide();
|
|
||||||
ProtectedItemContainer = universalItemContainer;
|
ProtectedItemContainer = universalItemContainer;
|
||||||
//Add initial weapon
|
//Add initial weapon
|
||||||
//添加初始武器
|
//添加初始武器
|
||||||
|
|
|
@ -9,6 +9,7 @@ using ColdMint.scripts.inventory;
|
||||||
using ColdMint.scripts.utils;
|
using ColdMint.scripts.utils;
|
||||||
using ColdMint.scripts.loot;
|
using ColdMint.scripts.loot;
|
||||||
using ColdMint.scripts.pickable;
|
using ColdMint.scripts.pickable;
|
||||||
|
using ColdMint.scripts.weapon;
|
||||||
using Godot;
|
using Godot;
|
||||||
using WeaponTemplate = ColdMint.scripts.weapon.WeaponTemplate;
|
using WeaponTemplate = ColdMint.scripts.weapon.WeaponTemplate;
|
||||||
|
|
||||||
|
@ -413,14 +414,6 @@ public partial class CharacterTemplate : CharacterBody2D
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get the currently selected node
|
|
||||||
//拿到当前选择的节点
|
|
||||||
var selectItemSlotNode = ItemContainer.GetSelectItemSlotNode();
|
|
||||||
if (selectItemSlotNode == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check to see if you can fit the item into the container first.
|
//Check to see if you can fit the item into the container first.
|
||||||
//先检查是否能将物品放入容器。
|
//先检查是否能将物品放入容器。
|
||||||
var canAddItem = ItemContainer.CanAddItem(item);
|
var canAddItem = ItemContainer.CanAddItem(item);
|
||||||
|
@ -431,8 +424,8 @@ public partial class CharacterTemplate : CharacterBody2D
|
||||||
|
|
||||||
//Is it successfully added to the container?
|
//Is it successfully added to the container?
|
||||||
//再检查是否成功的添加到容器内了?
|
//再检查是否成功的添加到容器内了?
|
||||||
var addSuccess = ItemContainer.AddItem(item);
|
var addSuccessNumber = ItemContainer.AddItem(item);
|
||||||
if (!addSuccess)
|
if (addSuccessNumber <= 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -453,8 +446,12 @@ public partial class CharacterTemplate : CharacterBody2D
|
||||||
pickAbleTemplate.Sleeping = true;
|
pickAbleTemplate.Sleeping = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pickAbleItemNode2D is ProjectileWeapon projectileWeapon)
|
||||||
|
{
|
||||||
|
projectileWeapon.UpdateSpellCache();
|
||||||
|
}
|
||||||
|
|
||||||
if (_currentItem == null && selectItemSlotNode.GetItem() == item)
|
if (_currentItem == null && ItemContainer.GetSelectItem() == item)
|
||||||
{
|
{
|
||||||
//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.
|
//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.
|
||||||
//如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。
|
//如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。
|
||||||
|
@ -484,10 +481,10 @@ public partial class CharacterTemplate : CharacterBody2D
|
||||||
|
|
||||||
if (_currentItem is IItem item)
|
if (_currentItem is IItem item)
|
||||||
{
|
{
|
||||||
item.Use(this, position);
|
return item.Use(this, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _Process(double delta)
|
public override void _Process(double delta)
|
||||||
|
@ -723,7 +720,7 @@ public partial class CharacterTemplate : CharacterBody2D
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var len = ItemContainer.GetItemSlotCount();
|
var len = ItemContainer.GetUsedCapacity();
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -762,21 +759,19 @@ public partial class CharacterTemplate : CharacterBody2D
|
||||||
/// </param>
|
/// </param>
|
||||||
protected void ThrowItem(int index, int number, Vector2 velocity)
|
protected void ThrowItem(int index, int number, Vector2 velocity)
|
||||||
{
|
{
|
||||||
var itemSlotNode = ItemContainer?.GetItemSlotNode(index);
|
if (number == 0)
|
||||||
if (itemSlotNode is null) return;
|
|
||||||
if (number < 0)
|
|
||||||
{
|
{
|
||||||
while (!itemSlotNode.IsEmpty())
|
return;
|
||||||
{
|
|
||||||
ThrowOneItem(itemSlotNode, velocity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
var item = ItemContainer?.GetItem(index);
|
||||||
|
if (item is null) return;
|
||||||
|
//Less than 0, throw everything
|
||||||
|
//小于0,扔出所有物品
|
||||||
|
var actualQuantity = number < 0 ? item.Quantity : Math.Min(item.Quantity, number);
|
||||||
|
for (var i = 0; i < actualQuantity; i++)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < number && !itemSlotNode.IsEmpty(); i++)
|
ThrowOneItem(index, item, velocity);
|
||||||
{
|
|
||||||
ThrowOneItem(itemSlotNode, velocity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -784,21 +779,23 @@ public partial class CharacterTemplate : CharacterBody2D
|
||||||
/// <para>Throw item</para>
|
/// <para>Throw item</para>
|
||||||
/// <para>抛出物品</para>
|
/// <para>抛出物品</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="itemSlotNode"></param>
|
/// <param name="index"></param>
|
||||||
|
/// <param name="originalItem"></param>
|
||||||
/// <param name="velocity">
|
/// <param name="velocity">
|
||||||
/// <para>The speed to be applied to the item</para>
|
/// <para>The speed to be applied to the item</para>
|
||||||
/// <para>要施加到物品上的速度</para>
|
/// <para>要施加到物品上的速度</para>
|
||||||
/// </param>
|
/// </param>
|
||||||
private void ThrowOneItem(ItemSlotNode itemSlotNode, Vector2 velocity)
|
private void ThrowOneItem(int index, IItem originalItem, Vector2 velocity)
|
||||||
{
|
{
|
||||||
//Remove the item from the item container
|
//Remove the item from the item container
|
||||||
//从物品容器内取出物品
|
//从物品容器内取出物品
|
||||||
var item = itemSlotNode.CreateItemInstance(1);
|
originalItem.OnThrow(velocity);
|
||||||
|
var item = originalItem.CreateItem(1);
|
||||||
if (item is not Node2D node2D)
|
if (item is not Node2D node2D)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
item.ItemContainer = null;
|
||||||
NodeUtils.CallDeferredAddChild(NodeUtils.FindContainerNode(node2D, GetNode("/root")), node2D);
|
NodeUtils.CallDeferredAddChild(NodeUtils.FindContainerNode(node2D, GetNode("/root")), node2D);
|
||||||
switch (item)
|
switch (item)
|
||||||
{
|
{
|
||||||
|
@ -847,7 +844,8 @@ public partial class CharacterTemplate : CharacterBody2D
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
itemSlotNode.RemoveItem(1);
|
ProtectedItemContainer?.RemoveItem(index, 1);
|
||||||
|
originalItem.QueueFreeSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ public partial class Player : CharacterTemplate
|
||||||
|
|
||||||
//Subscribe to events when the item container is bound to the player.
|
//Subscribe to events when the item container is bound to the player.
|
||||||
//在物品容器与玩家绑定时订阅事件。
|
//在物品容器与玩家绑定时订阅事件。
|
||||||
itemContainer.SelectedItemSlotChangeEvent += SelectedItemSlotChangeEvent;
|
itemContainer.SelectedItemChangeEvent += SelectedItemChangeEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _ExitTree()
|
public override void _ExitTree()
|
||||||
|
@ -82,14 +82,13 @@ public partial class Player : CharacterTemplate
|
||||||
{
|
{
|
||||||
//Unsubscribe to events when this object is destroyed.
|
//Unsubscribe to events when this object is destroyed.
|
||||||
//此节点被销毁时,取消订阅事件。
|
//此节点被销毁时,取消订阅事件。
|
||||||
ProtectedItemContainer.SelectedItemSlotChangeEvent -= SelectedItemSlotChangeEvent;
|
ProtectedItemContainer.SelectedItemChangeEvent -= SelectedItemChangeEvent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectedItemSlotChangeEvent(SelectedItemSlotChangeEvent selectedItemSlotChangeEvent)
|
private void SelectedItemChangeEvent(SelectedItemChangeEvent selectedItemChangeEvent)
|
||||||
{
|
{
|
||||||
var item = selectedItemSlotChangeEvent.NewItemSlotNode?.GetItem();
|
var item = selectedItemChangeEvent.NewItem;
|
||||||
GameSceneDepend.DynamicUiGroup?.HideAllControl();
|
|
||||||
if (item is Node2D node2D)
|
if (item is Node2D node2D)
|
||||||
{
|
{
|
||||||
CurrentItem = node2D;
|
CurrentItem = node2D;
|
||||||
|
@ -229,9 +228,6 @@ public partial class Player : CharacterTemplate
|
||||||
{
|
{
|
||||||
UseItem(GetGlobalMousePosition());
|
UseItem(GetGlobalMousePosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Pick up an item
|
//Pick up an item
|
||||||
//捡起物品
|
//捡起物品
|
||||||
if (Input.IsActionJustPressed("pick_up"))
|
if (Input.IsActionJustPressed("pick_up"))
|
||||||
|
@ -305,7 +301,6 @@ public partial class Player : CharacterTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowItem(ItemContainer.GetSelectIndex(), 1, GetThrowVelocity());
|
ThrowItem(ItemContainer.GetSelectIndex(), 1, GetThrowVelocity());
|
||||||
GameSceneDepend.DynamicUiGroup?.HideAllControl();
|
|
||||||
CurrentItem = null;
|
CurrentItem = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,12 @@ public static class LogCat
|
||||||
/// <para>房间</para>
|
/// <para>房间</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Room = "Room";
|
public const string Room = "Room";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>ItemSpawn</para>
|
||||||
|
/// <para>物品生成器</para>
|
||||||
|
/// </summary>
|
||||||
|
public const string ItemSpawn = "ItemSpawn";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
using ColdMint.scripts.character;
|
||||||
|
using ColdMint.scripts.loader.uiLoader;
|
||||||
|
using ColdMint.scripts.utils;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace ColdMint.scripts.furniture;
|
namespace ColdMint.scripts.furniture;
|
||||||
|
@ -18,22 +21,93 @@ public partial class GuiFurniture : Furniture
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private Area2D? _operateArea2D;
|
private Area2D? _operateArea2D;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Whether the player is within range of the operation</para>
|
||||||
|
/// <para>玩家是否在操作范围内</para>
|
||||||
|
/// </summary>
|
||||||
|
private bool _playerInRange;
|
||||||
|
[Export]
|
||||||
|
public string? Path;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>There's a mouse hover</para>
|
||||||
|
/// <para>有鼠标悬停</para>
|
||||||
|
/// </summary>
|
||||||
|
private bool _hasMouseOver;
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
base._Ready();
|
base._Ready();
|
||||||
|
InputPickable = true;
|
||||||
_operateArea2D = GetNode<Area2D>("OperateArea2D");
|
_operateArea2D = GetNode<Area2D>("OperateArea2D");
|
||||||
_operateArea2D.BodyEntered += OnBodyEntered;
|
_operateArea2D.BodyEntered += OnBodyEntered;
|
||||||
_operateArea2D.BodyExited += OnBodyExited;
|
_operateArea2D.BodyExited += OnBodyExited;
|
||||||
|
_operateArea2D.SetCollisionMaskValue(Config.LayerNumber.Player, true);
|
||||||
|
if (Path != null)
|
||||||
|
{
|
||||||
|
GameSceneDepend.DynamicUiGroup?.RegisterControl(Path, () =>
|
||||||
|
{
|
||||||
|
var packedScene = GD.Load<PackedScene>(Path);
|
||||||
|
return NodeUtils.InstantiatePackedScene<SpellEditorUi>(packedScene);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Use furniture</para>
|
||||||
|
/// <para>使用家具</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="player"></param>
|
||||||
|
private void Use(Player player)
|
||||||
|
{
|
||||||
|
if (Path == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GameSceneDepend.DynamicUiGroup?.ShowControl(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _MouseEnter()
|
||||||
|
{
|
||||||
|
_hasMouseOver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _MouseExit()
|
||||||
|
{
|
||||||
|
_hasMouseOver = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBodyEntered(Node node)
|
private void OnBodyEntered(Node node)
|
||||||
{
|
{
|
||||||
|
if (node is Player)
|
||||||
|
{
|
||||||
|
_playerInRange = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _PhysicsProcess(double delta)
|
||||||
|
{
|
||||||
|
base._PhysicsProcess(delta);
|
||||||
|
if (GameSceneDepend.Player == null || !_playerInRange || !_hasMouseOver)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Input.IsActionJustPressed("use_item"))
|
||||||
|
{
|
||||||
|
Use(GameSceneDepend.Player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBodyExited(Node2D node2D)
|
private void OnBodyExited(Node2D node2D)
|
||||||
{
|
{
|
||||||
|
if (node2D is Player)
|
||||||
|
{
|
||||||
|
_playerInRange = false;
|
||||||
|
if (Path != null)
|
||||||
|
{
|
||||||
|
GameSceneDepend.DynamicUiGroup?.HideControl(Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _ExitTree()
|
public override void _ExitTree()
|
||||||
|
|
|
@ -10,22 +10,18 @@ namespace ColdMint.scripts.inventory;
|
||||||
public partial class HotBar : HBoxContainer
|
public partial class HotBar : HBoxContainer
|
||||||
{
|
{
|
||||||
private IItemContainer? _itemContainer;
|
private IItemContainer? _itemContainer;
|
||||||
|
private IItemContainerDisplay? _itemContainerDisplay;
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
base._Ready();
|
base._Ready();
|
||||||
_itemContainer = new UniversalItemContainer();
|
var universalItemContainer = new UniversalItemContainer(Config.HotBarSize);
|
||||||
|
_itemContainer = universalItemContainer;
|
||||||
|
universalItemContainer.AllowItemTypesExceptPlaceholder();
|
||||||
_itemContainer.SupportSelect = true;
|
_itemContainer.SupportSelect = true;
|
||||||
|
_itemContainerDisplay = new ItemSlotContainerDisplay(this);
|
||||||
|
_itemContainerDisplay.BindItemContainer(_itemContainer);
|
||||||
NodeUtils.DeleteAllChild(this);
|
NodeUtils.DeleteAllChild(this);
|
||||||
for (var i = 0; i < Config.HotBarSize; i++)
|
|
||||||
{
|
|
||||||
var itemSlotNode = _itemContainer.AddItemSlot(this);
|
|
||||||
if (itemSlotNode != null)
|
|
||||||
{
|
|
||||||
itemSlotNode.BackpackAllowed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,14 +32,14 @@ public partial class HotBar : HBoxContainer
|
||||||
{
|
{
|
||||||
//Mouse wheel down
|
//Mouse wheel down
|
||||||
//鼠标滚轮向下
|
//鼠标滚轮向下
|
||||||
_itemContainer?.SelectTheNextItemSlot();
|
_itemContainer?.SelectNextItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Input.IsActionJustPressed("hotbar_previous"))
|
if (Input.IsActionJustPressed("hotbar_previous"))
|
||||||
{
|
{
|
||||||
//Mouse wheel up
|
//Mouse wheel up
|
||||||
//鼠标滚轮向上
|
//鼠标滚轮向上
|
||||||
_itemContainer?.SelectThePreviousItemSlot();
|
_itemContainer?.SelectPreviousItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Input.IsActionJustPressed("hotbar_1"))
|
if (Input.IsActionJustPressed("hotbar_1"))
|
||||||
|
@ -106,7 +102,7 @@ public partial class HotBar : HBoxContainer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_itemContainer.SelectItemSlot(shortcutKeyIndex);
|
_itemContainer.SelectItem(shortcutKeyIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IItemContainer? GetItemContainer()
|
public IItemContainer? GetItemContainer()
|
||||||
|
|
|
@ -4,11 +4,34 @@ namespace ColdMint.scripts.inventory;
|
||||||
|
|
||||||
public interface IItem
|
public interface IItem
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para>The position of the item in the container</para>
|
||||||
|
/// <para>物品在容器内的位置</para>
|
||||||
|
/// </summary>
|
||||||
|
public int Index { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>ID of current item</para>
|
/// <para>ID of current item</para>
|
||||||
/// <para>当前物品的ID</para>
|
/// <para>当前物品的ID</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Id { get; set; }
|
string Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>ShowSelf</para>
|
||||||
|
/// <para>显示自身</para>
|
||||||
|
/// </summary>
|
||||||
|
void ShowSelf();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>QueueFreeSelf</para>
|
||||||
|
/// <para>销毁自身</para>
|
||||||
|
/// </summary>
|
||||||
|
void QueueFreeSelf();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>HideSelf</para>
|
||||||
|
/// <para>隐藏自身</para>
|
||||||
|
/// </summary>
|
||||||
|
void HideSelf();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Icon of current item</para>
|
/// <para>Icon of current item</para>
|
||||||
|
@ -33,12 +56,69 @@ public interface IItem
|
||||||
/// <para>当前的数量</para>
|
/// <para>当前的数量</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int Quantity { get; set; }
|
int Quantity { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>MaxItemQuantity</para>
|
/// <para>MaxItemQuantity</para>
|
||||||
/// <para>最大物品数量</para>
|
/// <para>最大物品数量</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int MaxQuantity { get; }
|
int MaxQuantity { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>ItemType</para>
|
||||||
|
/// <para>获取物品类型</para>
|
||||||
|
/// </summary>
|
||||||
|
int ItemType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Check or not</para>
|
||||||
|
/// <para>是否选中</para>
|
||||||
|
/// </summary>
|
||||||
|
bool IsSelect { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>The container in which the item is located</para>
|
||||||
|
/// <para>物品所在的物品容器</para>
|
||||||
|
/// </summary>
|
||||||
|
IItemContainer? ItemContainer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Its own container of items</para>
|
||||||
|
/// <para>自身的物品容器</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>Returns a non-null value if the item itself can hold other items</para>
|
||||||
|
/// <para>物品本身可容纳其他物品,则返回非空值</para>
|
||||||
|
/// </remarks>
|
||||||
|
public IItemContainer? SelfItemContainer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Calculate how many items can be merged with other items</para>
|
||||||
|
/// <para>计算当前物品可与其他物品合并多少个</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other"></param>
|
||||||
|
/// <param name="unallocatedQuantity">
|
||||||
|
///<para>The amount yet to be allocated(This method doesn't actually change the number of iitems, so you need to allocate an int variable to keep track of how many items remain unallocated.)</para>
|
||||||
|
///<para>尚未分配的数量(在此方法并不会实际改变IItem的数量,故您需要分配一个int型变量记录还有多少个物品尚未分配)</para>
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
///<para>Number of mergable numbers. 0 indicates that the number cannot be merged. Greater than 0 indicates that the number can be merged.</para>
|
||||||
|
///<para>可合并的数量,0为不能合并,大于0可合并。</para>
|
||||||
|
/// </returns>
|
||||||
|
int MergeableItemCount(IItem other, int unallocatedQuantity);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Create a new item instance</para>
|
||||||
|
/// <para>创建新的物品实例</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">
|
||||||
|
///<para>Quantity (pass in a value less than 0 to create an instance using all the quantities of built-in items)</para>
|
||||||
|
///<para>数量(传入小于0的值,使用内置物品的所有数量创建实例)</para>
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
///<para>Newly created item</para>
|
||||||
|
///<para>新创建的物品</para>
|
||||||
|
/// </returns>
|
||||||
|
IItem? CreateItem(int number);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Execute when current item is used <br/> e.g. when player clicks left mouse button with current item in hand</para>
|
/// <para>Execute when current item is used <br/> e.g. when player clicks left mouse button with current item in hand</para>
|
||||||
|
@ -46,5 +126,16 @@ public interface IItem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="owner">Owner of current item, if any</param>
|
/// <param name="owner">Owner of current item, if any</param>
|
||||||
/// <param name="targetGlobalPosition">Target position, such as the position of the cursor when used by the player</param>
|
/// <param name="targetGlobalPosition">Target position, such as the position of the cursor when used by the player</param>
|
||||||
void Use(Node2D? owner, Vector2 targetGlobalPosition);
|
/// <returns>
|
||||||
|
///<para>Whether it was successfully executed</para>
|
||||||
|
///<para>是否成功被执行了</para>
|
||||||
|
/// </returns>
|
||||||
|
bool Use(Node2D? owner, Vector2 targetGlobalPosition);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>When the item is thrown</para>
|
||||||
|
/// <para>当物品被抛出时</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="velocity"></param>
|
||||||
|
void OnThrow(Vector2 velocity);
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using ColdMint.scripts.map.events;
|
using ColdMint.scripts.map.events;
|
||||||
using Godot;
|
|
||||||
|
|
||||||
namespace ColdMint.scripts.inventory;
|
namespace ColdMint.scripts.inventory;
|
||||||
|
|
||||||
|
@ -9,17 +7,33 @@ namespace ColdMint.scripts.inventory;
|
||||||
/// <para>item container</para>
|
/// <para>item container</para>
|
||||||
/// <para>物品容器</para>
|
/// <para>物品容器</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
public interface IItemContainer
|
||||||
///<para>Item containers can store items. Things like backpacks and Hotbars are containers with visual pages.</para>
|
|
||||||
///<para>物品容器可以储存物品。像背包和hotbar是具有可视化页面的容器。</para>
|
|
||||||
/// </remarks>
|
|
||||||
public interface IItemContainer : IEnumerable<ItemSlotNode>
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>This event is triggered when the selected item slot changes</para>
|
/// <para>This event is triggered when the selected item changes</para>
|
||||||
/// <para>当选中的物品槽改变时,触发此事件</para>
|
/// <para>当选中的物品改变时,触发此事件</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Action<SelectedItemSlotChangeEvent>? SelectedItemSlotChangeEvent { get; set; }
|
Action<SelectedItemChangeEvent>? SelectedItemChangeEvent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>This event is triggered when the item's data changes, such as the number increases, decreases, or new items are added to the container</para>
|
||||||
|
/// <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>
|
/// <summary>
|
||||||
/// <para>Can the specified item be added to the container?</para>
|
/// <para>Can the specified item be added to the container?</para>
|
||||||
|
@ -34,15 +48,28 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
|
||||||
/// <para>实现添加物品的方法</para>
|
/// <para>实现添加物品的方法</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item"></param>
|
/// <param name="item"></param>
|
||||||
/// <returns></returns>
|
/// <returns>
|
||||||
bool AddItem(IItem item);
|
///<para>How many items were successfully added. The addition failed, and 0 was returned.</para>
|
||||||
|
///<para>有多少个物品被成功添加了。添加失败,返回0</para>
|
||||||
|
/// </returns>
|
||||||
|
int AddItem(IItem item);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Whether this item container supports checking</para>
|
/// <para>Whether this item container supports checking</para>
|
||||||
/// <para>此物品容器是否支持选中</para>
|
/// <para>此物品容器是否支持选中</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SupportSelect { get; set; }
|
bool SupportSelect { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Gets a placeholder object</para>
|
||||||
|
/// <para>获取占位符对象</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">
|
||||||
|
///<para>index</para>
|
||||||
|
///<para>占位符代替的索引</para>
|
||||||
|
/// </param>
|
||||||
|
/// <returns></returns>
|
||||||
|
IItem GetPlaceHolderItem(int index);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Gets the selected location</para>
|
/// <para>Gets the selected location</para>
|
||||||
|
@ -52,104 +79,118 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
|
||||||
int GetSelectIndex();
|
int GetSelectIndex();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Gets the currently selected node</para>
|
/// <para>Gets the currently selected item</para>
|
||||||
/// <para>获取当前选中的节点</para>
|
/// <para>获取当前选中的物品</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
ItemSlotNode? GetSelectItemSlotNode();
|
IItem? GetSelectItem();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Removes an item from the inventory at the currently selected location</para>
|
/// <para>Gets the item in the specified location</para>
|
||||||
/// <para>移除当前选中位置物品栏内的物品</para>
|
/// <para>获取指定位置的物品</para>
|
||||||
/// </summary>
|
|
||||||
/// <param name="number">
|
|
||||||
/// <para>Quantity to be removed, inputs below zero represent all items</para>
|
|
||||||
/// <para>要删除的数量,小于0的输入代表全部物品</para>
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <para>The remaining number, if the number of items in the current item stack is less than the specified number. Otherwise,0</para>
|
|
||||||
/// <para>若物品槽内物品少于指定的数量,返回相差的数量。否则返回0</para>
|
|
||||||
/// </returns>
|
|
||||||
int RemoveItemFromItemSlotBySelectIndex(int number);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Gets the number of item slots</para>
|
|
||||||
/// <para>获取物品槽的数量</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
int GetItemSlotCount();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Gets the item slot for the specified location</para>
|
|
||||||
/// <para>获取指定位置的物品槽</para>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index"></param>
|
/// <param name="index"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
ItemSlotNode? GetItemSlotNode(int index);
|
IItem? GetItem(int index);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Gets the item slot for the specified location, equals to <see cref="GetItemSlotNode"/></para>
|
/// <para>ReplaceItem</para>
|
||||||
/// <para>获取指定位置的物品槽,等同于<see cref="GetItemSlotNode"/></para>
|
/// <para>替换物品</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>Even if the item corresponding to the index is null, it can be successfully replaced.</para>
|
||||||
|
///<para>即使索引对应的物品为null,也可以成功的替换。</para>
|
||||||
|
/// </remarks>
|
||||||
/// <param name="index"></param>
|
/// <param name="index"></param>
|
||||||
/// <returns></returns>
|
|
||||||
ItemSlotNode? this[int index] => GetItemSlotNode(index);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Removes an item from the item slot in the specified location</para>
|
|
||||||
/// <para>在指定位置的物品槽内移除物品</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="itemSlotIndex"></param>
|
|
||||||
/// <param name="number">
|
|
||||||
/// <para>Quantity to be removed, inputs below zero represent all items</para>
|
|
||||||
/// <para>要删除的数量,小于0的输入代表全部物品</para>
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <para>The remaining number, if the number of items in the current item stack is less than the specified number. Otherwise,0</para>
|
|
||||||
/// <para>若物品槽内物品少于指定的数量,返回相差的数量。否则返回0</para>
|
|
||||||
/// </returns>
|
|
||||||
int RemoveItemFromItemSlot(int itemSlotIndex, int number);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Based on the given item, match the item slots where it can be added to </para>
|
|
||||||
/// <para>根据给定的物品,匹配可放置它的物品槽</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item"></param>
|
/// <param name="item"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool ReplaceItem(int index, IItem item);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Whether items are replaceable</para>
|
||||||
|
/// <para>是否可替换物品</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool CanReplaceItem(int index, IItem item);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>ClearItem</para>
|
||||||
|
/// <para>清理物品</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool ClearItem(int index);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Gets the item in the specified location, equivalent to <see cref="GetItem"/></para>
|
||||||
|
/// <para>获取指定位置的物品,等同于<see cref="GetItem"/></para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
IItem? this[int index] => GetItem(index);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Removes the item from the currently selected location</para>
|
||||||
|
/// <para>移除当前选中位置的物品</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">
|
||||||
|
/// <para>Quantity to be removed, inputs below zero represent all items</para>
|
||||||
|
/// <para>要删除的数量,小于0的输入代表全部物品</para>
|
||||||
|
/// </param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// <para>Return null if there is no slot to place the item in</para>
|
///<para>How many items were actually removed</para>
|
||||||
/// <para>若没有槽可放置此物品,则返回null</para>
|
///<para>实际移除了多少个物品</para>
|
||||||
/// </returns>
|
/// </returns>
|
||||||
ItemSlotNode? Match(IItem item);
|
int RemoveSelectItem(int number);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>AddItemSlot</para>
|
/// <para>Deduct the number of items in the specified location</para>
|
||||||
/// <para>添加物品槽</para>
|
/// <para>扣除指定位置的物品数量</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rootNode"></param>
|
/// <param name="itemIndex"></param>
|
||||||
ItemSlotNode? AddItemSlot(Node rootNode);
|
/// <param name="number">
|
||||||
|
/// <para>Quantity to be removed, inputs below zero represent all items</para>
|
||||||
|
/// <para>要删除的数量,小于0的输入代表全部物品</para>
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
///<para>How many items were actually removed</para>
|
||||||
|
///<para>实际移除了多少个物品</para>
|
||||||
|
/// </returns>
|
||||||
|
int RemoveItem(int itemIndex, int number);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>SelectTheNextItemSlot</para>
|
/// <para>Gets the used capacity of the item container</para>
|
||||||
/// <para>选择下一个物品槽</para>
|
/// <para>获取物品容器已使用的容量</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void SelectTheNextItemSlot();
|
/// <returns></returns>
|
||||||
|
int GetUsedCapacity();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>SelectThePreviousItemSlot</para>
|
/// <para>Gets the total capacity of the item container</para>
|
||||||
/// <para>选择上一个物品槽</para>
|
/// <para>获取物品容器的总容量</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void SelectThePreviousItemSlot();
|
/// <returns></returns>
|
||||||
|
int GetTotalCapacity();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>选择物品槽</para>
|
/// <para>Select the next item</para>
|
||||||
/// <para>SelectItemSlot</para>
|
/// <para>选择下一个物品</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="newSelectIndex"></param>
|
void SelectNextItem();
|
||||||
void SelectItemSlot(int newSelectIndex);
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Select the previous item</para>
|
||||||
|
/// <para>选择上一个物品</para>
|
||||||
|
/// </summary>
|
||||||
|
void SelectPreviousItem();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>选择物品</para>
|
||||||
|
/// <para>Select item</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
void SelectItem(int index);
|
||||||
}
|
}
|
13
scripts/inventory/IItemContainerDisplay.cs
Normal file
13
scripts/inventory/IItemContainerDisplay.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.inventory;
|
||||||
|
|
||||||
|
public interface IItemContainerDisplay : IEnumerable<IItemDisplay>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Bind an item container to the item container display</para>
|
||||||
|
/// <para>为物品容器显示器绑定物品容器</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemContainer"></param>
|
||||||
|
void BindItemContainer(IItemContainer? itemContainer);
|
||||||
|
}
|
40
scripts/inventory/IItemDisplay.cs
Normal file
40
scripts/inventory/IItemDisplay.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
namespace ColdMint.scripts.inventory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>IItemDisplay</para>
|
||||||
|
/// <para>物品显示器</para>
|
||||||
|
/// </summary>
|
||||||
|
public interface IItemDisplay
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Call this method to refresh the display when the item's information changes</para>
|
||||||
|
/// <para>物品的信息发生变更时,调用此方法刷新显示器</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<param name="item">
|
||||||
|
///<para>New data for items after changes</para>
|
||||||
|
///<para>发生改变后的物品新数据</para>
|
||||||
|
/// </param>
|
||||||
|
/// </remarks>
|
||||||
|
void Update(IItem? item);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Gets the item that is being displayed</para>
|
||||||
|
/// <para>获取正在显示的物品</para>
|
||||||
|
/// </summary>
|
||||||
|
IItem? Item { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Show item Display</para>
|
||||||
|
/// <para>显示物品显示器</para>
|
||||||
|
/// </summary>
|
||||||
|
void ShowSelf();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Hide item Display</para>
|
||||||
|
/// <para>隐藏物品显示器</para>
|
||||||
|
/// </summary>
|
||||||
|
void HideSelf();
|
||||||
|
|
||||||
|
}
|
161
scripts/inventory/ItemContainerDisplayTemplate.cs
Normal file
161
scripts/inventory/ItemContainerDisplayTemplate.cs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ColdMint.scripts.map.events;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.inventory;
|
||||||
|
|
||||||
|
public abstract class ItemContainerDisplayTemplate : IItemContainerDisplay
|
||||||
|
{
|
||||||
|
protected readonly List<IItemDisplay> ItemDisplayList = [];
|
||||||
|
private IItemContainer? _itemContainer;
|
||||||
|
|
||||||
|
public async void BindItemContainer(IItemContainer? itemContainer)
|
||||||
|
{
|
||||||
|
if (_itemContainer == itemContainer)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_itemContainer != null)
|
||||||
|
{
|
||||||
|
_itemContainer.SelectedItemChangeEvent -= OnSelectedItemChangeEvent;
|
||||||
|
_itemContainer.ItemDataChangeEvent -= OnItemDataChangeEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
_itemContainer = itemContainer;
|
||||||
|
if (itemContainer == null)
|
||||||
|
{
|
||||||
|
//Set empty items container to hide all ui.
|
||||||
|
//设置空物品容器,隐藏全部ui。
|
||||||
|
foreach (var itemDisplay in ItemDisplayList)
|
||||||
|
{
|
||||||
|
itemDisplay.Update(null);
|
||||||
|
itemDisplay.HideSelf();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itemContainer.SelectedItemChangeEvent += OnSelectedItemChangeEvent;
|
||||||
|
itemContainer.ItemDataChangeEvent += OnItemDataChangeEvent;
|
||||||
|
var totalCapacity = itemContainer.GetTotalCapacity();
|
||||||
|
var currentCapacity = ItemDisplayList.Count;
|
||||||
|
var capacityDifference = totalCapacity - currentCapacity;
|
||||||
|
var adjustedEndIndex = totalCapacity;
|
||||||
|
if (capacityDifference > 0)
|
||||||
|
{
|
||||||
|
//There are those that need to be added, and we create them.
|
||||||
|
//有需要添加的,我们创建他们。
|
||||||
|
for (var i = 0; i < capacityDifference; i++)
|
||||||
|
{
|
||||||
|
AddItemDisplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (capacityDifference < 0)
|
||||||
|
{
|
||||||
|
//There are things that need to be hidden
|
||||||
|
//有需要被隐藏的
|
||||||
|
adjustedEndIndex += capacityDifference;
|
||||||
|
var loopEndIndex = currentCapacity + capacityDifference;
|
||||||
|
for (var i = currentCapacity - 1; i >= loopEndIndex; i--)
|
||||||
|
{
|
||||||
|
var itemDisplay = ItemDisplayList[i];
|
||||||
|
itemDisplay.Update(null);
|
||||||
|
itemDisplay.HideSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Yield();
|
||||||
|
UpdateData(itemContainer, adjustedEndIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnItemDataChangeEvent(ItemDataChangeEvent itemDataChangeEvent)
|
||||||
|
{
|
||||||
|
if (_itemContainer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateDataForSingleLocation(_itemContainer, itemDataChangeEvent.NewIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSelectedItemChangeEvent(SelectedItemChangeEvent selectedItemChangeEvent)
|
||||||
|
{
|
||||||
|
if (_itemContainer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateDataForSingleLocation(_itemContainer, selectedItemChangeEvent.OldIndex);
|
||||||
|
UpdateDataForSingleLocation(_itemContainer, selectedItemChangeEvent.NewIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Update data</para>
|
||||||
|
/// <para>更新数据</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>Used to batch update the Item data in itemContainer to the display.</para>
|
||||||
|
///<para>用于将itemContainer内的Item数据批量更新到显示器内。</para>
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="itemContainer">
|
||||||
|
///<para>Item container data</para>
|
||||||
|
///<para>物品容器数据</para>
|
||||||
|
/// </param>
|
||||||
|
/// <param name="endIndex">
|
||||||
|
///<para>endIndex</para>
|
||||||
|
///<para>结束位置</para>
|
||||||
|
/// </param>
|
||||||
|
/// <param name="startIndex">
|
||||||
|
///<para>startIndex</para>
|
||||||
|
///<para>起始位置</para>
|
||||||
|
/// </param>
|
||||||
|
private void UpdateData(IItemContainer itemContainer, int endIndex, int startIndex = 0)
|
||||||
|
{
|
||||||
|
for (var i = startIndex; i < endIndex; i++)
|
||||||
|
{
|
||||||
|
UpdateDataForSingleLocation(itemContainer, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Update data for a single location</para>
|
||||||
|
/// <para>更新单个位置的数据</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemContainer"></param>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
private void UpdateDataForSingleLocation(IItemContainer itemContainer, int index)
|
||||||
|
{
|
||||||
|
var itemDisplay = ItemDisplayList[index];
|
||||||
|
var item = itemContainer.GetItem(index);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
item = itemContainer.GetPlaceHolderItem(index);
|
||||||
|
}
|
||||||
|
if (itemContainer.SupportSelect)
|
||||||
|
{
|
||||||
|
item.IsSelect = index == itemContainer.GetSelectIndex();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.IsSelect = false;
|
||||||
|
}
|
||||||
|
itemDisplay.Update(item);
|
||||||
|
itemDisplay.ShowSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Add item display</para>
|
||||||
|
/// <para>添加物品显示器</para>
|
||||||
|
/// </summary>
|
||||||
|
protected abstract void AddItemDisplay();
|
||||||
|
|
||||||
|
public IEnumerator<IItemDisplay> GetEnumerator()
|
||||||
|
{
|
||||||
|
return ItemDisplayList.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
26
scripts/inventory/ItemSlotContainerDisplay.cs
Normal file
26
scripts/inventory/ItemSlotContainerDisplay.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using ColdMint.scripts.utils;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.inventory;
|
||||||
|
|
||||||
|
public class ItemSlotContainerDisplay(Node rootNode) : ItemContainerDisplayTemplate
|
||||||
|
{
|
||||||
|
private readonly PackedScene? _packedScene = GD.Load<PackedScene>("res://prefab/ui/ItemSlot.tscn");
|
||||||
|
|
||||||
|
protected override void AddItemDisplay()
|
||||||
|
{
|
||||||
|
if (_packedScene == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemSlotNode = NodeUtils.InstantiatePackedScene<ItemSlotNode>(_packedScene);
|
||||||
|
if (itemSlotNode == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemDisplayList.Add(itemSlotNode);
|
||||||
|
NodeUtils.CallDeferredAddChild(rootNode, itemSlotNode);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,3 @@
|
||||||
using System;
|
|
||||||
using ColdMint.scripts.debug;
|
|
||||||
using ColdMint.scripts.pickable;
|
|
||||||
using ColdMint.scripts.utils;
|
using ColdMint.scripts.utils;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
|
@ -10,27 +7,16 @@ namespace ColdMint.scripts.inventory;
|
||||||
/// <para>A slot in the inventory</para>
|
/// <para>A slot in the inventory</para>
|
||||||
/// <para>物品栏内的一个插槽</para>
|
/// <para>物品栏内的一个插槽</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ItemSlotNode : MarginContainer
|
public partial class ItemSlotNode : MarginContainer, IItemDisplay
|
||||||
{
|
{
|
||||||
private TextureRect? _backgroundTextureRect;
|
private TextureRect? _backgroundTextureRect;
|
||||||
private TextureRect? _iconTextureRect;
|
private TextureRect? _iconTextureRect;
|
||||||
private Label? _quantityLabel;
|
private Label? _quantityLabel;
|
||||||
private Control? _control;
|
private Control? _control;
|
||||||
private bool _isSelect;
|
|
||||||
private Texture2D? _backgroundTexture;
|
private Texture2D? _backgroundTexture;
|
||||||
private Texture2D? _backgroundTextureWhenSelect;
|
private Texture2D? _backgroundTextureWhenSelect;
|
||||||
private IItem? _item;
|
public IItem? Item { get; private set; }
|
||||||
|
|
||||||
public IItem? Item
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
//Set item
|
|
||||||
//设置物品
|
|
||||||
_item = value;
|
|
||||||
UpdateAllDisplay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
|
@ -42,254 +28,155 @@ public partial class ItemSlotNode : MarginContainer
|
||||||
_quantityLabel = GetNode<Label>("Control/QuantityLabel");
|
_quantityLabel = GetNode<Label>("Control/QuantityLabel");
|
||||||
_control = GetNode<Control>("Control");
|
_control = GetNode<Control>("Control");
|
||||||
_quantityLabel.Hide();
|
_quantityLabel.Hide();
|
||||||
UpdateBackground(_isSelect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Variant _GetDragData(Vector2 atPosition)
|
public override Variant _GetDragData(Vector2 atPosition)
|
||||||
{
|
{
|
||||||
if (_isSelect || _iconTextureRect == null)
|
switch (Item)
|
||||||
{
|
{
|
||||||
//Drag is not allowed if there is no icon or no pile of items.
|
case null:
|
||||||
//如果没有图标或者没有物品堆,那么不允许拖动。
|
return Config.EmptyVariant;
|
||||||
return new Variant();
|
case PlaceholderItem:
|
||||||
|
return Config.EmptyVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_iconTextureRect == null)
|
||||||
|
{
|
||||||
|
return Config.EmptyVariant;
|
||||||
}
|
}
|
||||||
|
|
||||||
var textureRect = new TextureRect();
|
var textureRect = new TextureRect();
|
||||||
textureRect.ExpandMode = _iconTextureRect.ExpandMode;
|
textureRect.ExpandMode = _iconTextureRect.ExpandMode;
|
||||||
textureRect.Size = _iconTextureRect.Size;
|
textureRect.Size = _iconTextureRect.Size;
|
||||||
textureRect.Texture = _iconTextureRect.Texture;
|
textureRect.Texture = _iconTextureRect.Texture;
|
||||||
|
textureRect.ZIndex = Config.ZIndexManager.FloatingIcon;
|
||||||
SetDragPreview(textureRect);
|
SetDragPreview(textureRect);
|
||||||
return Variant.CreateFrom(this);
|
return Variant.CreateFrom(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool _CanDropData(Vector2 atPosition, Variant data)
|
public override bool _CanDropData(Vector2 atPosition, Variant data)
|
||||||
{
|
{
|
||||||
if (_isSelect)
|
|
||||||
{
|
|
||||||
//Do not place items in the selected item slot, even if the item slot is empty.
|
|
||||||
//禁止在已选中的物品槽内放置物品,即使物品槽是空的。
|
|
||||||
LogCat.Log("item_slot_is_selected_and_not_allowed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//If the preplaced slot does not have an icon, the preplaced slot is not allowed.
|
|
||||||
//如果预放置的槽位没有图标,那么不允许放置。
|
|
||||||
if (_iconTextureRect == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = data.VariantType;
|
var type = data.VariantType;
|
||||||
if (type == Variant.Type.Nil)
|
if (type == Variant.Type.Nil)
|
||||||
{
|
{
|
||||||
//The preplaced data is null.
|
|
||||||
//预放置的数据为null。
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemSlotNode = data.As<ItemSlotNode>();
|
var itemSlotNode = data.As<ItemSlotNode>();
|
||||||
var item = itemSlotNode.GetItem();
|
var sourceItem = itemSlotNode.Item;
|
||||||
if (item == null)
|
if (sourceItem == null)
|
||||||
{
|
{
|
||||||
//Return null when trying to get the source item.
|
|
||||||
//尝试获取源物品时返回null。
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
switch (Item)
|
||||||
if (item is Packsack packsack)
|
|
||||||
{
|
{
|
||||||
if (_item == null)
|
case null:
|
||||||
{
|
return true;
|
||||||
//If the dragged item is a backpack and there are no items in the current slot, return whether the backpack is allowed.
|
case PlaceholderItem placeholderItem:
|
||||||
//如果拖拽的物品是背包,且当前槽位没有物品,那么返回是否允许放置背包。
|
var placeholderItemContainer = placeholderItem.ItemContainer;
|
||||||
return BackpackAllowed;
|
if (placeholderItemContainer == null)
|
||||||
}
|
{
|
||||||
|
return true;
|
||||||
if (packsack.ItemContainer == null)
|
}
|
||||||
{
|
return placeholderItemContainer.CanReplaceItem(placeholderItem.Index, sourceItem);
|
||||||
LogCat.Log("item_container_is_null");
|
default:
|
||||||
return false;
|
var sourceItemSelfContainer = sourceItem.SelfItemContainer;
|
||||||
}
|
if (sourceItemSelfContainer != null)
|
||||||
|
{
|
||||||
return packsack.ItemContainer.CanAddItem(_item);
|
//Place the container on the item.
|
||||||
|
//将容器放在物品上。
|
||||||
|
return sourceItemSelfContainer.CanAddItem(Item);
|
||||||
|
}
|
||||||
|
var itemSelfContainer = Item.SelfItemContainer;
|
||||||
|
if (itemSelfContainer != null)
|
||||||
|
{
|
||||||
|
//Drag the item onto the container.
|
||||||
|
//将物品拖到容器上。
|
||||||
|
return itemSelfContainer.CanAddItem(sourceItem);
|
||||||
|
}
|
||||||
|
return Item.MergeableItemCount(sourceItem, sourceItem.Quantity) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_item is Packsack currentPacksack)
|
|
||||||
{
|
|
||||||
if (currentPacksack.ItemContainer == null)
|
|
||||||
{
|
|
||||||
LogCat.Log("item_container_is_null");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentPacksack.ItemContainer.CanAddItem(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CanAddItem(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Get the items in the item container</para>
|
|
||||||
/// <para>获取物品容器内的物品</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
///<para>There may be multiple quantities</para>
|
|
||||||
///<para>数量可能有多个</para>
|
|
||||||
/// </returns>
|
|
||||||
public IItem? GetItem()
|
|
||||||
{
|
|
||||||
return _item;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>CreateItemInstance</para>
|
|
||||||
/// <para>创建物品槽内的物品实例</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="number">
|
|
||||||
///<para>Quantity (pass in a value less than 0 to create an instance using all the quantities of built-in items)</para>
|
|
||||||
///<para>数量(传入小于0的值,使用内置物品的所有数量创建实例)</para>
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
///<para>Newly created item</para>
|
|
||||||
///<para>新创建的物品</para>
|
|
||||||
/// </returns>
|
|
||||||
public IItem? CreateItemInstance(int number)
|
|
||||||
{
|
|
||||||
if (number == 0)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_item is not Node2D node2D)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var duplicate = node2D.Duplicate();
|
|
||||||
if (node2D is PickAbleTemplate pickAbleTemplate)
|
|
||||||
{
|
|
||||||
pickAbleTemplate.CopyAttributes(duplicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duplicate is not Node2D newNode2D)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
newNode2D.GlobalPosition = node2D.GlobalPosition;
|
|
||||||
if (duplicate is not IItem newItem)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (number < 0)
|
|
||||||
{
|
|
||||||
newItem.Quantity = _item.Quantity;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newItem.Quantity = number >= _item.Quantity ? _item.Quantity : number;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Clean out the items in the item slot</para>
|
|
||||||
/// <para>清理物品槽内的物品</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="queueFree">
|
|
||||||
///<para>Whether to release a node</para>
|
|
||||||
///<para>是否释放节点</para>
|
|
||||||
/// </param>
|
|
||||||
/// <remarks>
|
|
||||||
///<para>Clean up item object references in item slots.</para>
|
|
||||||
///<para>清理物品槽内的物品对象引用。</para>
|
|
||||||
/// </remarks>
|
|
||||||
public void ClearItem(bool queueFree = true)
|
|
||||||
{
|
|
||||||
if (_item == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queueFree && _item is Node node)
|
|
||||||
{
|
|
||||||
node.QueueFree();
|
|
||||||
}
|
|
||||||
|
|
||||||
_item = null;
|
|
||||||
UpdateAllDisplay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _DropData(Vector2 atPosition, Variant data)
|
public override void _DropData(Vector2 atPosition, Variant data)
|
||||||
{
|
{
|
||||||
if (_iconTextureRect == null)
|
//The item is empty and the corresponding item container cannot be retrieved.
|
||||||
|
//物品为空,无法获取对应的物品容器。
|
||||||
|
if (Item is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = data.VariantType;
|
var type = data.VariantType;
|
||||||
if (type == Variant.Type.Nil)
|
if (type == Variant.Type.Nil)
|
||||||
{
|
{
|
||||||
//The passed variable is null.
|
|
||||||
//传入的变量为null。
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemSlotNode = data.As<ItemSlotNode>();
|
var itemSlotNode = data.As<ItemSlotNode>();
|
||||||
var sourceItem = itemSlotNode.GetItem();
|
var sourceItem = itemSlotNode.Item;
|
||||||
if (sourceItem == null)
|
if (sourceItem == null)
|
||||||
{
|
{
|
||||||
//Return null when trying to get the source item.
|
|
||||||
//尝试获取源物品时返回null。
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceItem is Packsack packsack)
|
if (Item.SelfItemContainer != null && Item.SelfItemContainer.CanAddItem(sourceItem))
|
||||||
{
|
{
|
||||||
//If the source item is a backpack.
|
//Use items and place them on the container.
|
||||||
//如果源物品是背包。
|
//用物品,在物品容器上放置。
|
||||||
if (packsack.ItemContainer != null && _item != null)
|
var oldIndex = sourceItem.Index;
|
||||||
|
var oldItemContainer = sourceItem.ItemContainer;
|
||||||
|
var addNumber = Item.SelfItemContainer.AddItem(sourceItem);
|
||||||
|
if (addNumber >= 0)
|
||||||
{
|
{
|
||||||
packsack.ItemContainer.AddItem(_item);
|
if (addNumber == sourceItem.Quantity)
|
||||||
ClearItem(false);
|
{
|
||||||
return;
|
oldItemContainer?.ClearItem(oldIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oldItemContainer?.RemoveItem(oldIndex, addNumber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (_item is Packsack customPacksack)
|
|
||||||
{
|
|
||||||
if (customPacksack.ItemContainer == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
customPacksack.ItemContainer.AddItem(sourceItem);
|
|
||||||
itemSlotNode.ClearItem(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddItem(sourceItem);
|
if (sourceItem.SelfItemContainer != null && sourceItem.SelfItemContainer.CanAddItem(Item))
|
||||||
itemSlotNode.ClearItem(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Whether to place a backpack in the current slot</para>
|
|
||||||
/// <para>当前槽位是否允许放置背包</para>
|
|
||||||
/// </summary>
|
|
||||||
public bool BackpackAllowed { get; set; }
|
|
||||||
|
|
||||||
public bool IsSelect
|
|
||||||
{
|
|
||||||
get => _isSelect;
|
|
||||||
set
|
|
||||||
{
|
{
|
||||||
UpdateBackground(value);
|
//Use containers and place on top of items.
|
||||||
_isSelect = value;
|
//用容器物品,在物品上放置。
|
||||||
|
var oldIndex = Item.Index;
|
||||||
|
var oldItemContainer = Item.ItemContainer;
|
||||||
|
var addNumber = sourceItem.SelfItemContainer.AddItem(Item);
|
||||||
|
if (addNumber >= 0)
|
||||||
|
{
|
||||||
|
if (addNumber == Item.Quantity)
|
||||||
|
{
|
||||||
|
oldItemContainer?.ClearItem(oldIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oldItemContainer?.RemoveItem(oldIndex, addNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item is PlaceholderItem placeholderItem)
|
||||||
|
{
|
||||||
|
var placeholderItemContainer = placeholderItem.ItemContainer;
|
||||||
|
var sourceItemContainer = sourceItem.ItemContainer;
|
||||||
|
var sourceItemIndex = sourceItem.Index;
|
||||||
|
var replaceResult = false;
|
||||||
|
if (placeholderItemContainer != null)
|
||||||
|
{
|
||||||
|
replaceResult = placeholderItemContainer.ReplaceItem(placeholderItem.Index, sourceItem);
|
||||||
|
}
|
||||||
|
if (replaceResult && sourceItemContainer != null)
|
||||||
|
{
|
||||||
|
sourceItemContainer.ClearItem(sourceItemIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void UpdateBackground(bool isSelect)
|
private void UpdateBackground(bool isSelect)
|
||||||
{
|
{
|
||||||
if (_backgroundTextureRect == null)
|
if (_backgroundTextureRect == null)
|
||||||
|
@ -302,24 +189,6 @@ public partial class ItemSlotNode : MarginContainer
|
||||||
|
|
||||||
public TextureRect? BackgroundTextureRect => _backgroundTextureRect;
|
public TextureRect? BackgroundTextureRect => _backgroundTextureRect;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Whether the item in this node is empty</para>
|
|
||||||
/// <para>此节点内的物品是否为空的</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
///<para>Return true if the number of items is 0 or the item object does not exist</para>
|
|
||||||
///<para>当物品数量为0或物品对象不存在时,返回true</para>
|
|
||||||
/// </returns>
|
|
||||||
public bool IsEmpty()
|
|
||||||
{
|
|
||||||
if (_item == null || _item.Quantity == 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Update all displays of this slot</para>
|
/// <para>Update all displays of this slot</para>
|
||||||
|
@ -338,7 +207,7 @@ public partial class ItemSlotNode : MarginContainer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateTooltipText()
|
private void UpdateTooltipText()
|
||||||
{
|
{
|
||||||
if (_item == null)
|
if (Item is PlaceholderItem or null)
|
||||||
{
|
{
|
||||||
TooltipText = null;
|
TooltipText = null;
|
||||||
return;
|
return;
|
||||||
|
@ -349,16 +218,16 @@ public partial class ItemSlotNode : MarginContainer
|
||||||
var debugText = TranslationServerUtils.Translate("item_prompt_debug");
|
var debugText = TranslationServerUtils.Translate("item_prompt_debug");
|
||||||
if (debugText != null)
|
if (debugText != null)
|
||||||
{
|
{
|
||||||
TooltipText = string.Format(debugText, _item.Id,
|
TooltipText = string.Format(debugText, Item.Id,
|
||||||
TranslationServerUtils.Translate(_item.Name),
|
TranslationServerUtils.Translate(Item.Name),
|
||||||
_item.Quantity, _item.MaxQuantity, _item.GetType().Name,
|
Item.Quantity, Item.MaxQuantity, Item.GetType().Name,
|
||||||
TranslationServerUtils.Translate(_item.Description));
|
TranslationServerUtils.Translate(Item.Description));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TooltipText = TranslationServerUtils.Translate(_item.Name) + "\n" +
|
TooltipText = TranslationServerUtils.Translate(Item.Name) + "\n" +
|
||||||
TranslationServerUtils.Translate(_item.Description);
|
TranslationServerUtils.Translate(Item.Description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +242,13 @@ public partial class ItemSlotNode : MarginContainer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_item?.Quantity)
|
if (Item is PlaceholderItem or null)
|
||||||
|
{
|
||||||
|
_quantityLabel.Hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (Item?.Quantity)
|
||||||
{
|
{
|
||||||
case null or 1:
|
case null or 1:
|
||||||
_quantityLabel.Hide();
|
_quantityLabel.Hide();
|
||||||
|
@ -381,7 +256,7 @@ public partial class ItemSlotNode : MarginContainer
|
||||||
default:
|
default:
|
||||||
//When the quantity is not null or 1, we display the quantity.
|
//When the quantity is not null or 1, we display the quantity.
|
||||||
//当数量不为null或1时,我们显示数量
|
//当数量不为null或1时,我们显示数量
|
||||||
_quantityLabel.Text = _item?.Quantity.ToString();
|
_quantityLabel.Text = Item?.Quantity.ToString();
|
||||||
_quantityLabel.Show();
|
_quantityLabel.Show();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -396,94 +271,24 @@ public partial class ItemSlotNode : MarginContainer
|
||||||
{
|
{
|
||||||
if (_iconTextureRect != null)
|
if (_iconTextureRect != null)
|
||||||
{
|
{
|
||||||
_iconTextureRect.Texture = _item?.Icon;
|
_iconTextureRect.Texture = Item?.Icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanAddItem(IItem item)
|
public void Update(IItem? item)
|
||||||
{
|
{
|
||||||
if (!BackpackAllowed && item is Packsack)
|
Item = item;
|
||||||
{
|
UpdateAllDisplay();
|
||||||
//如果禁止放置背包,且新物品是背包
|
UpdateBackground(item is { IsSelect: true });
|
||||||
LogCat.Log("backpack_not_allowed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_item == null)
|
|
||||||
{
|
|
||||||
//If there is no item in the current item slot, it is allowed to add.
|
|
||||||
//如果当前物品槽内没物品,那么允许添加。
|
|
||||||
LogCat.Log("item_is_null");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.Id != _item.Id)
|
|
||||||
{
|
|
||||||
//If the item ID you want to add is different from the current item ID, disable.
|
|
||||||
//如果要添加的物品ID和当前的物品ID不一样,那么禁止。
|
|
||||||
LogCat.Log("item_id_not_same");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newQuantity = item.Quantity + _item.Quantity;
|
|
||||||
if (newQuantity > _item.MaxQuantity)
|
|
||||||
{
|
|
||||||
//The maximum number is exceeded and items cannot be added.
|
|
||||||
//超过了最大数量,无法添加物品。
|
|
||||||
LogCat.Log("max_quantity_exceeded");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public void ShowSelf()
|
||||||
/// <para>Remove items from the item slot</para>
|
|
||||||
/// <para>从物品槽内移除物品</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="number">
|
|
||||||
///<para>number(Less than 0, remove all items)</para>
|
|
||||||
///<para>物品数量(小于0,则移除全部物品)</para>
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
///<para>How many items were actually removed</para>
|
|
||||||
///<para>实际移除了多少个物品</para>
|
|
||||||
/// </returns>
|
|
||||||
public int RemoveItem(int number)
|
|
||||||
{
|
{
|
||||||
if (_item == null)
|
Show();
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//The number of actual removals
|
|
||||||
//实际移除的数量
|
|
||||||
var removeNumber = number < 0 ? _item.Quantity : number;
|
|
||||||
_item.Quantity -= removeNumber;
|
|
||||||
if (_item.Quantity <= 0)
|
|
||||||
{
|
|
||||||
ClearItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return removeNumber;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AddItem(IItem item)
|
public void HideSelf()
|
||||||
{
|
{
|
||||||
if (_item == null)
|
Hide();
|
||||||
{
|
|
||||||
Item = item;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newQuantity = item.Quantity + _item.Quantity;
|
|
||||||
_item.Quantity = Math.Min(newQuantity, _item.MaxQuantity);
|
|
||||||
if (item is Node2D node2D)
|
|
||||||
{
|
|
||||||
node2D.QueueFree();
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateQuantityLabel();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -97,16 +97,4 @@ public static class ItemTypeManager
|
||||||
Registry.TryGetValue(id, out var itemType)
|
Registry.TryGetValue(id, out var itemType)
|
||||||
? itemType.Icon ?? DefaultTexture
|
? itemType.Icon ?? DefaultTexture
|
||||||
: DefaultTexture;
|
: DefaultTexture;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Gets the maximum number of stacks for an item</para>
|
|
||||||
/// <para>获取某个物品的最大堆叠数量</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">
|
|
||||||
///<para>id</para>
|
|
||||||
///<para>物品ID</para>
|
|
||||||
/// </param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static int MaxStackQuantityOf(string id) =>
|
|
||||||
Registry.TryGetValue(id, out var itemType) ? itemType.MaxStackQuantity : 0;
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using ColdMint.scripts.pickable;
|
using ColdMint.scripts.pickable;
|
||||||
using ColdMint.scripts.utils;
|
using ColdMint.scripts.utils;
|
||||||
using Godot;
|
using Godot;
|
||||||
using PacksackUi = ColdMint.scripts.loader.uiLoader.PacksackUi;
|
using PacksackUi = ColdMint.scripts.loader.uiLoader.PacksackUi;
|
||||||
|
@ -12,55 +12,54 @@ namespace ColdMint.scripts.inventory;
|
||||||
public partial class Packsack : PickAbleTemplate
|
public partial class Packsack : PickAbleTemplate
|
||||||
{
|
{
|
||||||
private const string Path = "res://prefab/ui/packsackUI.tscn";
|
private const string Path = "res://prefab/ui/packsackUI.tscn";
|
||||||
[Export] public int NumberSlots { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
public override int ItemType
|
||||||
/// <para>Whether to allow backpacks</para>
|
|
||||||
/// <para>是否允许放置背包</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
///<para>Can a new backpack be placed in the slot of the backpack?</para>
|
|
||||||
///<para>即此背包的槽位内是否可以再放置新的背包?</para>
|
|
||||||
/// </remarks>
|
|
||||||
[Export]
|
|
||||||
public bool BackpackAllowed { get; set; }
|
|
||||||
|
|
||||||
public override bool CanPutInPack => false;
|
|
||||||
|
|
||||||
|
|
||||||
public override void Use(Node2D? owner, Vector2 targetGlobalPosition)
|
|
||||||
{
|
{
|
||||||
GameSceneDepend.DynamicUiGroup?.ShowControl(Path, control =>
|
get => Config.ItemType.Packsack;
|
||||||
|
}
|
||||||
|
[Export] public int NumberSlots { get; set; }
|
||||||
|
public override bool Use(Node2D? owner, Vector2 targetGlobalPosition)
|
||||||
|
{
|
||||||
|
if (GameSceneDepend.DynamicUiGroup == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return GameSceneDepend.DynamicUiGroup.ShowControl(Path, control =>
|
||||||
{
|
{
|
||||||
if (control is PacksackUi packsackUi)
|
if (control is PacksackUi packsackUi)
|
||||||
{
|
{
|
||||||
packsackUi.Title = Name;
|
packsackUi.Title = Name;
|
||||||
packsackUi.ItemContainer = ItemContainer;
|
packsackUi.ItemContainer = SelfItemContainer;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public IItemContainer? ItemContainer { get; private set; }
|
protected override void OnSelectChange(bool isSelected)
|
||||||
|
{
|
||||||
|
if (isSelected)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GameSceneDepend.DynamicUiGroup?.HideControl(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnThrow(Vector2 velocity)
|
||||||
|
{
|
||||||
|
GameSceneDepend.DynamicUiGroup?.HideControl(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
base._Ready();
|
base._Ready();
|
||||||
ItemContainer = new UniversalItemContainer();
|
if (SelfItemContainer == null)
|
||||||
ItemContainer.SupportSelect = false;
|
|
||||||
//When the backpack is created, the item slot is generated.
|
|
||||||
//当背包被创建时,物品槽就被生成出来了。
|
|
||||||
for (var i = 0; i < NumberSlots; i++)
|
|
||||||
{
|
{
|
||||||
var itemSlotNode = ItemContainer.AddItemSlot(this);
|
var universalItemContainer = new UniversalItemContainer(NumberSlots);
|
||||||
if (itemSlotNode == null)
|
universalItemContainer.AllowItemTypesExceptPlaceholder();
|
||||||
{
|
universalItemContainer.DisallowAddingItemByType(Config.ItemType.Packsack);
|
||||||
continue;
|
SelfItemContainer = universalItemContainer;
|
||||||
}
|
SelfItemContainer.SupportSelect = false;
|
||||||
|
|
||||||
itemSlotNode.BackpackAllowed = BackpackAllowed;
|
|
||||||
itemSlotNode.Hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GameSceneDepend.DynamicUiGroup?.RegisterControl(Path, () =>
|
GameSceneDepend.DynamicUiGroup?.RegisterControl(Path, () =>
|
||||||
{
|
{
|
||||||
var packedScene = GD.Load<PackedScene>(Path);
|
var packedScene = GD.Load<PackedScene>(Path);
|
||||||
|
|
61
scripts/inventory/PlaceholderItem.cs
Normal file
61
scripts/inventory/PlaceholderItem.cs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.inventory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>PlaceholderItem</para>
|
||||||
|
/// <para>占位物品</para>
|
||||||
|
/// </summary>
|
||||||
|
public class PlaceholderItem : IItem
|
||||||
|
{
|
||||||
|
public int Index { get; set; }
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public void ShowSelf()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueueFreeSelf()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HideSelf()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture2D Icon { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
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; }
|
||||||
|
public IItemContainer? SelfItemContainer { get; set; }
|
||||||
|
|
||||||
|
public int MergeableItemCount(IItem other, int unallocatedQuantity)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IItem? CreateItem(int number)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Use(Node2D? owner, Vector2 targetGlobalPosition)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnThrow(Vector2 velocity)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Reflection;
|
||||||
using ColdMint.scripts.map.events;
|
using ColdMint.scripts.map.events;
|
||||||
using ColdMint.scripts.utils;
|
|
||||||
using Godot;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
|
|
||||||
namespace ColdMint.scripts.inventory;
|
namespace ColdMint.scripts.inventory;
|
||||||
|
|
||||||
|
@ -13,11 +9,9 @@ namespace ColdMint.scripts.inventory;
|
||||||
/// <para>UniversalItemContainer</para>
|
/// <para>UniversalItemContainer</para>
|
||||||
/// <para>通用的物品容器</para>
|
/// <para>通用的物品容器</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UniversalItemContainer : IItemContainer
|
public class UniversalItemContainer(int totalCapacity) : IItemContainer
|
||||||
{
|
{
|
||||||
private readonly List<ItemSlotNode>? _itemSlotNodes = [];
|
private readonly Dictionary<int, IItem> _itemDictionary = [];
|
||||||
|
|
||||||
private readonly PackedScene? _itemSlotPackedScene = GD.Load<PackedScene>("res://prefab/ui/ItemSlot.tscn");
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>UnknownIndex</para>
|
/// <para>UnknownIndex</para>
|
||||||
|
@ -25,167 +19,383 @@ public class UniversalItemContainer : IItemContainer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int UnknownIndex = -1;
|
private const int UnknownIndex = -1;
|
||||||
|
|
||||||
|
//_selectIndex defaults to 0.
|
||||||
//_selectIndex默认为0.
|
//_selectIndex默认为0.
|
||||||
private int _selectIndex;
|
private int _selectIndex;
|
||||||
|
|
||||||
[MustDisposeResource]
|
/// <summary>
|
||||||
public IEnumerator<ItemSlotNode> GetEnumerator()
|
/// <para>The next available index</para>
|
||||||
|
/// <para>下个可用的索引</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>For example, the variable [1,2,3,5,6] represents 4, or the variable [1,2,3,4,5,6,7] represents 8.</para>
|
||||||
|
///<para>例如[1,2,3,5,6]这个变量表示4,再或者[1,2,3,4,5,6,7]这个变量表示8。</para>
|
||||||
|
/// </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()
|
||||||
{
|
{
|
||||||
return _itemSlotNodes?.GetEnumerator() ?? Enumerable.Empty<ItemSlotNode>().GetEnumerator();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MustDisposeResource]
|
public void AllowAddingItemByType(int itemType)
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
{
|
||||||
return GetEnumerator();
|
_allowedItemTypes.Add(itemType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action<SelectedItemSlotChangeEvent>? SelectedItemSlotChangeEvent { get; set; }
|
public void DisallowAddingItemByType(int itemType)
|
||||||
|
{
|
||||||
|
_allowedItemTypes.Remove(itemType);
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanAddItem(IItem item)
|
public bool CanAddItem(IItem item)
|
||||||
{
|
{
|
||||||
return Match(item) != null;
|
if (!_allowedItemTypes.Contains(item.ItemType))
|
||||||
}
|
|
||||||
|
|
||||||
public bool AddItem(IItem item)
|
|
||||||
{
|
|
||||||
var itemSlotNode = Match(item);
|
|
||||||
if (itemSlotNode == null)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
//If the capacity is not full, directly return to add items
|
||||||
|
//如果未占满容量,直接返回可添加物品
|
||||||
|
if (GetUsedCapacity() < totalCapacity)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return itemSlotNode.AddItem(item);
|
if (item.MaxQuantity == 1)
|
||||||
|
{
|
||||||
|
//New items do not support overlay, capacity is full, return cannot add.
|
||||||
|
//新物品不支持叠加,容量已满,返回不能添加。
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If the capacity is full, we calculate whether we can spread the new items evenly among the existing items.
|
||||||
|
//如果容量占满了,我们计算是否能将新物品均摊在已有的物品内。
|
||||||
|
var unallocatedQuantity = item.Quantity;
|
||||||
|
foreach (var unitItem in _itemDictionary.Values)
|
||||||
|
{
|
||||||
|
var number = unitItem.MergeableItemCount(item, unallocatedQuantity);
|
||||||
|
if (number == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unallocatedQuantity -= number;
|
||||||
|
if (unallocatedQuantity < 1)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return unallocatedQuantity < 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSelectStatus(int index, IItem item)
|
||||||
|
{
|
||||||
|
item.IsSelect = index == _selectIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Update the next available index location</para>
|
||||||
|
/// <para>更新下个可用的索引位置</para>
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateNextAvailableIndex()
|
||||||
|
{
|
||||||
|
_nextAvailableIndex = UnknownIndex;
|
||||||
|
if (totalCapacity <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < totalCapacity; i++)
|
||||||
|
{
|
||||||
|
var contains = _itemDictionary.ContainsKey(i);
|
||||||
|
if (!contains)
|
||||||
|
{
|
||||||
|
_nextAvailableIndex = i;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AddItem(IItem item)
|
||||||
|
{
|
||||||
|
if (item.MaxQuantity == 1)
|
||||||
|
{
|
||||||
|
if (_nextAvailableIndex == UnknownIndex)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextAvailableIndex = _nextAvailableIndex;
|
||||||
|
_itemDictionary[nextAvailableIndex] = item;
|
||||||
|
item.Index = nextAvailableIndex;
|
||||||
|
item.ItemContainer = this;
|
||||||
|
if (nextAvailableIndex != _selectIndex)
|
||||||
|
{
|
||||||
|
item.HideSelf();
|
||||||
|
}
|
||||||
|
UpdateNextAvailableIndex();
|
||||||
|
UpdateSelectStatus(nextAvailableIndex, item);
|
||||||
|
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||||||
|
{
|
||||||
|
NewItem = item,
|
||||||
|
NewIndex = nextAvailableIndex,
|
||||||
|
Type = Config.ItemDataChangeEventType.QuantityAdded
|
||||||
|
});
|
||||||
|
return item.Quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
//There can be more than one item, try to share equally.
|
||||||
|
//物品可有多个,尝试均摊。
|
||||||
|
var originalQuantity = item.Quantity;
|
||||||
|
var temporarilyQuantity = item.Quantity;
|
||||||
|
var index = 0;
|
||||||
|
foreach (var unitItem in _itemDictionary.Values)
|
||||||
|
{
|
||||||
|
var number = unitItem.MergeableItemCount(item, temporarilyQuantity);
|
||||||
|
if (number == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
temporarilyQuantity -= number;
|
||||||
|
unitItem.Quantity += number;
|
||||||
|
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||||||
|
{
|
||||||
|
NewItem = unitItem,
|
||||||
|
NewIndex = index,
|
||||||
|
Type = Config.ItemDataChangeEventType.QuantityAdded
|
||||||
|
});
|
||||||
|
if (item.Quantity < 1)
|
||||||
|
{
|
||||||
|
//New items are fully shared.
|
||||||
|
//新物品完全被均摊。
|
||||||
|
item.HideSelf();
|
||||||
|
return originalQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//New items have some left over.
|
||||||
|
//新物品有一些剩余。
|
||||||
|
if (GetUsedCapacity() >= totalCapacity)
|
||||||
|
{
|
||||||
|
//The capacity is full. The remaining capacity cannot be stored.
|
||||||
|
//容量已满,无法存放剩余。
|
||||||
|
return originalQuantity - temporarilyQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add the rest to the container.
|
||||||
|
//添加剩余到容器内。
|
||||||
|
if (_nextAvailableIndex == UnknownIndex)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
var finalNextAvailableIndex = _nextAvailableIndex;
|
||||||
|
_itemDictionary[finalNextAvailableIndex] = item;
|
||||||
|
item.Index = finalNextAvailableIndex;
|
||||||
|
item.ItemContainer = this;
|
||||||
|
if (finalNextAvailableIndex != _selectIndex)
|
||||||
|
{
|
||||||
|
item.HideSelf();
|
||||||
|
}
|
||||||
|
UpdateNextAvailableIndex();
|
||||||
|
UpdateSelectStatus(finalNextAvailableIndex, item);
|
||||||
|
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||||||
|
{
|
||||||
|
NewItem = item,
|
||||||
|
NewIndex = finalNextAvailableIndex,
|
||||||
|
Type = Config.ItemDataChangeEventType.Add
|
||||||
|
});
|
||||||
|
return originalQuantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SupportSelect { get; set; }
|
public bool SupportSelect { get; set; }
|
||||||
|
|
||||||
|
public IItem GetPlaceHolderItem(int index)
|
||||||
|
{
|
||||||
|
var placeholderItem = new PlaceholderItem
|
||||||
|
{
|
||||||
|
Index = index,
|
||||||
|
ItemContainer = this
|
||||||
|
};
|
||||||
|
return placeholderItem;
|
||||||
|
}
|
||||||
|
|
||||||
public int GetSelectIndex()
|
public int GetSelectIndex()
|
||||||
{
|
{
|
||||||
return _selectIndex;
|
return _selectIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemSlotNode? GetSelectItemSlotNode()
|
public IItem? GetSelectItem()
|
||||||
{
|
{
|
||||||
if (_itemSlotNodes == null || _itemSlotNodes.Count == 0)
|
return _itemDictionary.TryGetValue(_selectIndex, out var item) ? item : null;
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_selectIndex < _itemSlotNodes.Count)
|
|
||||||
{
|
|
||||||
//Prevent subscripts from going out of bounds.
|
|
||||||
//防止下标越界。
|
|
||||||
return _itemSlotNodes[_selectIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int RemoveItemFromItemSlotBySelectIndex(int number) => RemoveItemFromItemSlot(_selectIndex, number);
|
public IItem? GetItem(int index)
|
||||||
|
|
||||||
public int GetItemSlotCount()
|
|
||||||
{
|
{
|
||||||
if (_itemSlotNodes == null)
|
return _itemDictionary.TryGetValue(index, out var item) ? item : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ReplaceItem(int index, IItem item)
|
||||||
|
{
|
||||||
|
var oldItem = GetItem(index);
|
||||||
|
_itemDictionary[index] = item;
|
||||||
|
item.Index = index;
|
||||||
|
item.ItemContainer = this;
|
||||||
|
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||||||
|
{
|
||||||
|
NewItem = item,
|
||||||
|
NewIndex = index,
|
||||||
|
OldIndex = index,
|
||||||
|
OldItem = oldItem,
|
||||||
|
Type = Config.ItemDataChangeEventType.Replace
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanReplaceItem(int index, IItem item)
|
||||||
|
{
|
||||||
|
if (!_allowedItemTypes.Contains(item.ItemType))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool ClearItem(int index)
|
||||||
|
{
|
||||||
|
if (!_itemDictionary.TryGetValue(index, out var item))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var result = _itemDictionary.Remove(index);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||||||
|
{
|
||||||
|
NewItem = null,
|
||||||
|
NewIndex = index,
|
||||||
|
OldIndex = index,
|
||||||
|
OldItem = null,
|
||||||
|
Type = Config.ItemDataChangeEventType.Clear
|
||||||
|
});
|
||||||
|
if (SupportSelect && index == _selectIndex)
|
||||||
|
{
|
||||||
|
item.HideSelf();
|
||||||
|
SelectedItemChangeEvent?.Invoke(new SelectedItemChangeEvent()
|
||||||
|
{
|
||||||
|
NewIndex = index,
|
||||||
|
OldIndex = index,
|
||||||
|
NewItem = null,
|
||||||
|
OldItem = null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int RemoveSelectItem(int number)
|
||||||
|
{
|
||||||
|
return RemoveItem(_selectIndex, number);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RemoveItem(int itemIndex, int number)
|
||||||
|
{
|
||||||
|
if (number == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _itemSlotNodes.Count;
|
if (!_itemDictionary.TryGetValue(itemIndex, out var item))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalQuantity = item.Quantity;
|
||||||
|
if (number < 0)
|
||||||
|
{
|
||||||
|
//If the number entered is less than 0, all items are removed.
|
||||||
|
//输入的数量小于0,则移除全部物品。
|
||||||
|
item.Quantity = 0;
|
||||||
|
_itemDictionary.Remove(itemIndex);
|
||||||
|
UpdateNextAvailableIndex();
|
||||||
|
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||||||
|
{
|
||||||
|
NewItem = item,
|
||||||
|
NewIndex = itemIndex,
|
||||||
|
Type = Config.ItemDataChangeEventType.Remove
|
||||||
|
});
|
||||||
|
return originalQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
var removed = Math.Min(number, item.Quantity);
|
||||||
|
item.Quantity -= removed;
|
||||||
|
if (item.Quantity < 1)
|
||||||
|
{
|
||||||
|
_itemDictionary.Remove(itemIndex);
|
||||||
|
UpdateNextAvailableIndex();
|
||||||
|
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||||||
|
{
|
||||||
|
NewItem = item,
|
||||||
|
NewIndex = itemIndex,
|
||||||
|
Type = Config.ItemDataChangeEventType.Remove
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemSlotNode? GetItemSlotNode(int index)
|
public int GetUsedCapacity()
|
||||||
{
|
{
|
||||||
if (_itemSlotNodes == null)
|
return _itemDictionary.Count;
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var safeIndex = GetSafeIndex(index);
|
|
||||||
return _itemSlotNodes[safeIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int RemoveItemFromItemSlot(int itemSlotIndex, int number)
|
public int GetTotalCapacity()
|
||||||
{
|
{
|
||||||
if (_itemSlotNodes == null) return number;
|
return totalCapacity;
|
||||||
var safeIndex = GetSafeIndex(itemSlotIndex);
|
|
||||||
if (safeIndex == UnknownIndex)
|
|
||||||
{
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemSlot = _itemSlotNodes[safeIndex];
|
|
||||||
return itemSlot.RemoveItem(number);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Gets a secure subscript index</para>
|
public void SelectNextItem()
|
||||||
/// <para>获取安全的下标索引</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="itemSlotIndex"></param>
|
|
||||||
/// <returns>
|
|
||||||
/// <para>-1 is returned on failure, and the index that does not result in an out-of-bounds subscript is returned on success</para>
|
|
||||||
/// <para>失败返回-1,成功返回不会导致下标越界的索引</para>
|
|
||||||
/// </returns>
|
|
||||||
private int GetSafeIndex(int itemSlotIndex)
|
|
||||||
{
|
{
|
||||||
if (_itemSlotNodes == null)
|
var count = totalCapacity;
|
||||||
{
|
|
||||||
return UnknownIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = _itemSlotNodes.Count;
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
//Prevents the dividend from being 0
|
|
||||||
//防止被除数为0
|
|
||||||
return UnknownIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return itemSlotIndex % count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ItemSlotNode? Match(IItem item)
|
|
||||||
{
|
|
||||||
//Find and return the first slot that can hold this item, if the list is null or not found, return null
|
|
||||||
//寻找并返回第一个遇到的可放置此物品的物品槽,若列表为空或不存在,将返回null
|
|
||||||
return _itemSlotNodes?.FirstOrDefault(itemSlotNode => itemSlotNode.CanAddItem(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ItemSlotNode? AddItemSlot(Node rootNode)
|
|
||||||
{
|
|
||||||
if (_itemSlotNodes == null || _itemSlotPackedScene == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemSlotNode = NodeUtils.InstantiatePackedScene<ItemSlotNode>(_itemSlotPackedScene);
|
|
||||||
if (itemSlotNode == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
NodeUtils.CallDeferredAddChild(rootNode, itemSlotNode);
|
|
||||||
if (SupportSelect)
|
|
||||||
{
|
|
||||||
itemSlotNode.IsSelect = _itemSlotNodes.Count == _selectIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemSlotNode.IsSelect = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_itemSlotNodes.Add(itemSlotNode);
|
|
||||||
return itemSlotNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SelectTheNextItemSlot()
|
|
||||||
{
|
|
||||||
if (_itemSlotNodes == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = _itemSlotNodes.Count;
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -198,17 +408,12 @@ public class UniversalItemContainer : IItemContainer
|
||||||
newSelectIndex = 0;
|
newSelectIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivateSelectItemSlot(oldSelectIndex, newSelectIndex);
|
PrivateSelectItem(oldSelectIndex, newSelectIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SelectThePreviousItemSlot()
|
public void SelectPreviousItem()
|
||||||
{
|
{
|
||||||
if (_itemSlotNodes == null)
|
var count = totalCapacity;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = _itemSlotNodes.Count;
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -221,77 +426,57 @@ public class UniversalItemContainer : IItemContainer
|
||||||
newSelectIndex = count - 1;
|
newSelectIndex = count - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivateSelectItemSlot(oldSelectIndex, newSelectIndex);
|
PrivateSelectItem(oldSelectIndex, newSelectIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Select an item slot</para>
|
/// <para>Private methods for selecting items</para>
|
||||||
/// <para>选中某个物品槽</para>
|
/// <para>选择物品的私有方法</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void PrivateSelectItemSlot(int oldSelectIndex, int newSelectIndex)
|
/// <param name="oldIndex"></param>
|
||||||
|
/// <param name="newIndex"></param>
|
||||||
|
private void PrivateSelectItem(int oldIndex, int newIndex)
|
||||||
{
|
{
|
||||||
if (!SupportSelect || _itemSlotNodes == null || oldSelectIndex == newSelectIndex)
|
if (!SupportSelect || oldIndex == newIndex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldItemSlotNode = _itemSlotNodes[oldSelectIndex];
|
//There is no need to broadcast placeholders when an event is invoked.
|
||||||
oldItemSlotNode.IsSelect = false;
|
//在调用事件时,无需广播占位符。
|
||||||
var newItemSlotNode = _itemSlotNodes[newSelectIndex];
|
var oldItem = GetItem(oldIndex);
|
||||||
newItemSlotNode.IsSelect = true;
|
if (oldItem != null)
|
||||||
HideItem(oldSelectIndex);
|
|
||||||
DisplayItem(newSelectIndex);
|
|
||||||
SelectedItemSlotChangeEvent?.Invoke(new SelectedItemSlotChangeEvent
|
|
||||||
{
|
{
|
||||||
NewItemSlotNode = newItemSlotNode,
|
oldItem.HideSelf();
|
||||||
OldItemSlotNode = oldItemSlotNode
|
oldItem.IsSelect = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//There is no need to broadcast placeholders when an event is invoked.
|
||||||
|
//在调用事件时,无需广播占位符。
|
||||||
|
var newItem = GetItem(newIndex);
|
||||||
|
if (newItem != null)
|
||||||
|
{
|
||||||
|
newItem.ShowSelf();
|
||||||
|
newItem.IsSelect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectIndex = newIndex;
|
||||||
|
SelectedItemChangeEvent?.Invoke(new SelectedItemChangeEvent
|
||||||
|
{
|
||||||
|
NewIndex = newIndex,
|
||||||
|
OldIndex = oldIndex,
|
||||||
|
NewItem = newItem,
|
||||||
|
OldItem = oldItem
|
||||||
});
|
});
|
||||||
_selectIndex = newSelectIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>HideItem</para>
|
|
||||||
/// <para>隐藏某个物品</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
private void HideItem(int index)
|
|
||||||
{
|
|
||||||
var oldItem = _itemSlotNodes?[index].GetItem();
|
|
||||||
if (oldItem is not Node2D oldNode2D) return;
|
|
||||||
oldNode2D.ProcessMode = Node.ProcessModeEnum.Disabled;
|
|
||||||
oldNode2D.Hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public void SelectItem(int index)
|
||||||
/// <para>Displays the items in an item slot</para>
|
|
||||||
/// <para>显示某个物品槽内的物品</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
///<para>This method can also be used to refresh items held by the character, for example when a new item is dragged to the current display location, then call this method to refresh items held by the character.</para>
|
|
||||||
///<para>此方法也可用于刷新角色手上持有的物品,例如当新的物品被拖动到当前显示位置,那么请调用此方法刷新角色持有的物品。</para>
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
private void DisplayItem(int index)
|
|
||||||
{
|
{
|
||||||
var item = _itemSlotNodes?[index].GetItem();
|
if (totalCapacity == 0 || index < 0)
|
||||||
if (item is not Node2D newNode2D) return;
|
|
||||||
newNode2D.ProcessMode = Node.ProcessModeEnum.Inherit;
|
|
||||||
newNode2D.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SelectItemSlot(int newSelectIndex)
|
|
||||||
{
|
|
||||||
if (newSelectIndex == _selectIndex)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
PrivateSelectItem(_selectIndex, index % totalCapacity);
|
||||||
var safeIndex = GetSafeIndex(newSelectIndex);
|
|
||||||
if (safeIndex == UnknownIndex)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateSelectItemSlot(_selectIndex, newSelectIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -45,6 +45,10 @@ public partial class GameSceneLoader : SceneLoaderTemplate
|
||||||
//加载抛射体容器
|
//加载抛射体容器
|
||||||
var projectileContainer = GetNode<Node2D>("ProjectileContainer");
|
var projectileContainer = GetNode<Node2D>("ProjectileContainer");
|
||||||
GameSceneDepend.ProjectileContainer = projectileContainer;
|
GameSceneDepend.ProjectileContainer = projectileContainer;
|
||||||
|
//Load magic container
|
||||||
|
//加载魔术容器
|
||||||
|
var magicContainer = GetNode<Node2D>("SpellContainer");
|
||||||
|
GameSceneDepend.SpellContainer = magicContainer;
|
||||||
//Load Packsack container
|
//Load Packsack container
|
||||||
//加载背包容器
|
//加载背包容器
|
||||||
var packsackContainer = GetNode<Node2D>("PacksackContainer");
|
var packsackContainer = GetNode<Node2D>("PacksackContainer");
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using ColdMint.scripts.inventory;
|
using ColdMint.scripts.inventory;
|
||||||
using ColdMint.scripts.utils;
|
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace ColdMint.scripts.loader.uiLoader;
|
namespace ColdMint.scripts.loader.uiLoader;
|
||||||
|
@ -22,6 +21,8 @@ public partial class PacksackUi : UiLoaderTemplate
|
||||||
|
|
||||||
private Button? _exitButton;
|
private Button? _exitButton;
|
||||||
|
|
||||||
|
private IItemContainerDisplay? _itemContainerDisplay;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>title</para>
|
/// <para>title</para>
|
||||||
/// <para>标题</para>
|
/// <para>标题</para>
|
||||||
|
@ -46,29 +47,10 @@ public partial class PacksackUi : UiLoaderTemplate
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_itemContainer = value;
|
_itemContainer = value;
|
||||||
PlaceItemSlot(value);
|
BindItemContainer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Place item slots according to item information</para>
|
|
||||||
/// <para>根据物品信息放置物品槽</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="itemContainer"></param>
|
|
||||||
private void PlaceItemSlot(IItemContainer? itemContainer)
|
|
||||||
{
|
|
||||||
if (_hFlowContainer == null || itemContainer == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeUtils.DeleteAllChild(_hFlowContainer);
|
|
||||||
foreach (var itemSlotNode in itemContainer)
|
|
||||||
{
|
|
||||||
itemSlotNode.Reparent(_hFlowContainer);
|
|
||||||
itemSlotNode.Show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>SetTile</para>
|
/// <para>SetTile</para>
|
||||||
|
@ -90,14 +72,24 @@ public partial class PacksackUi : UiLoaderTemplate
|
||||||
_packedScene = GD.Load<PackedScene>("res://prefab/ui/ItemSlot.tscn");
|
_packedScene = GD.Load<PackedScene>("res://prefab/ui/ItemSlot.tscn");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BindItemContainer()
|
||||||
|
{
|
||||||
|
if (ItemContainer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_itemContainerDisplay?.BindItemContainer(ItemContainer);
|
||||||
|
}
|
||||||
|
|
||||||
public override void InitializeUi()
|
public override void InitializeUi()
|
||||||
{
|
{
|
||||||
_hFlowContainer = GetNode<HFlowContainer>("HFlowContainer");
|
_hFlowContainer = GetNode<HFlowContainer>("HFlowContainer");
|
||||||
_titleLabel = GetNode<Label>("TitleLabel");
|
_titleLabel = GetNode<Label>("TitleLabel");
|
||||||
_exitButton = GetNode<Button>("ExitButton");
|
_exitButton = GetNode<Button>("ExitButton");
|
||||||
|
_itemContainerDisplay = new ItemSlotContainerDisplay(_hFlowContainer);
|
||||||
//If the item container was set before this node was placed in the node tree, load it here.
|
//If the item container was set before this node was placed in the node tree, load it here.
|
||||||
//若物品容器在此节点放置到节点树之前被设置了,那么在这里加载。
|
//若物品容器在此节点放置到节点树之前被设置了,那么在这里加载。
|
||||||
PlaceItemSlot(_itemContainer);
|
BindItemContainer();
|
||||||
SetTile(_title);
|
SetTile(_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
66
scripts/loader/uiLoader/SpellEditorUi.cs
Normal file
66
scripts/loader/uiLoader/SpellEditorUi.cs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
using ColdMint.scripts.inventory;
|
||||||
|
using ColdMint.scripts.map.events;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.loader.uiLoader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>SpellEditorUi</para>
|
||||||
|
/// <para>法术编辑器UI</para>
|
||||||
|
/// </summary>
|
||||||
|
public partial class SpellEditorUi : UiLoaderTemplate
|
||||||
|
{
|
||||||
|
private Button? _exitButton;
|
||||||
|
private IItemContainer? _projectileWeaponContainer;
|
||||||
|
private ItemSlotNode? _itemSlot;
|
||||||
|
private HFlowContainer? _flowContainer;
|
||||||
|
private IItemContainerDisplay? _itemContainerDisplay;
|
||||||
|
|
||||||
|
public override void InitializeUi()
|
||||||
|
{
|
||||||
|
_exitButton = GetNode<Button>("ExitButton");
|
||||||
|
_itemSlot = GetNode<ItemSlotNode>("ItemSlot");
|
||||||
|
_projectileWeaponContainer = new UniversalItemContainer(1);
|
||||||
|
_projectileWeaponContainer.AllowAddingItemByType(Config.ItemType.ProjectileWeapon);
|
||||||
|
_projectileWeaponContainer.ItemDataChangeEvent += OnItemDataChangeEvent;
|
||||||
|
_itemSlot.Update(_projectileWeaponContainer.GetPlaceHolderItem(0));
|
||||||
|
_flowContainer = GetNode<HFlowContainer>("HFlowContainer");
|
||||||
|
_itemContainerDisplay = new ItemSlotContainerDisplay(_flowContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnItemDataChangeEvent(ItemDataChangeEvent itemDataChangeEvent)
|
||||||
|
{
|
||||||
|
if (_itemSlot == null || _projectileWeaponContainer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var item = itemDataChangeEvent.NewItem;
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
item = _projectileWeaponContainer.GetPlaceHolderItem(itemDataChangeEvent.NewIndex);
|
||||||
|
}
|
||||||
|
item.IsSelect = false;
|
||||||
|
_itemSlot.Update(item);
|
||||||
|
_itemContainerDisplay?.BindItemContainer(item.SelfItemContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _ExitTree()
|
||||||
|
{
|
||||||
|
base._ExitTree();
|
||||||
|
if (_projectileWeaponContainer != null)
|
||||||
|
{
|
||||||
|
_projectileWeaponContainer.ItemDataChangeEvent -= OnItemDataChangeEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void LoadUiActions()
|
||||||
|
{
|
||||||
|
if (_exitButton != null)
|
||||||
|
{
|
||||||
|
_exitButton.Pressed += () =>
|
||||||
|
{
|
||||||
|
GameSceneDepend.DynamicUiGroup?.HideControl(this);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using ColdMint.scripts.inventory;
|
using ColdMint.scripts.debug;
|
||||||
|
using ColdMint.scripts.inventory;
|
||||||
using ColdMint.scripts.map.events;
|
using ColdMint.scripts.map.events;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
|
@ -15,7 +16,6 @@ public partial class ItemSpawn : Marker2D
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
base._Ready();
|
base._Ready();
|
||||||
|
|
||||||
EventBus.MapGenerationCompleteEvent += MapGenerationCompleteEvent;
|
EventBus.MapGenerationCompleteEvent += MapGenerationCompleteEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ public partial class ItemSpawn : Marker2D
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = ItemTypeManager.CreateItem(ItemId, this);
|
var item = ItemTypeManager.CreateItem(ItemId, this);
|
||||||
|
LogCat.LogWithFormat("generated_item_is_empty",LogCat.LogLabel.ItemSpawn,true,ItemId,item == null);
|
||||||
if (item is Node2D node2D)
|
if (item is Node2D node2D)
|
||||||
{
|
{
|
||||||
node2D.GlobalPosition = GlobalPosition;
|
node2D.GlobalPosition = GlobalPosition;
|
||||||
|
|
16
scripts/map/events/ItemDataChangeEvent.cs
Normal file
16
scripts/map/events/ItemDataChangeEvent.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using ColdMint.scripts.inventory;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.map.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Item Data Change Event</para>
|
||||||
|
/// <para>项目数据改变事件</para>
|
||||||
|
/// </summary>
|
||||||
|
public class ItemDataChangeEvent
|
||||||
|
{
|
||||||
|
public Config.ItemDataChangeEventType Type { get; set; }
|
||||||
|
public int OldIndex { get; set; }
|
||||||
|
public int NewIndex { get; set; }
|
||||||
|
public IItem? NewItem { get; set; }
|
||||||
|
public IItem? OldItem { get; set; }
|
||||||
|
}
|
26
scripts/map/events/SelectedItemChangeEvent.cs
Normal file
26
scripts/map/events/SelectedItemChangeEvent.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using ColdMint.scripts.inventory;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.map.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Selected item slot changes event</para>
|
||||||
|
/// <para>选中的物品槽改变事件</para>
|
||||||
|
/// </summary>
|
||||||
|
public class SelectedItemChangeEvent
|
||||||
|
{
|
||||||
|
public int NewIndex { get; set; }
|
||||||
|
|
||||||
|
public int OldIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Newly selected item</para>
|
||||||
|
/// <para>新选中的物品</para>
|
||||||
|
/// </summary>
|
||||||
|
public IItem? NewItem { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Lost the selected item</para>
|
||||||
|
/// <para>失去选中的物品</para>
|
||||||
|
/// </summary>
|
||||||
|
public IItem? OldItem { get; set; }
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
using ColdMint.scripts.inventory;
|
|
||||||
|
|
||||||
namespace ColdMint.scripts.map.events;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Selected item slot changes event</para>
|
|
||||||
/// <para>选中的物品槽改变事件</para>
|
|
||||||
/// </summary>
|
|
||||||
public class SelectedItemSlotChangeEvent
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// <para></para>
|
|
||||||
/// <para>新选中的物品槽</para>
|
|
||||||
/// </summary>
|
|
||||||
public ItemSlotNode? NewItemSlotNode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Lost the selected item slot</para>
|
|
||||||
/// <para>失去选中的物品槽</para>
|
|
||||||
/// </summary>
|
|
||||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
|
||||||
public ItemSlotNode? OldItemSlotNode { get; set; }
|
|
||||||
// ReSharper restore UnusedAutoPropertyAccessor.Global
|
|
||||||
}
|
|
|
@ -15,10 +15,27 @@ namespace ColdMint.scripts.pickable;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PickAbleTemplate : RigidBody2D, IItem
|
public partial class PickAbleTemplate : RigidBody2D, IItem
|
||||||
{
|
{
|
||||||
|
public int Index { get; set; }
|
||||||
//Do not export this field because the ID is specified within yaml.
|
//Do not export this field because the ID is specified within yaml.
|
||||||
//不要导出此字段,因为ID是在yaml内指定的。
|
//不要导出此字段,因为ID是在yaml内指定的。
|
||||||
public virtual string Id { get; set; } = "ID";
|
public virtual string Id { get; set; } = "ID";
|
||||||
[Export] protected Texture2D? UniqueIcon { get; set; }
|
[Export] protected Texture2D? UniqueIcon { get; set; }
|
||||||
|
|
||||||
|
public void ShowSelf()
|
||||||
|
{
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueueFreeSelf()
|
||||||
|
{
|
||||||
|
QueueFree();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HideSelf()
|
||||||
|
{
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
|
||||||
public Texture2D Icon => UniqueIcon ?? ItemTypeManager.DefaultIconOf(Id);
|
public Texture2D Icon => UniqueIcon ?? ItemTypeManager.DefaultIconOf(Id);
|
||||||
|
|
||||||
public new string Name
|
public new string Name
|
||||||
|
@ -30,8 +47,6 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool CanPutInPack => true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Owner</para>
|
/// <para>Owner</para>
|
||||||
/// <para>主人</para>
|
/// <para>主人</para>
|
||||||
|
@ -76,12 +91,105 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Picked { get; set; }
|
public bool Picked { get; set; }
|
||||||
|
|
||||||
public int MaxQuantity { get; set; }
|
public int MaxQuantity { get; set; } = 1;
|
||||||
|
public virtual int ItemType
|
||||||
|
{
|
||||||
|
get => Config.ItemType.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
|
public bool IsSelect
|
||||||
|
{
|
||||||
|
get => _isSelected;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isSelected == value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isSelected = value;
|
||||||
|
OnSelectChange(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IItemContainer? ItemContainer { get; set; }
|
||||||
|
public IItemContainer? SelfItemContainer { get; set; }
|
||||||
|
|
||||||
private Label? _tipLabel;
|
private Label? _tipLabel;
|
||||||
|
|
||||||
public virtual void Use(Node2D? owner, Vector2 targetGlobalPosition)
|
/// <summary>
|
||||||
|
/// <para></para>
|
||||||
|
/// <para>当选中状态发生改变时</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isSelected"></param>
|
||||||
|
protected virtual void OnSelectChange(bool isSelected)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IItem? CreateItem(int number)
|
||||||
|
{
|
||||||
|
if (number == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicate = Duplicate();
|
||||||
|
if (duplicate is PickAbleTemplate pickAbleTemplate)
|
||||||
|
{
|
||||||
|
pickAbleTemplate.CopyAttributes(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicate is not Node2D newNode2D)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
newNode2D.GlobalPosition = GlobalPosition;
|
||||||
|
if (duplicate is not IItem newItem)
|
||||||
|
{
|
||||||
|
duplicate.QueueFree();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number < 0)
|
||||||
|
{
|
||||||
|
newItem.Quantity = Quantity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newItem.Quantity = Math.Min(Quantity, number);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int MergeableItemCount(IItem other, int unallocatedQuantity)
|
||||||
|
{
|
||||||
|
var freeQuantity = MaxQuantity - Quantity;
|
||||||
|
if (freeQuantity == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other.Id != Id)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.Min(freeQuantity, unallocatedQuantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Use(Node2D? owner, Vector2 targetGlobalPosition)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnThrow(Vector2 velocity)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
|
@ -235,21 +343,14 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
||||||
/// <para>Please copy node properties within this function</para>
|
/// <para>Please copy node properties within this function</para>
|
||||||
/// <para>请在此函数内复制节点属性</para>
|
/// <para>请在此函数内复制节点属性</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="node"></param>
|
/// <param name="originalNode"></param>
|
||||||
public void CopyAttributes(Node node)
|
public void CopyAttributes(Node originalNode)
|
||||||
{
|
{
|
||||||
if (node is not PickAbleTemplate pickAbleTemplate)
|
if (originalNode is not PickAbleTemplate originalPickAbleTemplate)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Id = originalPickAbleTemplate.Id;
|
||||||
pickAbleTemplate.Id = Id;
|
SelfItemContainer = originalPickAbleTemplate.SelfItemContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Destroy()
|
|
||||||
{
|
|
||||||
QueueFree();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanStackWith(IItem item) => false;
|
|
||||||
}
|
}
|
55
scripts/projectile/ISpell.cs
Normal file
55
scripts/projectile/ISpell.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
using ColdMint.scripts.weapon;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.projectile;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Spell</para>
|
||||||
|
/// <para>法术</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>For projectile weapons</para>
|
||||||
|
///<para>用于抛射体武器</para>
|
||||||
|
/// </remarks>
|
||||||
|
public interface ISpell
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para>GetProjectile</para>
|
||||||
|
/// <para>获取抛射体</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
PackedScene? GetProjectile();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Modify Weapon</para>
|
||||||
|
/// <para>修改武器</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="projectileWeapon"></param>
|
||||||
|
void ModifyWeapon(ProjectileWeapon projectileWeapon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Restores the modified weapon properties</para>
|
||||||
|
/// <para>还原修改的武器属性</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="projectileWeapon"></param>
|
||||||
|
void RestoreWeapon(ProjectileWeapon projectileWeapon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Modify the projectile</para>
|
||||||
|
/// <para>修改抛射体</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">
|
||||||
|
///<para>What is the current projectile? For example, a weapon can fire three projectiles at once, with indexes 0,1,2</para>
|
||||||
|
///<para>当前抛射体是第几个?例如:武器可一下发射3个抛射体,索引为0,1,2</para>
|
||||||
|
/// </param>
|
||||||
|
/// <param name="projectile">
|
||||||
|
///<para>Projectile object</para>
|
||||||
|
///<para>抛射体对象</para>
|
||||||
|
/// </param>
|
||||||
|
/// <param name="velocity">
|
||||||
|
///<para>The velocity of the projectile</para>
|
||||||
|
///<para>抛射体的飞行速度</para>
|
||||||
|
/// </param>
|
||||||
|
void ModifyProjectile(int index,Projectile projectile, ref Vector2 velocity);
|
||||||
|
|
||||||
|
}
|
82
scripts/spell/MultipleFireSpell.cs
Normal file
82
scripts/spell/MultipleFireSpell.cs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
using ColdMint.scripts.projectile;
|
||||||
|
using ColdMint.scripts.utils;
|
||||||
|
using ColdMint.scripts.weapon;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.spell;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>MultipleFireSpell</para>
|
||||||
|
/// <para>多重射击法术</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>Use this spell to create shotgun effects</para>
|
||||||
|
///<para>通过此法术打造霰弹枪的效果</para>
|
||||||
|
/// </remarks>
|
||||||
|
public partial class MultipleFireSpell : SpellPickAble
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para>How many projectiles are generated per fire</para>
|
||||||
|
/// <para>每次开火生成多少个抛射体</para>
|
||||||
|
/// </summary>
|
||||||
|
[Export]
|
||||||
|
public int NumberOfProjectiles { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>RandomAngle</para>
|
||||||
|
/// <para>随机角度</para>
|
||||||
|
/// </summary>
|
||||||
|
[Export]
|
||||||
|
public bool RandomAngle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Unit radian</para>
|
||||||
|
/// <para>单位弧度</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>Unit radian of correction for the projectile Angle.Suppose there are three bullets fired at once, and this is the arc between the two bullets.</para>
|
||||||
|
///<para>对抛射体角度修正的单位弧度。假定有三颗子弹一次发射,这是两颗子弹之间的弧度。</para>
|
||||||
|
/// </remarks>
|
||||||
|
[Export]
|
||||||
|
public float UnitRadian { get; set; } = 0.069813f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>initial Radian</para>
|
||||||
|
/// <para>起始弧度</para>
|
||||||
|
/// </summary>
|
||||||
|
///<remarks>
|
||||||
|
///<para>The Angle of the first bullet, and subsequent bullets will be offset in unit radians.</para>
|
||||||
|
///<para>第一颗子弹的角度,随后的子弹会以单位弧度偏移。</para>
|
||||||
|
/// </remarks>
|
||||||
|
private float _initialRadian;
|
||||||
|
private float _maxRadian;
|
||||||
|
private int _oldNumberOfProjectiles;
|
||||||
|
public override void ModifyWeapon(ProjectileWeapon projectileWeapon)
|
||||||
|
{
|
||||||
|
base.ModifyWeapon(projectileWeapon);
|
||||||
|
_oldNumberOfProjectiles = projectileWeapon.NumberOfProjectiles;
|
||||||
|
projectileWeapon.NumberOfProjectiles = NumberOfProjectiles;
|
||||||
|
_initialRadian = -(NumberOfProjectiles / 2f * UnitRadian);
|
||||||
|
_maxRadian = NumberOfProjectiles * UnitRadian;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RestoreWeapon(ProjectileWeapon projectileWeapon)
|
||||||
|
{
|
||||||
|
base.RestoreWeapon(projectileWeapon);
|
||||||
|
projectileWeapon.NumberOfProjectiles = _oldNumberOfProjectiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ModifyProjectile(int index, Projectile projectile, ref Vector2 velocity)
|
||||||
|
{
|
||||||
|
base.ModifyProjectile(index, projectile, ref velocity);
|
||||||
|
if (RandomAngle)
|
||||||
|
{
|
||||||
|
velocity = velocity.Rotated(_initialRadian + _maxRadian * RandomUtils.Instance.NextSingle());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
velocity = velocity.Rotated(_initialRadian + UnitRadian * index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
55
scripts/spell/SpellPickAble.cs
Normal file
55
scripts/spell/SpellPickAble.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
using ColdMint.scripts.pickable;
|
||||||
|
using ColdMint.scripts.projectile;
|
||||||
|
using ColdMint.scripts.weapon;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.spell;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>magic</para>
|
||||||
|
/// <para>法术</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>For projectile weapons</para>
|
||||||
|
///<para>用于抛射体武器</para>
|
||||||
|
/// </remarks>
|
||||||
|
public partial class SpellPickAble : PickAbleTemplate, ISpell
|
||||||
|
{
|
||||||
|
[Export]
|
||||||
|
private string? _projectilePath;
|
||||||
|
|
||||||
|
private PackedScene? _projectileScene;
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
base._Ready();
|
||||||
|
if (_projectilePath != null)
|
||||||
|
{
|
||||||
|
_projectileScene = GD.Load<PackedScene>(_projectilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int ItemType
|
||||||
|
{
|
||||||
|
get => Config.ItemType.Spell;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PackedScene? GetProjectile()
|
||||||
|
{
|
||||||
|
return _projectileScene;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void ModifyWeapon(ProjectileWeapon projectileWeapon)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void RestoreWeapon(ProjectileWeapon projectileWeapon)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void ModifyProjectile(int index, Projectile projectile, ref Vector2 velocity)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -225,16 +225,21 @@ public static class NodeUtils
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Node FindContainerNode(Node childNode, Node defaultParentNode)
|
public static Node FindContainerNode(Node childNode, Node defaultParentNode)
|
||||||
{
|
{
|
||||||
if (GameSceneDepend.AiCharacterContainer!= null && childNode is AiCharacter)
|
if (GameSceneDepend.AiCharacterContainer != null && childNode is AiCharacter)
|
||||||
{
|
{
|
||||||
return GameSceneDepend.AiCharacterContainer;
|
return GameSceneDepend.AiCharacterContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GameSceneDepend.ProjectileContainer != null && childNode is Projectile)
|
if (GameSceneDepend.ProjectileContainer != null && childNode is Projectile)
|
||||||
{
|
{
|
||||||
return GameSceneDepend.ProjectileContainer;
|
return GameSceneDepend.ProjectileContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GameSceneDepend.SpellContainer != null && childNode is ISpell)
|
||||||
|
{
|
||||||
|
return GameSceneDepend.SpellContainer;
|
||||||
|
}
|
||||||
|
|
||||||
if (GameSceneDepend.WeaponContainer != null && childNode is WeaponTemplate)
|
if (GameSceneDepend.WeaponContainer != null && childNode is WeaponTemplate)
|
||||||
{
|
{
|
||||||
return GameSceneDepend.WeaponContainer;
|
return GameSceneDepend.WeaponContainer;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
using ColdMint.scripts.debug;
|
using ColdMint.scripts.debug;
|
||||||
|
using ColdMint.scripts.inventory;
|
||||||
|
using ColdMint.scripts.map.events;
|
||||||
using ColdMint.scripts.projectile;
|
using ColdMint.scripts.projectile;
|
||||||
using ColdMint.scripts.projectile.decorator;
|
|
||||||
using ColdMint.scripts.utils;
|
using ColdMint.scripts.utils;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
|
@ -23,157 +25,251 @@ public partial class ProjectileWeapon : WeaponTemplate
|
||||||
private Marker2D? _marker2D;
|
private Marker2D? _marker2D;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Scattering radians</para>
|
/// <para>Number of slots for ranged weapons</para>
|
||||||
/// <para>散射弧度</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>
|
/// </summary>
|
||||||
[Export]
|
[Export]
|
||||||
protected bool Sequentially { get; set; }
|
private int _numberSlots;
|
||||||
|
|
||||||
private int _projectileIndex;
|
/// <summary>
|
||||||
|
/// <para>How many projectiles are generated per fire</para>
|
||||||
|
/// <para>每次开火生成多少个抛射体</para>
|
||||||
|
/// </summary>
|
||||||
|
public int NumberOfProjectiles { get; set; } = 1;
|
||||||
|
|
||||||
|
private readonly List<ISpell> _spells = new();
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Saves the position of a spell with projectile generation</para>
|
||||||
|
/// <para>保存具有抛射体生成能力的法术位置</para>
|
||||||
|
/// </summary>
|
||||||
|
private readonly List<int> _spellProjectileIndexes = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Whether to fire spells in sequence</para>
|
||||||
|
/// <para>是否按顺序发射法术</para>
|
||||||
|
/// </summary>
|
||||||
|
[Export]
|
||||||
|
private bool _fireSequentially;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>The index used the last time a spell was cast</para>
|
||||||
|
/// <para>上次发射法术时采用的索引</para>
|
||||||
|
/// </summary>
|
||||||
|
private int _lastUsedProjectileMagicIndex = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Get next index</para>
|
||||||
|
/// <para>获取下次索引</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private int GetNextProjectileMagicIndex()
|
||||||
|
{
|
||||||
|
if (_fireSequentially)
|
||||||
|
{
|
||||||
|
_lastUsedProjectileMagicIndex++;
|
||||||
|
if (_lastUsedProjectileMagicIndex >= _spellProjectileIndexes.Count)
|
||||||
|
{
|
||||||
|
_lastUsedProjectileMagicIndex = 0;
|
||||||
|
}
|
||||||
|
return _lastUsedProjectileMagicIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return RandomUtils.Instance.Next(0, _spellProjectileIndexes.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Gets the loading range of the spell</para>
|
||||||
|
/// <para>获取法术的加载范围</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
///<para>Return array meaning: 0, starting position 1, ending position 2, with projectile generated spell position, length 3.</para>
|
||||||
|
///<para>返回数组的含义为:0,起始位置1,结束位置2,带有抛射体生成的法术位置,长度为3。</para>
|
||||||
|
/// </returns>
|
||||||
|
private int[] GetSpellScope()
|
||||||
|
{
|
||||||
|
var index = GetNextProjectileMagicIndex();
|
||||||
|
var projectileSpellPosition = _spellProjectileIndexes[index];
|
||||||
|
var endIndex = projectileSpellPosition;
|
||||||
|
var startIndex = 0;
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
//And the previous index can set the starting position.(The starting position is increased by 1 to avoid using spells with projectile generation as starting points.)
|
||||||
|
//还有前面的索引可设定起始位置。(这里起始位置加1是为了避免 具有抛射体生成能力的法术 作为起点。)
|
||||||
|
startIndex = _spellProjectileIndexes[index - 1] + 1;
|
||||||
|
}
|
||||||
|
if (index == _spellProjectileIndexes.Count - 1)
|
||||||
|
{
|
||||||
|
endIndex = _spells.Count - 1;
|
||||||
|
}
|
||||||
|
return [startIndex, endIndex, projectileSpellPosition];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int ItemType
|
||||||
|
{
|
||||||
|
get => Config.ItemType.ProjectileWeapon;
|
||||||
|
}
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
base._Ready();
|
base._Ready();
|
||||||
_marker2D = GetNode<Marker2D>("Marker2D");
|
_marker2D = GetNode<Marker2D>("Marker2D");
|
||||||
|
if (SelfItemContainer == null)
|
||||||
|
{
|
||||||
|
SelfItemContainer = new UniversalItemContainer(_numberSlots);
|
||||||
|
SelfItemContainer.AllowAddingItemByType(Config.ItemType.Spell);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>GetNextProjectileScene</para>
|
/// <para>Update spell cache</para>
|
||||||
/// <para>获取下一个抛射体</para>
|
/// <para>更新法术缓存</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <remarks>
|
||||||
private PackedScene GetNextProjectileScene()
|
///<para>This will parse available spells from inside the item container.</para>
|
||||||
|
///<para>这将从物品容器内解析可用的法术。</para>
|
||||||
|
/// </remarks>
|
||||||
|
public void UpdateSpellCache()
|
||||||
{
|
{
|
||||||
if (Sequentially)
|
if (SelfItemContainer == null)
|
||||||
{
|
{
|
||||||
_projectileIndex = (_projectileIndex + 1) % ProjectileScenes.Length;
|
return;
|
||||||
return ProjectileScenes[_projectileIndex];
|
|
||||||
}
|
}
|
||||||
else
|
_spells.Clear();
|
||||||
|
_spellProjectileIndexes.Clear();
|
||||||
|
var totalCapacity = SelfItemContainer.GetTotalCapacity();
|
||||||
|
for (var i = 0; i < totalCapacity; i++)
|
||||||
{
|
{
|
||||||
return ProjectileScenes[RandomUtils.Instance.Next(ProjectileScenes.Length)];
|
var item = SelfItemContainer.GetItem(i);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (item is not ISpell spell)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_spells.Add(spell);
|
||||||
|
var packedScene = spell.GetProjectile();
|
||||||
|
if (packedScene != null)
|
||||||
|
{
|
||||||
|
//Has the ability to generate projectiles.
|
||||||
|
//拥有抛射体生成能力。
|
||||||
|
_spellProjectileIndexes.Add(_spells.Count - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnItemDataChangeEvent(ItemDataChangeEvent itemDataChangeEvent)
|
||||||
/// <summary>
|
|
||||||
/// <para>GetRandomAngle</para>
|
|
||||||
/// <para>获取随机的偏移弧度</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
private float GetRandomAngle()
|
|
||||||
{
|
{
|
||||||
if (OffsetAngle == 0)
|
UpdateSpellCache();
|
||||||
{
|
|
||||||
//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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void _EnterTree()
|
||||||
|
{
|
||||||
|
base._EnterTree();
|
||||||
|
if (SelfItemContainer != null)
|
||||||
|
{
|
||||||
|
SelfItemContainer.ItemDataChangeEvent += OnItemDataChangeEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void DoFire(Node2D? owner, Vector2 enemyGlobalPosition)
|
public override void _ExitTree()
|
||||||
|
{
|
||||||
|
base._ExitTree();
|
||||||
|
if (SelfItemContainer != null)
|
||||||
|
{
|
||||||
|
SelfItemContainer.ItemDataChangeEvent -= OnItemDataChangeEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool DoFire(Node2D? owner, Vector2 enemyGlobalPosition)
|
||||||
{
|
{
|
||||||
if (owner == null)
|
if (owner == null)
|
||||||
{
|
{
|
||||||
LogCat.LogError("owner_is_null");
|
LogCat.LogError("owner_is_null");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_marker2D == null)
|
if (_marker2D == null)
|
||||||
{
|
{
|
||||||
LogCat.LogError("marker2d_is_null");
|
LogCat.LogError("marker2d_is_null");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GameSceneDepend.ProjectileContainer == null)
|
if (GameSceneDepend.ProjectileContainer == null)
|
||||||
{
|
{
|
||||||
LogCat.LogError("projectile_container_is_null");
|
LogCat.LogError("projectile_container_is_null");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (_spellProjectileIndexes.Count == 0)
|
||||||
//Empty list check
|
|
||||||
//空列表检查
|
|
||||||
if (ProjectileScenes is [])
|
|
||||||
{
|
{
|
||||||
LogCat.LogError("projectiles_is_empty");
|
LogCat.LogError("projectile_generate_magic_is_null");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
var spellScope = GetSpellScope();
|
||||||
//Get the first projectile
|
LogCat.LogWithFormat("projectile_weapon_range", LogCat.LogLabel.Default, true, string.Join(",", spellScope), _fireSequentially, _lastUsedProjectileMagicIndex);
|
||||||
//获取第一个抛射体
|
//The final spell is a projectile generator.
|
||||||
var projectileScene = GetNextProjectileScene();
|
//最后的法术是拥有抛射体生成能力的。
|
||||||
for (int i = 0; i < NumberOfProjectiles; i++)
|
var spellProjectile = _spells[spellScope[2]];
|
||||||
|
var packedScene = spellProjectile.GetProjectile();
|
||||||
|
if (packedScene == null)
|
||||||
{
|
{
|
||||||
var projectile = NodeUtils.InstantiatePackedScene<Projectile>(projectileScene);
|
LogCat.LogError("projectile_scene_is_null");
|
||||||
if (projectile == null) return;
|
return false;
|
||||||
if (Config.IsDebug())
|
}
|
||||||
|
ModifyWeapon(spellScope);
|
||||||
|
for (var i = 0; i < NumberOfProjectiles; i++)
|
||||||
|
{
|
||||||
|
var projectile = NodeUtils.InstantiatePackedScene<Projectile>(packedScene);
|
||||||
|
if (projectile == null)
|
||||||
{
|
{
|
||||||
var nodeSpawnOnKillCharacterDecorator = new NodeSpawnOnKillCharacterDecorator
|
LogCat.LogError("projectile_is_null");
|
||||||
{
|
RestoreWeapon(spellScope);
|
||||||
DefaultParentNode = this,
|
return false;
|
||||||
PackedScenePath = "res://prefab/entitys/BlackenedAboriginalWarrior.tscn"
|
}
|
||||||
};
|
var velocity = _marker2D.GlobalPosition.DirectionTo(enemyGlobalPosition) * projectile.Speed;
|
||||||
projectile.AddProjectileDecorator(nodeSpawnOnKillCharacterDecorator);
|
for (var s = spellScope[0]; s <= spellScope[1]; s++)
|
||||||
|
{
|
||||||
|
var spell = _spells[s];
|
||||||
|
spell.ModifyProjectile(i, projectile, ref velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeUtils.CallDeferredAddChild(GameSceneDepend.ProjectileContainer, projectile);
|
NodeUtils.CallDeferredAddChild(GameSceneDepend.ProjectileContainer, projectile);
|
||||||
projectile.Owner = owner;
|
projectile.Owner = owner;
|
||||||
projectile.TargetNode = GameSceneDepend.TemporaryTargetNode;
|
projectile.TargetNode = GameSceneDepend.TemporaryTargetNode;
|
||||||
projectile.Velocity =
|
projectile.Velocity = velocity;
|
||||||
(_marker2D.GlobalPosition.DirectionTo(enemyGlobalPosition) * projectile.Speed)
|
|
||||||
.Rotated(GetRandomAngle());
|
|
||||||
projectile.Position = _marker2D.GlobalPosition;
|
projectile.Position = _marker2D.GlobalPosition;
|
||||||
}
|
}
|
||||||
|
RestoreWeapon(spellScope);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Modify weapon attributes</para>
|
||||||
|
/// <para>修改武器属性</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="spellScope"></param>
|
||||||
|
private void ModifyWeapon(int[] spellScope)
|
||||||
|
{
|
||||||
|
for (var i = spellScope[0]; i <= spellScope[1]; i++)
|
||||||
|
{
|
||||||
|
var spell = _spells[i];
|
||||||
|
spell.ModifyWeapon(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Restores modifications to weapons</para>
|
||||||
|
/// <para>恢复对武器的修改</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="spellScope"></param>
|
||||||
|
private void RestoreWeapon(int[] spellScope)
|
||||||
|
{
|
||||||
|
for (var i = spellScope[0]; i <= spellScope[1]; i++)
|
||||||
|
{
|
||||||
|
var spell = _spells[i];
|
||||||
|
spell.RestoreWeapon(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,9 +25,9 @@ public abstract partial class WeaponTemplate : PickAbleTemplate
|
||||||
_audioStreamPlayer2D = GetNodeOrNull<AudioStreamPlayer2D>("Marker2D/AudioStreamPlayer2D");
|
_audioStreamPlayer2D = GetNodeOrNull<AudioStreamPlayer2D>("Marker2D/AudioStreamPlayer2D");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Use(Node2D? owner, Vector2 targetGlobalPosition)
|
public override bool Use(Node2D? owner, Vector2 targetGlobalPosition)
|
||||||
{
|
{
|
||||||
Fire(owner, targetGlobalPosition);
|
return Fire(owner, targetGlobalPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,34 +77,38 @@ public abstract partial class WeaponTemplate : PickAbleTemplate
|
||||||
///<para>敌人所在位置</para>
|
///<para>敌人所在位置</para>
|
||||||
/// </param>
|
/// </param>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void Fire(Node2D? owner, Vector2 enemyGlobalPosition)
|
public bool Fire(Node2D? owner, Vector2 enemyGlobalPosition)
|
||||||
{
|
{
|
||||||
var nowTime = DateTime.Now;
|
var nowTime = DateTime.Now;
|
||||||
//If the present time minus the time of the last fire is less than the interval between fires, it means that the fire cannot be fired yet.
|
//If the present time minus the time of the last fire is less than the interval between fires, it means that the fire cannot be fired yet.
|
||||||
//如果现在时间减去上次开火时间小于开火间隔,说明还不能开火。
|
//如果现在时间减去上次开火时间小于开火间隔,说明还不能开火。
|
||||||
if (_lastFiringTime != null && nowTime - _lastFiringTime < _firingInterval)
|
if (_lastFiringTime != null && nowTime - _lastFiringTime < _firingInterval)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (owner is CharacterTemplate characterTemplate)
|
|
||||||
{
|
|
||||||
//We check the recoil of the weapon before each firing.
|
|
||||||
//我们在每次开火之前,检查武器的后坐力。
|
|
||||||
if (_recoilStrength != 0)
|
|
||||||
{
|
|
||||||
characterTemplate.AddForce(enemyGlobalPosition.DirectionTo(characterTemplate.GlobalPosition) * _recoilStrength * Config.CellSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_audioStreamPlayer2D?.Play();
|
|
||||||
DoFire(owner, enemyGlobalPosition);
|
|
||||||
_lastFiringTime = nowTime;
|
_lastFiringTime = nowTime;
|
||||||
|
var result = DoFire(owner, enemyGlobalPosition);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
if (owner is CharacterTemplate characterTemplate)
|
||||||
|
{
|
||||||
|
if (_recoilStrength != 0)
|
||||||
|
{
|
||||||
|
characterTemplate.AddForce(enemyGlobalPosition.DirectionTo(characterTemplate.GlobalPosition) * _recoilStrength * Config.CellSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_audioStreamPlayer2D?.Play();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Execute fire</para>
|
/// <para>Execute fire</para>
|
||||||
/// <para>执行开火</para>
|
/// <para>执行开火</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract void DoFire(Node2D? owner, Vector2 enemyGlobalPosition);
|
/// <returns>
|
||||||
|
///<para>Return Is the fire successful?</para>
|
||||||
|
///<para>返回是否成功开火?</para>
|
||||||
|
/// </returns>
|
||||||
|
protected abstract bool DoFire(Node2D? owner, Vector2 enemyGlobalPosition);
|
||||||
}
|
}
|
BIN
sprites/furnitures/SpellEditor.png
Normal file
BIN
sprites/furnitures/SpellEditor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
34
sprites/furnitures/SpellEditor.png.import
Normal file
34
sprites/furnitures/SpellEditor.png.import
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cyciw4drjvrs8"
|
||||||
|
path="res://.godot/imported/SpellEditor.png-a8650f7f0135f5be6d7d33b867de82ab.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sprites/furnitures/SpellEditor.png"
|
||||||
|
dest_files=["res://.godot/imported/SpellEditor.png-a8650f7f0135f5be6d7d33b867de82ab.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
Binary file not shown.
Before Width: | Height: | Size: 310 B After Width: | Height: | Size: 383 B |
BIN
sprites/projectile/x3.png
Normal file
BIN
sprites/projectile/x3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 272 B |
34
sprites/projectile/x3.png.import
Normal file
34
sprites/projectile/x3.png.import
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://mb5yijtw7sw5"
|
||||||
|
path="res://.godot/imported/x3.png-096b2fc27f9da2412a5f72aad28677de.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sprites/projectile/x3.png"
|
||||||
|
dest_files=["res://.godot/imported/x3.png-096b2fc27f9da2412a5f72aad28677de.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
Loading…
Reference in New Issue
Block a user