Files
settled-reach/client/ui/inventory_grid.gd
T
jpmschweitzerandClaude Opus 4.6 8bf7f6e610 fix(client): entity lerp, fog shader safety, type fixes
Entity renderer: add framerate-independent position lerping so entities
slide between tiles instead of snapping. Tuned for Sprint snappiness
and Walk/Careful fluidity.

Fog shader: set ColorRect to transparent fallback so a shader load
failure doesn't paint solid white over the world. Track camera position
(not player position) so fog stays synced during smooth camera pan.
Restructure GLSL to avoid early return (some GPU drivers miscompile it).

Minor type fixes: typed Array[Vector2] in cursor_renderer tick drawing,
untyped Array in inventory_grid to avoid Godot typed-array cast issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 23:26:23 +01:00

140 lines
3.7 KiB
GDScript

extends Control
## D-065: Inventory grid — 3x3 slots, bottom-right, 40x40px icons.
## No empty slots displayed — icons appear only when items are carried.
## 1-9 hotkeys for direct slot access. Reads from GameState.player_inventory.
## Lives on UILayer (z-layer 7).
signal item_selected(slot: int, item_id: int)
const SLOT_SIZE := 40
const SLOT_GAP := 4
const GRID_COLS := 3
const GRID_ROWS := 3
const MAX_SLOTS := 9
const SLOT_BG := Color(0.1, 0.1, 0.14, 0.6)
const SLOT_BORDER := Color(0.3, 0.3, 0.38, 0.8)
const SLOT_ACTIVE := Color("#c8d0e0")
const SLOT_TEXT := Color("#c8d0e0")
const SLOT_TEXT_DIM := Color("#8b8ba0")
const HOTKEY_SIZE := 10
var _slots: Array = [] # [{item_id, name, slot}] from GameState
var _slot_nodes: Array[Control] = []
var _selected_slot: int = -1
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
_build_grid()
visible = false
func _build_grid() -> void:
for node in _slot_nodes:
if is_instance_valid(node):
node.queue_free()
_slot_nodes.clear()
var grid_w: float = GRID_COLS * SLOT_SIZE + (GRID_COLS - 1) * SLOT_GAP
var grid_h: float = GRID_ROWS * SLOT_SIZE + (GRID_ROWS - 1) * SLOT_GAP
# Position grid at bottom-right with margin
custom_minimum_size = Vector2(grid_w, grid_h)
size = Vector2(grid_w, grid_h)
func update_from_state() -> void:
_slots = GameState.player_inventory.duplicate()
if _slots.is_empty():
visible = false
return
visible = true
queue_redraw()
func _draw() -> void:
if _slots.is_empty():
return
for item in _slots:
var slot_idx: int = item.get("slot", 0)
if slot_idx < 0 or slot_idx >= MAX_SLOTS:
continue
var col: int = slot_idx % GRID_COLS
var row: int = slot_idx / GRID_COLS
var pos := Vector2(
col * (SLOT_SIZE + SLOT_GAP),
row * (SLOT_SIZE + SLOT_GAP)
)
var rect := Rect2(pos, Vector2(SLOT_SIZE, SLOT_SIZE))
# Slot background
draw_rect(rect, SLOT_BG)
# Border
draw_rect(rect, SLOT_BORDER, false, 1.0)
# Selected highlight
if slot_idx == _selected_slot:
draw_rect(rect, SLOT_ACTIVE, false, 2.0)
# Item name (truncated, centered)
var item_name: String = item.get("name", "?")
if item_name.length() > 5:
item_name = item_name.substr(0, 4) + "."
var font := ThemeDB.fallback_font
var font_size := 11
var text_size := font.get_string_size(item_name, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size)
var text_pos := pos + Vector2((SLOT_SIZE - text_size.x) / 2.0, SLOT_SIZE / 2.0 + text_size.y / 4.0)
draw_string(font, text_pos, item_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, SLOT_TEXT)
# Hotkey number (top-left corner)
var hotkey := str(slot_idx + 1)
draw_string(font, pos + Vector2(3, HOTKEY_SIZE + 2), hotkey, HORIZONTAL_ALIGNMENT_LEFT, -1, HOTKEY_SIZE, SLOT_TEXT_DIM)
func _unhandled_input(event: InputEvent) -> void:
if not visible:
return
if event is InputEventKey and event.pressed and not event.echo:
var key: int = event.physical_keycode
# 1-9 hotkeys (Key_1 = 49, Key_9 = 57)
if key >= KEY_1 and key <= KEY_9:
var slot_idx: int = key - KEY_1
_select_slot(slot_idx)
get_viewport().set_input_as_handled()
func _select_slot(slot_idx: int) -> void:
# Find item in this slot
for item in _slots:
if item.get("slot", -1) == slot_idx:
_selected_slot = slot_idx
item_selected.emit(slot_idx, item.get("item_id", -1))
queue_redraw()
return
# No item in slot — deselect
_selected_slot = -1
queue_redraw()
# -- Public API ---------------------------------------------------------------
func get_slot_count() -> int:
return _slots.size()
func get_selected_slot() -> int:
return _selected_slot
func get_item_at_slot(slot_idx: int) -> Dictionary:
for item in _slots:
if item.get("slot", -1) == slot_idx:
return item
return {}