add item equipping/unequipping

This commit is contained in:
Rowan 2025-06-29 22:13:30 -04:00
parent 5647763c96
commit 89bcba0ff2
30 changed files with 598 additions and 165 deletions

View file

@ -330,6 +330,7 @@ func _get_child_rect(child : Control) -> Rect2:
child.position = child_pos child.position = child_pos
return Rect2(child_pos, child_size) return Rect2(child_pos, child_size)
func _get_control_children() -> Array[Control]: func _get_control_children() -> Array[Control]:
var ret : Array[Control] var ret : Array[Control]
ret.assign(get_children().filter(func(child : Node): return child is Control && child.visible)) ret.assign(get_children().filter(func(child : Node): return child is Control && child.visible))

View file

@ -21,4 +21,6 @@ func clone() -> IndexedIterator:
return IndexedIterator.new(_iter.clone()) return IndexedIterator.new(_iter.clone())
func next() -> Option: func next() -> Option:
return _iter.next().map(_add_index) var next_value = _iter.next().map(_add_index)
_index += 1
return next_value

View file

@ -70,6 +70,10 @@ func last() -> Option:
last = Option.some(value) last = Option.some(value)
return last return last
func nth(index: int) -> Option:
advance_by(index - 1)
return self.next()
func map(fn: Callable) -> MapIterator: func map(fn: Callable) -> MapIterator:
return MapIterator.new(self, fn) return MapIterator.new(self, fn)

View file

@ -34,6 +34,24 @@ static func to_str(value: Variant) -> String:
return "%s %s" % [name, to_str(props)] return "%s %s" % [name, to_str(props)]
_: return str(value) _: return str(value)
static func _always_pass(_any: Variant) -> bool:
return true
static func notify(node: Node, notification: int, up: bool = false):
notify_while(node, notification, _always_pass, up)
static func notify_while(node: Node, notification: int, predicate: Callable = _always_pass, up: bool = false):
node.notification(notification)
if up:
var parent = node.get_parent()
if parent != null and predicate.call(parent):
notify_while(parent, notification, predicate, up)
else:
for child in node.get_children():
if predicate.call(child):
notify_while(child, notification, predicate)
static func propagate(node: Node, fn: StringName, args: Array, call_on_self: bool = true): static func propagate(node: Node, fn: StringName, args: Array, call_on_self: bool = true):
if call_on_self and node.has_method(fn): if call_on_self and node.has_method(fn):
node.callv(fn, args) node.callv(fn, args)

View file

@ -0,0 +1,8 @@
[gd_resource type="AtlasTexture" load_steps=2 format=3 uid="uid://dngw0c0dc6u7x"]
[ext_resource type="Texture2D" uid="uid://d3ergv453flsw" path="res://assets/sprites.png" id="1_gm5ao"]
[resource]
atlas = ExtResource("1_gm5ao")
region = Rect2(256, 128, 64, 64)
margin = Rect2(-10, -12, -20, -20)

View file

@ -0,0 +1,8 @@
[gd_resource type="AtlasTexture" load_steps=2 format=3 uid="uid://dvxrifa0iu8ql"]
[ext_resource type="Texture2D" uid="uid://d3ergv453flsw" path="res://assets/sprites.png" id="1_y5otb"]
[resource]
atlas = ExtResource("1_y5otb")
region = Rect2(192, 256, 64, 64)
margin = Rect2(0, -10, 0, -12)

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bb35noqd4affs"
path="res://.godot/imported/pistol_icon.png-b7372c1e15b929d59375290c822ef47c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/pistol_icon.png"
dest_files=["res://.godot/imported/pistol_icon.png-b7372c1e15b929d59375290c822ef47c.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

BIN
godot/assets/sprites.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d3ergv453flsw"
path="res://.godot/imported/sprites.png-bb36f92a96c2b9ad48a8528dbf01963e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites.png"
dest_files=["res://.godot/imported/sprites.png-bb36f92a96c2b9ad48a8528dbf01963e.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

View file

@ -23,6 +23,10 @@ PhantomCameraManager="*res://addons/phantom_camera/scripts/managers/phantom_came
settings/addon_root_folder="res://addons/debug_draw_3d/addons/debug_draw_3d" settings/addon_root_folder="res://addons/debug_draw_3d/addons/debug_draw_3d"
[display]
window/stretch/mode="canvas_items"
[editor_plugins] [editor_plugins]
enabled=PackedStringArray("res://addons/FreeControl/plugin.cfg", "res://addons/godot_object_serializer/plugin.cfg", "res://addons/phantom_camera/plugin.cfg") enabled=PackedStringArray("res://addons/FreeControl/plugin.cfg", "res://addons/godot_object_serializer/plugin.cfg", "res://addons/phantom_camera/plugin.cfg")

View file

@ -1,12 +1,12 @@
[gd_resource type="Resource" script_class="Item" load_steps=3 format=3 uid="uid://cqfnwpmo4fyv4"] [gd_resource type="Resource" script_class="Item" load_steps=3 format=3 uid="uid://cqfnwpmo4fyv4"]
[ext_resource type="Script" uid="uid://bqprls343ue6e" path="res://src/item.gd" id="1_7ur50"] [ext_resource type="Script" uid="uid://bqprls343ue6e" path="res://src/item.gd" id="1_7ur50"]
[ext_resource type="Texture2D" uid="uid://djmxd4580q6xs" path="res://icon.svg" id="1_ykwyx"] [ext_resource type="Texture2D" uid="uid://dngw0c0dc6u7x" path="res://assets/icons/key_icon.tres" id="1_ykwyx"]
[resource] [resource]
script = ExtResource("1_7ur50") script = ExtResource("1_7ur50")
name = "DebugKey" name = "Debug Key"
description = "whatever" description = "An unusual key card."
max_stack = 1 max_stack = 1
icon = ExtResource("1_ykwyx") icon = ExtResource("1_ykwyx")
metadata/_custom_type_script = "uid://bqprls343ue6e" metadata/_custom_type_script = "uid://bqprls343ue6e"

View file

@ -1,12 +1,16 @@
[gd_resource type="Resource" script_class="Equipment" load_steps=3 format=3 uid="uid://crgwey6ibaf2h"] [gd_resource type="Resource" script_class="EquippableItem" load_steps=5 format=3 uid="uid://crgwey6ibaf2h"]
[ext_resource type="PackedScene" uid="uid://isqkayrtr7t8" path="res://scenes/pistol.tscn" id="1_6ttue"] [ext_resource type="PackedScene" uid="uid://isqkayrtr7t8" path="res://scenes/pistol.tscn" id="1_6ttue"]
[ext_resource type="Texture2D" uid="uid://dvxrifa0iu8ql" path="res://assets/icons/pistol_icon.tres" id="1_112eb"]
[ext_resource type="PackedScene" uid="uid://dfljhd4e3l6j2" path="res://scenes/pistol_preview.tscn" id="1_gintf"]
[ext_resource type="Script" uid="uid://c710qg683rqbc" path="res://src/equippable_item.gd" id="1_lqglu"] [ext_resource type="Script" uid="uid://c710qg683rqbc" path="res://src/equippable_item.gd" id="1_lqglu"]
[resource] [resource]
script = ExtResource("1_lqglu") script = ExtResource("1_lqglu")
scene = ExtResource("1_6ttue")
name = "Pistol" name = "Pistol"
description = "" description = "A semi-automatic pistol. Fires 10mm ammunition from a 10-round magazine. Easy to use, high rate of fire."
max_stack = 1 max_stack = 1
icon = ExtResource("1_112eb")
scene = ExtResource("1_6ttue")
preview_scene = ExtResource("1_gintf")
metadata/_custom_type_script = "uid://c710qg683rqbc" metadata/_custom_type_script = "uid://c710qg683rqbc"

45
godot/scenes/box_ui.tres Normal file
View file

@ -0,0 +1,45 @@
[gd_resource type="Theme" load_steps=6 format=3 uid="uid://bb6tvyqikcf3r"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_odwru"]
bg_color = Color(0.6, 0.6, 0.6, 0)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_t6lws"]
bg_color = Color(0.92549, 0.243137, 0.0392157, 1)
[sub_resource type="SystemFont" id="SystemFont_t6lws"]
font_names = PackedStringArray("Monospace")
font_weight = 600
keep_rounding_remainders = false
multichannel_signed_distance_field = true
[sub_resource type="SystemFont" id="SystemFont_odwru"]
font_names = PackedStringArray("monospace")
font_weight = 500
multichannel_signed_distance_field = true
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_4gq11"]
bg_color = Color(0.6, 0.6, 0.6, 0)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.92549, 0.243137, 0.0392157, 1)
[resource]
BoxContent/base_type = &"Panel"
BoxContent/styles/panel = SubResource("StyleBoxFlat_odwru")
BoxHeader/base_type = &"PanelContainer"
BoxHeader/styles/panel = SubResource("StyleBoxFlat_t6lws")
BoxHeaderLabel/base_type = &"Label"
BoxHeaderLabel/colors/font_color = Color(0, 0, 0, 1)
BoxHeaderLabel/font_sizes/font_size = 8
BoxHeaderLabel/fonts/font = SubResource("SystemFont_t6lws")
HBoxContainer/constants/separation = 8
Label/font_sizes/font_size = 12
Label/fonts/font = SubResource("SystemFont_odwru")
MarginContainer/constants/margin_bottom = 4
MarginContainer/constants/margin_left = 4
MarginContainer/constants/margin_right = 4
MarginContainer/constants/margin_top = 4
PanelContainer/styles/panel = SubResource("StyleBoxFlat_4gq11")
RichTextLabel/colors/default_color = Color(0.0745098, 0.0745098, 0.0745098, 1)

28
godot/scenes/box_ui.tscn Normal file
View file

@ -0,0 +1,28 @@
[gd_scene load_steps=2 format=3 uid="uid://cidwsnulug6fl"]
[ext_resource type="Theme" uid="uid://bb6tvyqikcf3r" path="res://scenes/box_ui.tres" id="1_e1aew"]
[node name="Box" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_e1aew")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="Header" type="PanelContainer" parent="VBoxContainer"]
layout_mode = 2
theme = ExtResource("1_e1aew")
theme_type_variation = &"BoxHeader"
[node name="TitleContainer" type="MarginContainer" parent="VBoxContainer/Header"]
layout_mode = 2
[node name="Body" type="PanelContainer" parent="VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme = ExtResource("1_e1aew")
theme_type_variation = &"BoxContent"

View file

@ -0,0 +1,8 @@
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://bardurtuo08ox"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_vxpal"]
bg_color = Color(1, 1, 1, 1)
[resource]
BoxContainer/constants/separation = 4
PanelContainer/styles/panel = SubResource("StyleBoxFlat_vxpal")

View file

@ -1,12 +1,15 @@
[gd_scene load_steps=12 format=3 uid="uid://cn7tgd4y67wnd"] [gd_scene load_steps=14 format=3 uid="uid://cn7tgd4y67wnd"]
[ext_resource type="Script" uid="uid://qvoqvonnxwfc" path="res://src/inventory_ui.gd" id="1_a0rpf"] [ext_resource type="Script" uid="uid://qvoqvonnxwfc" path="res://src/inventory_ui.gd" id="1_a0rpf"]
[ext_resource type="Script" uid="uid://bx4wxlm5mv268" path="res://src/root_control.gd" id="1_as33y"] [ext_resource type="Script" uid="uid://bx4wxlm5mv268" path="res://src/root_control.gd" id="1_as33y"]
[ext_resource type="Theme" uid="uid://bb6tvyqikcf3r" path="res://scenes/box_ui.tres" id="1_sr8g6"]
[ext_resource type="Resource" uid="uid://bllq6ri54q3ne" path="res://resources/player_inventory.tres" id="2_as33y"] [ext_resource type="Resource" uid="uid://bllq6ri54q3ne" path="res://resources/player_inventory.tres" id="2_as33y"]
[ext_resource type="PackedScene" uid="uid://gn8k2ir47n1m" path="res://scenes/inventory_item.tscn" id="3_tg4gd"] [ext_resource type="PackedScene" uid="uid://gn8k2ir47n1m" path="res://scenes/inventory_item.tscn" id="3_tg4gd"]
[ext_resource type="Script" uid="uid://bpwbaanf6w65h" path="res://src/index_carousel.gd" id="5_uae8j"] [ext_resource type="Script" uid="uid://bpwbaanf6w65h" path="res://src/index_carousel.gd" id="5_uae8j"]
[ext_resource type="Script" uid="uid://c8m7eq6a3tpgu" path="res://src/item_details_ui.gd" id="6_s887n"] [ext_resource type="Script" uid="uid://c8m7eq6a3tpgu" path="res://src/item_details_ui.gd" id="6_s887n"]
[ext_resource type="Resource" uid="uid://cxm3s081hnw8l" path="res://resources/player_equipment.tres" id="7_s887n"] [ext_resource type="Resource" uid="uid://cxm3s081hnw8l" path="res://resources/player_equipment.tres" id="7_s887n"]
[ext_resource type="PackedScene" uid="uid://cidwsnulug6fl" path="res://scenes/box_ui.tscn" id="8_g71ie"]
[ext_resource type="Script" uid="uid://c4gfdpuc14mpq" path="res://src/cancel_button.gd" id="10_p7iul"]
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_as33y"] [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_as33y"]
size = Vector2(128, 128) size = Vector2(128, 128)
@ -17,9 +20,6 @@ size = Vector2(128, 128)
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_usnyx"] [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_usnyx"]
size = Vector2(128, 64) size = Vector2(128, 64)
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_sebc8"]
size = Vector2(256, 256)
[node name="Inventory" type="Control"] [node name="Inventory" type="Control"]
layout_mode = 3 layout_mode = 3
anchors_preset = 15 anchors_preset = 15
@ -30,6 +30,7 @@ grow_vertical = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
focus_mode = 2 focus_mode = 2
theme = ExtResource("1_sr8g6")
script = ExtResource("1_as33y") script = ExtResource("1_as33y")
open_action = &"open_inventory" open_action = &"open_inventory"
close_action = &"ui_close_inventory" close_action = &"ui_close_inventory"
@ -43,16 +44,23 @@ grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
color = Color(0.147672, 0.147672, 0.147672, 1) color = Color(0.147672, 0.147672, 0.147672, 1)
[node name="Contents" type="VBoxContainer" parent="."] [node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
[node name="Contents" type="VBoxContainer" parent="MarginContainer"]
layout_mode = 2
alignment = 1 alignment = 1
[node name="InventoryUI" type="Control" parent="Contents" node_paths=PackedStringArray("carousel")] [node name="MarginContainer" type="MarginContainer" parent="MarginContainer/Contents"]
layout_mode = 2
[node name="InventoryList" type="Control" parent="MarginContainer/Contents/MarginContainer" node_paths=PackedStringArray("carousel")]
custom_minimum_size = Vector2(0, 150)
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
script = ExtResource("1_a0rpf") script = ExtResource("1_a0rpf")
@ -61,63 +69,182 @@ item_scene = ExtResource("3_tg4gd")
carousel = NodePath("Carousel") carousel = NodePath("Carousel")
metadata/_custom_type_script = "uid://qvoqvonnxwfc" metadata/_custom_type_script = "uid://qvoqvonnxwfc"
[node name="Carousel" type="Container" parent="Contents/InventoryUI"] [node name="Carousel" type="Container" parent="MarginContainer/Contents/MarginContainer/InventoryList"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
scale = Vector2(1, 1.01)
script = ExtResource("5_uae8j") script = ExtResource("5_uae8j")
with_drag = true with_drag = true
with_clamp = true with_clamp = true
display_range = 5 item_size = Vector2(150, 100)
item_seperation = 64
snap_behavior = 2 snap_behavior = 2
paging_requirement = 100 paging_requirement = 100
metadata/_custom_type_script = "uid://bpwbaanf6w65h" metadata/_custom_type_script = "uid://bpwbaanf6w65h"
[node name="ItemDetails" type="HBoxContainer" parent="Contents" node_paths=PackedStringArray("icon_image", "description_label")] [node name="Details" type="HBoxContainer" parent="MarginContainer/Contents" node_paths=PackedStringArray("viewport", "description_label", "action_menu")]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
script = ExtResource("6_s887n") script = ExtResource("6_s887n")
equipment = ExtResource("7_s887n") equipment = ExtResource("7_s887n")
icon_image = NodePath("Display/TextureRect") viewport = NodePath("ItemDisplay/VBoxContainer/Body/Display/SubViewport")
description_label = NodePath("Description/Label") description_label = NodePath("ItemDescription/VBoxContainer/Body/MarginContainer/Label")
action_menu = NodePath("ItemDescription/VBoxContainer/Body/MarginContainer/Box")
[node name="PlayerInfo" type="VBoxContainer" parent="Contents/ItemDetails"] [node name="PlayerDetails" parent="MarginContainer/Contents/Details" instance=ExtResource("8_g71ie")]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
[node name="TextureRect" type="TextureRect" parent="Contents/ItemDetails/PlayerInfo"] [node name="Body" parent="MarginContainer/Contents/Details/PlayerDetails/VBoxContainer" index="1"]
clip_contents = true
[node name="PlayerInfo" type="VBoxContainer" parent="MarginContainer/Contents/Details/PlayerDetails/VBoxContainer/Body" index="0"]
layout_mode = 2
size_flags_horizontal = 3
[node name="TextureRect" type="TextureRect" parent="MarginContainer/Contents/Details/PlayerDetails/VBoxContainer/Body/PlayerInfo"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
texture = SubResource("PlaceholderTexture2D_as33y") texture = SubResource("PlaceholderTexture2D_as33y")
[node name="TextureRect2" type="TextureRect" parent="Contents/ItemDetails/PlayerInfo"] [node name="TextureRect2" type="TextureRect" parent="MarginContainer/Contents/Details/PlayerDetails/VBoxContainer/Body/PlayerInfo"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
texture = SubResource("PlaceholderTexture2D_tg4gd") texture = SubResource("PlaceholderTexture2D_tg4gd")
[node name="TextureRect3" type="TextureRect" parent="Contents/ItemDetails/PlayerInfo"] [node name="TextureRect3" type="TextureRect" parent="MarginContainer/Contents/Details/PlayerDetails/VBoxContainer/Body/PlayerInfo"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
texture = SubResource("PlaceholderTexture2D_usnyx") texture = SubResource("PlaceholderTexture2D_usnyx")
[node name="Display" type="VBoxContainer" parent="Contents/ItemDetails"] [node name="ItemDisplay" parent="MarginContainer/Contents/Details" instance=ExtResource("8_g71ie")]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
mouse_filter = 1
[node name="TextureRect" type="TextureRect" parent="Contents/ItemDetails/Display"] [node name="Display" type="SubViewportContainer" parent="MarginContainer/Contents/Details/ItemDisplay/VBoxContainer/Body" index="0"]
layout_mode = 2
size_flags_horizontal = 4
texture = SubResource("PlaceholderTexture2D_sebc8")
[node name="Description" type="VBoxContainer" parent="Contents/ItemDetails"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
stretch = true
[node name="Label" type="Label" parent="Contents/ItemDetails/Description"] [node name="SubViewport" type="SubViewport" parent="MarginContainer/Contents/Details/ItemDisplay/VBoxContainer/Body/Display"]
own_world_3d = true
transparent_bg = true
handle_input_locally = false
size = Vector2i(374, 464)
render_target_update_mode = 4
[node name="Camera3D" type="Camera3D" parent="MarginContainer/Contents/Details/ItemDisplay/VBoxContainer/Body/Display/SubViewport"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2)
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="MarginContainer/Contents/Details/ItemDisplay/VBoxContainer/Body/Display/SubViewport"]
transform = Transform3D(0.71887, 0.431921, -0.544674, -0.0668683, 0.822874, 0.564277, 0.691921, -0.36922, 0.620421, 0, 0, 0)
[node name="ItemDescription" parent="MarginContainer/Contents/Details" instance=ExtResource("8_g71ie")]
layout_mode = 2 layout_mode = 2
text = "a description" size_flags_horizontal = 3
focus_mode = 2
[connection signal="item_selected" from="Contents/InventoryUI" to="Contents/ItemDetails" method="_on_updated"] [node name="VBoxContainer" parent="MarginContainer/Contents/Details/ItemDescription" index="0"]
mouse_filter = 2
[node name="Header" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer" index="0"]
mouse_filter = 2
[node name="TitleContainer" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Header" index="0"]
mouse_filter = 2
[node name="Body" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer" index="1"]
mouse_filter = 2
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body" index="0"]
layout_mode = 2
mouse_filter = 2
[node name="Label" type="Label" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer"]
layout_mode = 2
size_flags_vertical = 1
autowrap_mode = 3
[node name="Box" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer" instance=ExtResource("8_g71ie")]
layout_mode = 2
size_flags_horizontal = 0
size_flags_vertical = 8
focus_mode = 2
[node name="VBoxContainer" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box" index="0"]
mouse_filter = 2
[node name="Header" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer" index="0"]
mouse_filter = 2
[node name="TitleContainer" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer/Header" index="0"]
mouse_filter = 2
[node name="Body" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer" index="1"]
mouse_filter = 2
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer/Body" index="0"]
layout_mode = 2
mouse_filter = 2
[node name="UseButton" type="Button" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer/Body/VBoxContainer"]
layout_mode = 2
focus_neighbor_left = NodePath(".")
focus_neighbor_top = NodePath(".")
focus_neighbor_right = NodePath(".")
focus_neighbor_bottom = NodePath("../CombineButton")
focus_next = NodePath("../CombineButton")
focus_previous = NodePath(".")
mouse_filter = 1
mouse_default_cursor_shape = 2
text = "USE"
alignment = 0
script = ExtResource("10_p7iul")
metadata/_custom_type_script = "uid://c4gfdpuc14mpq"
[node name="CombineButton" type="Button" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer/Body/VBoxContainer"]
layout_mode = 2
focus_neighbor_left = NodePath(".")
focus_neighbor_top = NodePath("../UseButton")
focus_neighbor_right = NodePath(".")
focus_neighbor_bottom = NodePath("../InspectButton")
focus_next = NodePath("../InspectButton")
focus_previous = NodePath("../UseButton")
mouse_filter = 1
text = "COMBINE"
alignment = 0
script = ExtResource("10_p7iul")
metadata/_custom_type_script = "uid://c4gfdpuc14mpq"
[node name="InspectButton" type="Button" parent="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer/Body/VBoxContainer"]
layout_mode = 2
focus_neighbor_left = NodePath(".")
focus_neighbor_top = NodePath("../CombineButton")
focus_neighbor_right = NodePath(".")
focus_neighbor_bottom = NodePath(".")
focus_next = NodePath(".")
focus_previous = NodePath("../CombineButton")
mouse_filter = 1
text = "INSPECT"
alignment = 0
script = ExtResource("10_p7iul")
metadata/_custom_type_script = "uid://c4gfdpuc14mpq"
[connection signal="item_focused" from="MarginContainer/Contents/MarginContainer/InventoryList" to="MarginContainer/Contents/Details" method="_on_updated"]
[connection signal="item_selected" from="MarginContainer/Contents/MarginContainer/InventoryList" to="MarginContainer/Contents/Details" method="_on_selected"]
[connection signal="hidden" from="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box" to="." method="grab_focus"]
[connection signal="visibility_changed" from="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box" to="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer/Body/VBoxContainer/UseButton" method="grab_focus"]
[connection signal="pressed" from="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer/Body/VBoxContainer/UseButton" to="MarginContainer/Contents/Details" method="use"]
[connection signal="pressed" from="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer/Body/VBoxContainer/CombineButton" to="MarginContainer/Contents/Details" method="combine"]
[connection signal="pressed" from="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box/VBoxContainer/Body/VBoxContainer/InspectButton" to="MarginContainer/Contents/Details" method="inspect"]
[editable path="MarginContainer/Contents/Details/PlayerDetails"]
[editable path="MarginContainer/Contents/Details/ItemDisplay"]
[editable path="MarginContainer/Contents/Details/ItemDescription"]
[editable path="MarginContainer/Contents/Details/ItemDescription/VBoxContainer/Body/MarginContainer/Box"]

View file

@ -1,27 +1,23 @@
[gd_scene load_steps=3 format=3 uid="uid://gn8k2ir47n1m"] [gd_scene load_steps=4 format=3 uid="uid://gn8k2ir47n1m"]
[ext_resource type="Script" uid="uid://dt67n4jti376d" path="res://src/item_ui.gd" id="1_letey"] [ext_resource type="Script" uid="uid://dt67n4jti376d" path="res://src/item_ui.gd" id="1_letey"]
[ext_resource type="PackedScene" uid="uid://cidwsnulug6fl" path="res://scenes/box_ui.tscn" id="1_y87vu"]
[ext_resource type="Theme" uid="uid://bb6tvyqikcf3r" path="res://scenes/box_ui.tres" id="3_y87vu"]
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_u4rwp"] [node name="Item" node_paths=PackedStringArray("name_label", "icon_texture") instance=ExtResource("1_y87vu")]
size = Vector2(128, 128)
[node name="Item" type="BoxContainer" node_paths=PackedStringArray("name_label", "icon_texture")]
offset_right = 128.0
offset_bottom = 155.0
size_flags_horizontal = 3
size_flags_vertical = 3
alignment = 1
script = ExtResource("1_letey") script = ExtResource("1_letey")
name_label = NodePath("VBoxContainer/Label") name_label = NodePath("VBoxContainer/Header/TitleContainer/Label")
icon_texture = NodePath("VBoxContainer/Icon") icon_texture = NodePath("VBoxContainer/Body/TextureRect")
[node name="VBoxContainer" type="VBoxContainer" parent="."] [node name="Label" type="Label" parent="VBoxContainer/Header/TitleContainer" index="0"]
layout_mode = 2 layout_mode = 2
theme = ExtResource("3_y87vu")
theme_type_variation = &"BoxHeaderLabel"
text = "pistol"
vertical_alignment = 1
uppercase = true
[node name="Label" type="Label" parent="VBoxContainer"] [node name="TextureRect" type="TextureRect" parent="VBoxContainer/Body" index="0"]
layout_mode = 2 layout_mode = 2
text = "Empty" expand_mode = 3
stretch_mode = 5
[node name="Icon" type="TextureRect" parent="VBoxContainer"]
layout_mode = 2
texture = SubResource("PlaceholderTexture2D_u4rwp")

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://dfljhd4e3l6j2"]
[ext_resource type="PackedScene" uid="uid://5a023hrws3gx" path="res://assets/Glock 17 Gen 4/Main.glb" id="1_h1roh"]
[node name="Main" instance=ExtResource("1_h1roh")]
transform = Transform3D(5.96244e-09, 0.866026, -0.5, -1.03272e-08, 0.5, 0.866026, 1, -8.28682e-16, 1.19249e-08, 0, 0, 0)

View file

@ -0,0 +1,9 @@
class_name CancelButton extends Button
@export var cancel_action: StringName = &"ui_cancel"
func _gui_input(event: InputEvent) -> void:
if event.is_action_pressed(cancel_action):
accept_event()
release_focus()
KCUtils.notify(get_parent(), NOTIFICATION_FOCUS_EXIT, true)

View file

@ -0,0 +1 @@
uid://c4gfdpuc14mpq

View file

@ -8,56 +8,83 @@ signal equipped_secondary(equipment: EquippableItem)
@export var _primary: EquippableItem @export var _primary: EquippableItem
var primary: Option: var primary: Option:
get: return Option.from(_primary) get: return Option.from(_primary)
set(value): set_primary(value) set(value): set_primary(value)
func _unequip_primary(): func _unequip_primary():
if _primary != null: if _primary != null:
var previous = _primary var previous = _primary
_primary = null _primary = null
unequipped_primary.emit(previous) unequipped_primary.emit(previous)
func _equip_primary(item: EquippableItem): func _equip_primary(item: EquippableItem):
if _primary != null: if _primary != null:
_unequip_primary() _unequip_primary()
if item != null: if item != null:
_primary = item _primary = item
equipped_primary.emit(item) equipped_primary.emit(item)
func set_primary(item: Option): func set_primary(item: Option):
_equip_primary(item.unwrap()) match item:
var x when x.is_some():
_equip_primary(x.unwrap())
_: _unequip_primary()
@export var _secondary: EquippableItem @export var _secondary: EquippableItem
var secondary: Option: var secondary: Option:
get: return Option.from(_secondary) get: return Option.from(_secondary)
set(value): set_secondary(value) set(value): set_secondary(value)
func _unequip_secondary(): func _unequip_secondary():
if _secondary != null: if _secondary != null:
var previous = _secondary var previous = _secondary
_secondary = null _secondary = null
unequipped_secondary.emit(previous) unequipped_secondary.emit(previous)
func _equip_secondary(item: EquippableItem): func _equip_secondary(item: EquippableItem):
if _secondary != null: if _secondary != null:
_unequip_secondary() _unequip_secondary()
if item != null: if item != null:
_secondary = item _secondary = item
equipped_secondary.emit(item) equipped_secondary.emit(item)
func set_secondary(item: Option): func set_secondary(item: Option):
_equip_secondary(item.unwrap()) match item:
var x when x.is_some():
_equip_secondary(x.unwrap())
_: _unequip_secondary()
func _init(): func _init():
call_deferred("_ready") call_deferred("_ready")
func _ready(): func _ready():
if _primary != null: if _primary != null:
_equip_primary(_primary) _equip_primary(_primary)
if _secondary != null: if _secondary != null:
_equip_secondary(_secondary) _equip_secondary(_secondary)
func equip(item: EquippableItem):
match item.type:
EquippableItem.EquipmentType.PRIMARY: primary = Option.some(item)
EquippableItem.EquipmentType.SECONDARY: secondary = Option.some(item)
func unequip(equip_type: EquippableItem.EquipmentType):
match equip_type:
EquippableItem.EquipmentType.PRIMARY: primary = Option.none
EquippableItem.EquipmentType.SECONDARY:secondary = Option.none
func unequip_item(item: EquippableItem):
equipped_to(item).inspect(unequip)
func is_equipped(item: EquippableItem) -> bool:
return equipped_to(item).is_some()
func equipped_to(item: EquippableItem) -> Option:
match item.type:
EquippableItem.EquipmentType.PRIMARY when item == _primary: return Option.some(EquippableItem.EquipmentType.PRIMARY)
EquippableItem.EquipmentType.SECONDARY when item == _secondary: return Option.some(EquippableItem.EquipmentType.SECONDARY)
return Option.none

View file

@ -1,9 +1,14 @@
class_name EquippableItem extends Item class_name EquippableItem extends Item
@export var scene: PackedScene enum EquipmentType {
PRIMARY,
SECONDARY
}
func create() -> Option: @export var type: EquipmentType = EquipmentType.PRIMARY
if scene != null and scene.can_instantiate():
return Option.some(scene.instantiate()) func is_primary() -> bool:
else: return type == EquipmentType.PRIMARY
return Option.none
func is_secondary() -> bool:
return type == EquipmentType.SECONDARY

View file

@ -15,4 +15,4 @@ func _on_progress(_scroll: int) -> void:
var next_index = get_carousel_index(with_drag, with_clamp) var next_index = get_carousel_index(with_drag, with_clamp)
if current_index != next_index: if current_index != next_index:
current_index = next_index current_index = next_index
index_changed.emit(current_index) index_changed.emit(next_index)

View file

@ -1,6 +1,7 @@
class_name InventoryUI extends Control class_name InventoryUI extends Control
signal item_selected(item: Option) signal item_focused(item: Option)
signal item_selected(item: Item)
@export var inventory: Inventory @export var inventory: Inventory
@export var item_scene: PackedScene @export var item_scene: PackedScene
@ -10,16 +11,14 @@ func _ready() -> void:
inventory.updated.connect(_build_carousel) inventory.updated.connect(_build_carousel)
carousel.index_changed.connect(_on_item_changed) carousel.index_changed.connect(_on_item_changed)
func _on_item_changed(index: int) -> void: func _items() -> Iterator:
var found: bool = false return inventory.iter().map(func(x): return x.value.item)
for indexed_value in IndexedIterator.new(inventory.iter()):
if indexed_value.index == index:
found = true
item_selected.emit(Option.from(indexed_value.value.value.item))
break
if not found: func _on_item_changed(index: int) -> void:
item_selected.emit(Option.none) match _items().nth(index):
var x when x.is_some():
item_focused.emit(Option.from(x.unwrap()))
_: item_focused.emit(Option.none)
func _build_carousel() -> void: func _build_carousel() -> void:
KCUtils.remove_children(carousel) KCUtils.remove_children(carousel)
@ -34,16 +33,6 @@ func _build_carousel() -> void:
bind_item(scene, option) bind_item(scene, option)
carousel.add_child(scene) carousel.add_child(scene)
#var items = inventory.items.values()
#var count = items.size()
#for i in range(inventory.max_capacity):
# var scene = create_item()
# var value = Option.none
# if i < count:
# value = Option.some(items[i].item)
# bind_item(scene, value)
# carousel.add_child(scene)
func create_item() -> Node: func create_item() -> Node:
return item_scene.instantiate() return item_scene.instantiate()
@ -56,6 +45,10 @@ func _gui_input(event: InputEvent) -> void:
move_right() move_right()
elif event.is_action_pressed(PlayerInput.UIAction.Left): elif event.is_action_pressed(PlayerInput.UIAction.Left):
move_left() move_left()
elif event.is_action_pressed(PlayerInput.UIAction.Accept):
var selected = _items().nth(carousel.current_index)
if selected.is_some():
item_selected.emit(selected.unwrap())
func move_by(delta: int): func move_by(delta: int):
var next_index = carousel.get_carousel_index() + delta var next_index = carousel.get_carousel_index() + delta

View file

@ -4,6 +4,31 @@ class_name Item extends Resource
@export var description: String @export var description: String
@export var max_stack: int @export var max_stack: int
@export var icon: Texture2D @export var icon: Texture2D
@export var scene: PackedScene
@export var preview_scene: PackedScene
func _to_string() -> String:
var icon_path = _resource_path_name(icon)
var scene_path = _resource_path_name(scene)
var preview_scene_path = _resource_path_name(preview_scene)
return 'Item { name = "%s", description = "%s", max_stack = %s, icon = "%s", scene = "%s", preview_scene = "%s" }' % [name, description.substr(0, 24), max_stack, icon_path, scene_path, preview_scene_path]
func _resource_path_name(res: Resource) -> String:
return res.resource_path if res != null else "None"
func _create(_scene: PackedScene) -> Option:
if _scene != null and _scene.can_instantiate():
return Option.some(_scene.instantiate())
else:
return Option.none
func create() -> Option:
return _create(scene)
func create_preview() -> Option:
return _create(preview_scene)
func combine(_with: Item) -> bool: func combine(_with: Item) -> bool:
return false return false

View file

@ -1,24 +1,67 @@
class_name ItemDetailsUI extends HBoxContainer class_name ItemDetailsUI extends HBoxContainer
@export var equipment: Equipment @export var equipment: Equipment
@export var icon_image: TextureRect @export var viewport: SubViewport
@export var description_label: Label @export var description_label: Label
@export var action_menu: Control
var _default_icon: Texture2D var current_item: Option = Option.none
var _displayed_item: Node3D
var _default_description: String var _default_description: String
func _ready() -> void: func _ready() -> void:
_default_icon = icon_image.texture action_menu.hide()
_default_description = description_label.text _default_description = description_label.text
func _set_default(): func _set_default():
icon_image.texture = _default_icon if _displayed_item != null:
viewport.remove_child(_displayed_item)
_displayed_item.queue_free()
description_label.text = _default_description description_label.text = _default_description
func _on_updated(item: Option): func _on_updated(item: Option):
current_item = item
if item.is_some(): if item.is_some():
var _item: Item = item.unwrap() var _item: Item = item.unwrap()
icon_image.texture = _item.icon match _item.create_preview():
var x when x.is_some():
_displayed_item = x.unwrap()
viewport.add_child(_displayed_item)
_: _set_default()
description_label.text = _item.description description_label.text = _item.description
else: else:
_set_default() _set_default()
func _on_selected(_item: Item):
action_menu.show()
func _notification(what: int) -> void:
if what == NOTIFICATION_FOCUS_EXIT:
action_menu.hide()
func _is_equip(item: Item) -> bool:
return is_instance_of(item, EquippableItem)
func _already_equipped(item: Item) -> bool:
return equipment.is_equipped(item)
func use():
if current_item.is_some():
var item = current_item.unwrap()
if _is_equip(item):
if _already_equipped(item):
equipment.unequip_item(item)
else:
equipment.equip(item)
else:
item.use()
action_menu.hide()
func combine():
current_item.inspect(func(x): x.combine())
action_menu.hide()
func inspect():
current_item.inspect(func(x): x.inspect())
action_menu.hide()

View file

@ -1,36 +1,36 @@
class_name Raycast extends Node3D class_name Raycast extends Node3D
class RaycastHit extends RefCounted: class RaycastHit extends RefCounted:
var collider: CollisionObject3D var collider: CollisionObject3D
var shape: int var shape: int
var normal: Vector3 var normal: Vector3
var position: Vector3 var position: Vector3
var face_index: int var face_index: int
@warning_ignore("shadowed_variable") @warning_ignore("shadowed_variable")
func _init(collider: CollisionObject3D, shape: int, normal: Vector3, position: Vector3, index: int) -> void: func _init(collider: CollisionObject3D, shape: int, normal: Vector3, position: Vector3, index: int) -> void:
self.collider = collider self.collider = collider
self.shape = shape self.shape = shape
self.normal = normal self.normal = normal
self.position = position self.position = position
self.face_index = index self.face_index = index
func get_shape_owner() -> Option: func get_shape_owner() -> Option:
if typeof(self.collider) == TYPE_NIL or self.shape == 0: return Option.none if typeof(self.collider) == TYPE_NIL or self.shape == 0: return Option.none
var owner_id = self.collider.shape_find_owner(shape) var owner_id = self.collider.shape_find_owner(shape)
return Option.some(self.collider.shape_owner_get_owner(owner_id)) return Option.some(self.collider.shape_owner_get_owner(owner_id))
static func from_dict(hit: Dictionary) -> Option: static func from_dict(hit: Dictionary) -> Option:
if hit.is_empty(): return Option.none if hit.is_empty(): return Option.none
return Option.some( return Option.some(
RaycastHit.new( RaycastHit.new(
hit['collider'], hit['collider'],
hit['shape'], hit['shape'],
hit['normal'], hit['normal'],
hit['position'], hit['position'],
hit['face_index'] hit['face_index']
) )
) )
@export var enabled: bool = true @export var enabled: bool = true
@export var exclude_parent: bool = true @export var exclude_parent: bool = true
@ -53,38 +53,40 @@ var _exclusions: Array
@export var width: int = 2 @export var width: int = 2
var forward: Vector3: var forward: Vector3:
get: return -global_basis.z get: return -global_basis.z
var hit: Option = Option.none var hit: Option = Option.none
func _ready() -> void: func _ready() -> void:
set_physics_process(enabled) set_physics_process(enabled)
_exclusions = exclusions.map(func(x): return x.get_rid()) _exclusions = exclusions.map(func(x): return x.get_rid())
if exclude_parent: if exclude_parent:
match _find_parent_collider(): match _find_parent_collider():
var x when x.is_some(): var x when x.is_some():
_exclusions.append(x.unwrap().get_rid()) _exclusions.append(x.unwrap().get_rid())
func _physics_process(_delta: float) -> void: func _physics_process(_delta: float) -> void:
force_raycast_update() force_raycast_update()
func _find_parent_collider(node: Node3D = self) -> Option: func _find_parent_collider(node: Node = self) -> Option:
if typeof(node) == TYPE_NIL: return Option.none if typeof(node) == TYPE_NIL: return Option.none
if is_instance_of(node, CollisionObject3D): if is_instance_of(node, SubViewport):
return Option.some(node) return Option.none
else: if is_instance_of(node, CollisionObject3D):
return _find_parent_collider(node.get_parent()) return Option.some(node)
else:
return _find_parent_collider(node.get_parent())
func force_raycast_update() -> void: func force_raycast_update() -> void:
var to = forward * length + global_position var to = forward * length + global_position
var query = PhysicsRayQueryParameters3D.create(global_position, to, collision_mask, _exclusions) var query = PhysicsRayQueryParameters3D.create(global_position, to, collision_mask, _exclusions)
DebugDraw3D.draw_ray(global_position, forward, length, custom_color, .25) DebugDraw3D.draw_ray(global_position, forward, length, custom_color, .25)
query.hit_from_inside = hit_from_inside query.hit_from_inside = hit_from_inside
query.hit_back_faces = hit_backfaces query.hit_back_faces = hit_backfaces
query.collide_with_areas = areas query.collide_with_areas = areas
query.collide_with_bodies = bodies query.collide_with_bodies = bodies
var raycast_hit = get_world_3d().direct_space_state.intersect_ray(query) var raycast_hit = get_world_3d().direct_space_state.intersect_ray(query)
hit = RaycastHit.from_dict(raycast_hit) hit = RaycastHit.from_dict(raycast_hit)
func is_colliding() -> bool: func is_colliding() -> bool:
return hit.is_some() return hit.is_some()

0
godot/src/window_ui.gd Normal file
View file

View file

@ -0,0 +1 @@
uid://bxd483uvo31jf