add more iterators; add item pickups

This commit is contained in:
Rowan 2025-06-29 14:10:00 -04:00
parent 262928664a
commit d48e068a00
38 changed files with 488 additions and 142 deletions

View file

@ -18,13 +18,22 @@ func _init(start: int, end: int, inclusive: bool = false) -> void:
minmax = PackedInt32Array([start, end])
self.inclusive = inclusive
static func from_sized(value: Variant) -> Result:
match KCUtils.get_size(value):
var len when len.is_ok():
return Result.ok(IntRange.new(0, len.unwrap()))
var e: return e
func _to_string() -> String:
var ch = "]" if inclusive else ")"
return "IntRange [%s, %s%s" % [min, max, ch]
func length() -> int:
func size() -> int:
return absi(max - min)
func length() -> int:
return size()
func is_zero() -> bool:
return min == 0 and max == 0

View file

@ -0,0 +1,15 @@
class_name Tuple extends RefCounted
var _items: Array
func _init(items: Array) -> void:
_items = items
static func from_iter(iter: Iterator):
Tuple.new(iter.collect())
func iter() -> Iterator:
return ListIterator.from_array(_items)
func _get(property) -> Variant:
return _items[property]

View file

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

View file

@ -0,0 +1,23 @@
class_name DictionaryIterator extends Iterator
class Entry extends RefCounted:
var key
var value
func _init(key: Variant, value: Variant) -> void:
self.key = key
self.value = value
var dict: Dictionary
var keys: Iterator
func _init(dict: Dictionary) -> void:
self.dict = dict
self.keys = ListIterator.from_array(dict.keys())
func next() -> Option:
match keys.next():
var _key when _key.is_some():
var key = _key.unwrap()
return Option.some(Entry.new(key, dict[key]))
return Option.none

View file

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

View file

@ -0,0 +1,4 @@
class_name EmptyIterator extends Iterator
func next() -> Option:
return Option.none

View file

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

View file

@ -0,0 +1,14 @@
class_name FilterIterator extends Iterator
var iterator: Iterator
var predicate: Callable
func _init(iterator: Iterator, predicate: Callable) -> void:
self.iterator = iterator
self.predicate = predicate
func next() -> Option:
var value = iterator.next()
while value.is_some() and not predicate.call(value.unwrap()):
value = iterator.next()
return value

View file

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

View file

@ -0,0 +1,15 @@
class_name InspectIterator extends Iterator
var iterator: Iterator
var fn: Callable
func _init(iterator: Iterator, fn: Callable) -> void:
self.iterator = iterator
self.fn = fn
func next() -> Option:
match iterator.next():
var x when x.is_some():
fn.call(x.unwrap())
return x
var x: return x

View file

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

View file

@ -1,5 +1,8 @@
class_name Iterator extends RefCounted
static func repeat(value: Variant) -> RepeatIterator:
return RepeatIterator.new(value)
func _to_string() -> String:
return "Iterator"
@ -10,17 +13,95 @@ func clone() -> Iterator:
func next() -> Option:
return Option.none
static func empty() -> Iterator:
return EmptyIterator.new()
func advance_by(amount: int) -> Iterator:
for _i in range(amount):
if self.next().is_none():
return empty()
return self
func all(predicate: Callable) -> bool:
for value in self:
if not predicate.call(value):
return false
return true
func any(predicate: Callable) -> bool:
for value in self:
if predicate.call(value):
return true
return false
func collect() -> Array:
var result = []
for item in self:
result.append(item)
return result
func into_peekable() -> PeekableIter:
func count() -> int:
var length = 0
for _value in self:
length += 1
return length
func enumerate() -> IndexedIterator:
return IndexedIterator.new(self)
func filter(predicate: Callable) -> FilterIterator:
return FilterIterator.new(self, predicate)
func find(predicate: Callable) -> Option:
for value in self:
if predicate.call(value):
return Option.some(value)
return Option.none
func fuse(other: Iterator) -> FusedIterator:
return FusedIterator.new([self, other])
func inspect(fn: Callable) -> Iterator:
return InspectIterator.new(self, fn)
func last() -> Option:
var last = Option.none
for value in self:
last = Option.some(value)
return last
func map(fn: Callable) -> MapIterator:
return MapIterator.new(self, fn)
func peekable() -> PeekableIter:
return PeekableIter.new(self)
func into_indexed() -> IndexedIterator:
return IndexedIterator.new(self)
func position(predicate: Callable) -> Option:
for indexed_value in self.enumerate():
if predicate.call(indexed_value.value):
return Option.some(indexed_value.index)
return Option.none
func reduce(reducer: Callable, initial_value: Variant) -> Option:
return scan(reducer, initial_value).last()
func scan(reducer: Callable, initial_value: Variant) -> ReduceIterator:
return ReduceIterator.new(self, reducer, initial_value)
func skip(amount: int) -> SkipIterator:
return SkipIterator.new(self, amount)
func skip_while(predicate: Callable) -> SkipWhileIterator:
return SkipWhileIterator.new(self, predicate)
func take(limit: int) -> TakeIterator:
return TakeIterator.new(self, limit)
func take_while(predicate: Callable) -> TakeWhileIter:
return TakeWhileIter.new(self, predicate)
func zip(a: Iterator, b: Iterator) -> ZipIterator:
return ZipIterator.new(a, b)
func _iter_init(iter: Array) -> bool:
return _iter_next(iter)

View file

@ -7,6 +7,9 @@ func _init(value: Variant, iter: RangeIterator) -> void:
_value = value
_iter = iter
static func from_array(array: Array) -> ListIterator:
return ListIterator.new(array, RangeIterator.from_array(array))
func _at(index: int) -> Variant:
return _value[index]

View file

@ -0,0 +1,14 @@
class_name MapIterator extends Iterator
var iterator: Iterator
var morphism: Callable
func _init(iterator: Iterator, fn: Callable) -> void:
self.iterator = iterator
self.morphism = fn
func next() -> Option:
match iterator.next():
var x when x.is_some():
return Option.some(morphism.call(x.unwrap()))
_: return Option.none

View file

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

View file

@ -17,6 +17,18 @@ func _init(range: IntRange, start_index: int = range.min, step: int = 1) -> void
func from_values(from: int, to: int, start_index: int = range.min, step: int = 1) -> RangeIterator:
return RangeIterator.new(IntRange.new(from, to), start_index, step)
static func from_sized(value: Variant) -> Result:
match IntRange.from_sized(value):
var range when range.is_ok():
return Result.ok(RangeIterator.new(range.unwrap()))
var e: return e
static func from_array(array: Array) -> RangeIterator:
return RangeIterator.from_sized(array).unwrap()
func size() -> int:
return range.size()
func length() -> int:
return range.length()

View file

@ -0,0 +1,21 @@
class_name ReduceIterator extends Iterator
var iterator: Iterator
var reducer: Callable
var accumulator: Variant
func _init(iterator: Iterator, reducer: Callable, initial_value: Variant) -> void:
self.iterator = iterator
self.reducer = reducer
accumulator = initial_value
func next() -> Option:
var value = iterator.next()
if value.is_none(): return value
if accumulator == null:
accumulator = value
else:
accumulator = reducer.call(accumulator, value)
return Option.some(accumulator)

View file

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

View file

@ -0,0 +1,9 @@
class_name RepeatIterator extends Iterator
var value: Variant
func _init(value: Variant) -> void:
self.value = value
func next() -> Option:
return Option.some(value)

View file

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

View file

@ -0,0 +1,15 @@
class_name SkipIterator extends Iterator
var iterator: Iterator
var amount: int
var _skipped: bool = false
func _init(iterator: Iterator, amount: int) -> void:
self.iterator = iterator
self.amount = amount
func next() -> Option:
if not _skipped:
iterator.advance_by(amount)
_skipped = true
return iterator.next()

View file

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

View file

@ -0,0 +1,18 @@
class_name SkipWhileIterator extends Iterator
var iterator: Iterator
var predicate: Callable
var _skipped: bool = false
func _init(iterator: Iterator, predicate: Callable) -> void:
self.iterator = iterator
self.predicate = predicate
func next() -> Option:
if not _skipped:
var value = iterator.next()
while value.is_some() and predicate.call(value):
value = iterator.next()
_skipped = true
return value
return iterator.next()

View file

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

View file

@ -0,0 +1,15 @@
class_name TakeIterator extends Iterator
var iter: Iterator
var limit: int
func _init(iter: Iterator, limit: int) -> void:
self.iter = iter
self.limit = limit
func next() -> Option:
if limit < 0:
return Option.none
limit -= 1
return iter.next()

View file

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

View file

@ -0,0 +1,14 @@
class_name ZipIterator extends Iterator
var iter1: Iterator
var iter2: Iterator
func _init(a: Iterator, b: Iterator) -> void:
iter1 = a
iter2 = b
func next() -> Option:
match [iter1.next(), iter2.next()]:
[var a, var b] when a.is_some() and b.is_some():
return Option.some(Tuple.new([a.unwrap(), b.unwrap()]))
_: return Option.none

View file

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

View file

@ -1,8 +1,11 @@
class_name KCUtils
static func remove_children(node: Node):
for i in range(node.get_child_count()):
node.remove_child(node.get_child(i))
static func remove_children(node: Node, delete: bool = true):
for i in range(node.get_child_count() - 1, -1, -1):
var child = node.get_child(i)
node.remove_child(child)
if delete:
child.queue_free()
static func get_class_name(value: Object) -> String:
match value.get_script():
@ -47,3 +50,14 @@ static func propagate_input_event(node: Node, fn: StringName, event: InputEvent,
for child in node.get_children():
propagate_input_event(child, fn, event)
static func get_size(value: Variant) -> Result:
match typeof(value):
TYPE_STRING | TYPE_VECTOR2 | TYPE_VECTOR2I | TYPE_VECTOR3 | TYPE_VECTOR3I | TYPE_VECTOR4 | TYPE_VECTOR4I | TYPE_QUATERNION:
return Result.ok(value.length())
var x when x >= 27 and x <= 38:
return Result.ok(value.size())
_: return Result.err(Error.NotImplemented.new('"length" or "size"'))
static func len(value: Variant) -> int:
return get_size(value).unwrap()

View file

@ -1,9 +1,7 @@
[gd_resource type="Resource" script_class="Equipment" load_steps=3 format=3 uid="uid://cxm3s081hnw8l"]
[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3 uid="uid://cxm3s081hnw8l"]
[ext_resource type="Script" uid="uid://bff1tgf3sn855" path="res://src/equipment.gd" id="1_ajqsy"]
[ext_resource type="Resource" uid="uid://crgwey6ibaf2h" path="res://resources/items/pistol.tres" id="1_hds1i"]
[resource]
script = ExtResource("1_ajqsy")
_primary = ExtResource("1_hds1i")
metadata/_custom_type_script = "uid://bff1tgf3sn855"

View file

@ -1,10 +1,12 @@
[gd_scene load_steps=10 format=3 uid="uid://cn7tgd4y67wnd"]
[gd_scene load_steps=12 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://bx4wxlm5mv268" path="res://src/root_control.gd" id="1_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="Script" uid="uid://13lxe4c4fmrp" path="res://addons/FreeControl/src/CustomClasses/Carousel/Carousel.gd" id="4_usnyx"]
[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="Resource" uid="uid://cxm3s081hnw8l" path="res://resources/player_equipment.tres" id="7_s887n"]
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_as33y"]
size = Vector2(128, 128)
@ -66,14 +68,21 @@ anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("4_usnyx")
script = ExtResource("5_uae8j")
with_drag = true
with_clamp = true
display_range = 5
snap_behavior = 2
paging_requirement = 100
metadata/_custom_type_script = "uid://bpwbaanf6w65h"
[node name="ItemDetails" type="HBoxContainer" parent="Contents"]
[node name="ItemDetails" type="HBoxContainer" parent="Contents" node_paths=PackedStringArray("icon_image", "description_label")]
layout_mode = 2
size_flags_vertical = 3
script = ExtResource("6_s887n")
equipment = ExtResource("7_s887n")
icon_image = NodePath("Display/TextureRect")
description_label = NodePath("Description/Label")
[node name="PlayerInfo" type="VBoxContainer" parent="Contents/ItemDetails"]
layout_mode = 2
@ -110,3 +119,5 @@ size_flags_horizontal = 3
[node name="Label" type="Label" parent="Contents/ItemDetails/Description"]
layout_mode = 2
text = "a description"
[connection signal="item_selected" from="Contents/InventoryUI" to="Contents/ItemDetails" method="_on_updated"]

View file

@ -0,0 +1,18 @@
@tool
class_name IndexCarousel extends Carousel
signal index_changed(index: int)
@export var with_drag: bool = false
@export var with_clamp: bool = false
var current_index: int = 0
func _ready() -> void:
current_index = get_carousel_index(with_drag, with_clamp)
func _on_progress(_scroll: int) -> void:
var next_index = get_carousel_index(with_drag, with_clamp)
if current_index != next_index:
current_index = next_index
index_changed.emit(current_index)

View file

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

View file

@ -1,33 +1,15 @@
class_name Inventory extends Resource
class InventoryIterator extends Iterator:
var values: Array[ItemInstance]
var size: int
var index: int = 0
@warning_ignore("shadowed_variable")
func _init(values: Array[ItemInstance], size: int = values.size()) -> void:
self.values = values
self.size = size
func next() -> Option:
if index < size:
var value = Option.some(values[index])
index += 1
return value
else:
return Option.none
signal item_added(item: Item, quantity: int)
signal item_removed(item: Item, remaining: int)
signal updated
@export var initial_items: Array[ItemInstance]
@export var max_capacity: int = 6
var items: Dictionary[int, ItemInstance]
var size: int:
get: return items.size()
signal item_added(item: Item, quantity: int)
signal item_removed(item: Item, remaining: int)
signal updated
var empty_slots: int:
get: return max_capacity - size()
func _init():
call_deferred("_ready")
@ -37,6 +19,9 @@ func _ready():
items[_get_id(item.item)] = item
updated.emit()
func size() -> int:
return items.size()
func _get_id(item: Item) -> int:
return item.get_instance_id()
@ -71,5 +56,5 @@ func remove_item(item: Item, quantity: int = 1):
item_removed.emit(item, item.quantity)
updated.emit()
func iter() -> InventoryIterator:
return InventoryIterator.new(items.values(), max_capacity)
func iter() -> Iterator:
return DictionaryIterator.new(items)

View file

@ -1,29 +1,53 @@
class_name InventoryUI extends Control
signal item_selected(item: Option)
@export var inventory: Inventory
@export var item_scene: PackedScene
@export var carousel: Carousel
@export var carousel: IndexCarousel
func _ready() -> void:
inventory.updated.connect(_build_carousel)
carousel.index_changed.connect(_on_item_changed)
func _build_carousel():
func _on_item_changed(index: int) -> void:
var found: bool = false
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:
item_selected.emit(Option.none)
func _build_carousel() -> void:
KCUtils.remove_children(carousel)
var items = inventory.items.values()
var count = items.size()
for i in range(inventory.max_capacity):
var empty_iter = Iterator.repeat(null).take(inventory.empty_slots)
var item_iter = ListIterator.from_array(inventory.items.values().map(func(x): return x.item))
var items = FusedIterator.new([item_iter, empty_iter])
for item in items:
var scene = create_item()
var value = Option.none
if i < count:
value = Option.some(items[i].item)
bind_item(scene, value)
var option = Option.from(item)
bind_item(scene, option)
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:
return item_scene.instantiate()
func bind_item(node: Node, item: Option):
func bind_item(node: Node, item: Option) -> void:
if node.has_method('bind'):
node.bind(item)

View file

@ -14,5 +14,5 @@ func add_item(item: Item, quantity: int = 1):
func remove_item(item: Item, quantity: int = 1):
return inventory.remove_item(item, quantity)
func iter() -> Inventory.InventoryIterator:
func iter() -> Iterator:
return inventory.iter()

View file

@ -1,5 +1,6 @@
class_name ItemDetailsUI extends HBoxContainer
@export var equipment: Equipment
@export var icon_image: TextureRect
@export var description_label: Label
@ -14,10 +15,10 @@ func _set_default():
icon_image.texture = _default_icon
description_label.text = _default_description
func _on_updated(_item: Option):
if _item.is_some():
var item: Item = _item.unwrap()
icon_image.texture = item.icon
description_label.text = item.description
func _on_updated(item: Option):
if item.is_some():
var _item: Item = item.unwrap()
icon_image.texture = _item.icon
description_label.text = _item.description
else:
_set_default()

View file

@ -1 +1 @@
uid://dfvvqpgu8r5v6
uid://c8m7eq6a3tpgu