class_name RadialMenu extends Node2D const TIME_FADE := 0.6 const TIME_VISIBLE := 0.4 @export var cursor : Cursor @export var player : Player @export var min_segments := 8 @export var inner_radius := 32.0 @export var outer_radius := 64.0 @export var separation := 2.0 @onready var label := $Label as Label @onready var start_angle := (TAU / 4) - (TAU / min_segments / 2) var visibility_timer: float var hovered: Item: get: return hovered set(value): hovered = value; label.text = str(value.name) if value else "" func _unhandled_input(event: InputEvent) -> void: if not player or not player.health.is_alive: return if event.is_action_pressed("interact_select"): position = cursor.hud_position.round() visible = true modulate = Color.WHITE hovered = player.inventory.equipped player.inventory.equipped = null visibility_timer = INF var select_dec := event.is_action_pressed("interact_select_dec") var select_inc := event.is_action_pressed("interact_select_inc") if select_dec != select_inc: # xor would be more fitting here if visible and visibility_timer == INF: var cursor_offset := to_local(cursor.hud_position) if cursor_offset.length() > inner_radius: return else: position = player.get_global_transform_with_canvas().origin hovered = player.inventory.equipped visible = true modulate = Color.WHITE visibility_timer = TIME_FADE + TIME_VISIBLE var items := player.inventory.get_items() if items.is_empty(): return var new_index := 0 if hovered: var index := hovered.get_index() var diff := 1 if select_inc else -1 new_index = posmod(index + diff, items.size()) hovered = items[new_index] player.inventory.equipped = hovered queue_redraw() func _process(delta: float) -> void: if not player or not player.health.is_alive: visible = false if not visible: return visibility_timer -= delta if visibility_timer <= 0.0: visible = false if visibility_timer < TIME_FADE: modulate = Color(Color.WHITE, visibility_timer / TIME_FADE) if visibility_timer < INF: return var cursor_offset := to_local(cursor.hud_position) var angle := cursor_offset.angle() - start_angle var index := floori(fmod((angle / TAU + 1), 1) * min_segments) var items := player.inventory.get_items() if cursor_offset.length() > inner_radius and index < items.size() and items[index] != hovered: hovered = items[index] if not Input.is_action_pressed("interact_select"): player.inventory.equipped = hovered visibility_timer = TIME_FADE queue_redraw() func _draw() -> void: var items := player.inventory.get_items() var segments := maxi(min_segments, items.size()) for i in segments: var angle_1 := start_angle + TAU * ( i / float(segments)) var angle_3 := start_angle + TAU * ((i + 1) / float(segments)) var angle_2 := (angle_1 + angle_3) / 2 var sep_1 := separation * Vector2.from_angle(angle_1 + TAU / 4) var sep_2 := separation * Vector2.from_angle(angle_3 - TAU / 4) var is_hovered := i < items.size() and items[i] == hovered var inner_radius_2 := inner_radius + (5.0 if is_hovered else 0.0) var outer_radius_2 := outer_radius + (5.0 if is_hovered else 0.0) var color := Color(0.1, 0.1, 0.1, 0.7 if is_hovered else 0.4) draw_colored_polygon([ inner_radius_2 * Vector2.from_angle(angle_1) + sep_1, outer_radius_2 * Vector2.from_angle(angle_1) + sep_1, (outer_radius_2 + separation) * Vector2.from_angle(angle_2) , outer_radius_2 * Vector2.from_angle(angle_3) + sep_2, inner_radius_2 * Vector2.from_angle(angle_3) + sep_2, ], color) if i < items.size(): var sprite := items[i] var pos := (inner_radius_2 + outer_radius_2) / 2 * Vector2.from_angle(angle_2) if sprite.centered: pos -= sprite.texture.get_size() / 2 draw_texture(sprite.texture, pos, sprite.modulate)