feat(stage): complete phase 20b action plans

This commit is contained in:
2026-11-04 01:19:57 -05:00
parent 87a33b0abc
commit a9c0886348
70 changed files with 14234 additions and 469 deletions
@@ -0,0 +1,198 @@
class_name StageActionSummaryGlyph
extends Control
## Small, screen-sized glyphs for the Stage action-plan summary rows.
var _kind: String = "move"
var _tint: Color = Color("#8BD9FF")
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
focus_mode = Control.FOCUS_NONE
func configure(kind: String, tint: Color) -> void:
_kind = kind
_tint = tint
queue_redraw()
func _draw() -> void:
draw_in_canvas(self, _kind, Vector2.ZERO, 1.0, _tint)
## Draws the same vector glyph into another CanvasItem's active draw callback.
## `origin` is the top-left of the 28 x 30 glyph design space.
static func draw_in_canvas(canvas: CanvasItem, kind: String, origin: Vector2, scale_factor: float, tint: Color) -> void:
if canvas == null or scale_factor <= 0.0:
return
match kind:
"move":
_draw_move(canvas, origin, scale_factor, tint)
"speak":
_draw_speech(canvas, origin, scale_factor, tint)
"wait":
_draw_wait(canvas, origin, scale_factor, tint)
"ragdoll":
_draw_ragdoll(canvas, origin, scale_factor, tint)
"recover":
_draw_recover(canvas, origin, scale_factor, tint)
"drop_prop":
_draw_drop_prop(canvas, origin, scale_factor, tint)
"throw_prop":
_draw_throw_prop(canvas, origin, scale_factor, tint)
"set_down_prop":
_draw_set_down_prop(canvas, origin, scale_factor, tint)
"use_prop":
_draw_use_prop(canvas, origin, scale_factor, tint)
static func _draw_move(canvas: CanvasItem, origin: Vector2, scale_factor: float, tint: Color) -> void:
var route := PackedVector2Array([
origin + Vector2(4.0, 24.0) * scale_factor,
origin + Vector2(8.0, 15.0) * scale_factor,
origin + Vector2(13.0, 19.0) * scale_factor,
origin + Vector2(24.0, 7.0) * scale_factor,
])
canvas.draw_polyline(route, tint, 2.8 * scale_factor, true)
canvas.draw_circle(origin + Vector2(4.0, 24.0) * scale_factor, 2.5 * scale_factor, tint, true, -1.0, true)
var arrow_head := PackedVector2Array([
origin + Vector2(17.0, 7.0) * scale_factor,
origin + Vector2(25.0, 6.0) * scale_factor,
origin + Vector2(24.0, 14.0) * scale_factor,
])
canvas.draw_polyline(arrow_head, tint, 2.8 * scale_factor, true)
static func _draw_speech(canvas: CanvasItem, origin: Vector2, scale_factor: float, tint: Color) -> void:
var outline := PackedVector2Array([
origin + Vector2(5.0, 5.0) * scale_factor,
origin + Vector2(22.0, 5.0) * scale_factor,
origin + Vector2(26.0, 9.0) * scale_factor,
origin + Vector2(26.0, 18.0) * scale_factor,
origin + Vector2(22.0, 22.0) * scale_factor,
origin + Vector2(15.0, 22.0) * scale_factor,
origin + Vector2(8.0, 28.0) * scale_factor,
origin + Vector2(8.0, 22.0) * scale_factor,
origin + Vector2(5.0, 22.0) * scale_factor,
origin + Vector2(2.0, 19.0) * scale_factor,
origin + Vector2(2.0, 8.0) * scale_factor,
origin + Vector2(5.0, 5.0) * scale_factor,
])
canvas.draw_polyline(outline, tint, 2.2 * scale_factor, true)
static func _draw_wait(canvas: CanvasItem, origin: Vector2, scale_factor: float, tint: Color) -> void:
var outline := PackedVector2Array([
origin + Vector2(6.0, 5.0) * scale_factor,
origin + Vector2(24.0, 5.0) * scale_factor,
origin + Vector2(22.0, 9.0) * scale_factor,
origin + Vector2(17.0, 14.0) * scale_factor,
origin + Vector2(22.0, 19.0) * scale_factor,
origin + Vector2(24.0, 23.0) * scale_factor,
origin + Vector2(6.0, 23.0) * scale_factor,
origin + Vector2(8.0, 19.0) * scale_factor,
origin + Vector2(13.0, 14.0) * scale_factor,
origin + Vector2(8.0, 9.0) * scale_factor,
origin + Vector2(6.0, 5.0) * scale_factor,
])
canvas.draw_polyline(outline, tint, 2.2 * scale_factor, true)
canvas.draw_line(origin + Vector2(9.0, 8.0) * scale_factor, origin + Vector2(21.0, 8.0) * scale_factor, tint, 2.0 * scale_factor, true)
canvas.draw_line(origin + Vector2(9.0, 20.0) * scale_factor, origin + Vector2(21.0, 20.0) * scale_factor, tint, 2.0 * scale_factor, true)
static func _draw_ragdoll(canvas: CanvasItem, origin: Vector2, scale_factor: float, tint: Color) -> void:
var center: Vector2 = origin + Vector2(14.0, 15.0) * scale_factor
var points := PackedVector2Array([
center + Vector2(0.0, -13.0) * scale_factor,
center + Vector2(3.0, -5.0) * scale_factor,
center + Vector2(10.0, -10.0) * scale_factor,
center + Vector2(7.0, -2.0) * scale_factor,
center + Vector2(14.0, 1.0) * scale_factor,
center + Vector2(6.0, 4.0) * scale_factor,
center + Vector2(10.0, 11.0) * scale_factor,
center + Vector2(2.0, 7.0) * scale_factor,
center + Vector2(-2.0, 14.0) * scale_factor,
center + Vector2(-5.0, 6.0) * scale_factor,
center + Vector2(-12.0, 10.0) * scale_factor,
center + Vector2(-8.0, 2.0) * scale_factor,
center + Vector2(-14.0, -2.0) * scale_factor,
center + Vector2(-6.0, -5.0) * scale_factor,
center + Vector2(-10.0, -12.0) * scale_factor,
center + Vector2(-2.0, -7.0) * scale_factor,
])
canvas.draw_colored_polygon(points, tint)
static func _draw_recover(canvas: CanvasItem, origin: Vector2, scale_factor: float, tint: Color) -> void:
var center: Vector2 = origin + Vector2(14.0, 15.0) * scale_factor
var arc := PackedVector2Array()
for index: int in 16:
var angle: float = deg_to_rad(-55.0 + float(index) / 15.0 * 285.0)
arc.append(center + Vector2(cos(angle), sin(angle)) * 10.0 * scale_factor)
canvas.draw_polyline(arc, tint, 2.4 * scale_factor, true)
var end_angle: float = deg_to_rad(230.0)
var tip: Vector2 = center + Vector2(cos(end_angle), sin(end_angle)) * 10.0 * scale_factor
var tangent: Vector2 = Vector2(-sin(end_angle), cos(end_angle))
var base: Vector2 = tip - tangent * 7.0 * scale_factor
var wing: Vector2 = Vector2(-tangent.y, tangent.x) * 4.0 * scale_factor
canvas.draw_colored_polygon(PackedVector2Array([
tip,
base + wing,
base - wing,
]), tint)
static func _draw_drop_prop(canvas: CanvasItem, origin: Vector2, scale_factor: float, tint: Color) -> void:
canvas.draw_rect(Rect2(origin + Vector2(4.0, 3.0) * scale_factor, Vector2(20.0, 9.0) * scale_factor), tint, false, 2.2 * scale_factor, true)
canvas.draw_line(origin + Vector2(14.0, 13.0) * scale_factor, origin + Vector2(14.0, 23.0) * scale_factor, tint, 2.5 * scale_factor, true)
canvas.draw_line(origin + Vector2(14.0, 23.0) * scale_factor, origin + Vector2(8.0, 18.0) * scale_factor, tint, 2.5 * scale_factor, true)
canvas.draw_line(origin + Vector2(14.0, 23.0) * scale_factor, origin + Vector2(20.0, 18.0) * scale_factor, tint, 2.5 * scale_factor, true)
canvas.draw_line(origin + Vector2(4.0, 28.0) * scale_factor, origin + Vector2(24.0, 28.0) * scale_factor, tint, 2.0 * scale_factor, true)
static func _draw_throw_prop(canvas: CanvasItem, origin: Vector2, scale_factor: float, tint: Color) -> void:
var bow := PackedVector2Array([
origin + Vector2(17.0, 3.0) * scale_factor,
origin + Vector2(11.0, 5.0) * scale_factor,
origin + Vector2(6.0, 9.0) * scale_factor,
origin + Vector2(4.0, 15.0) * scale_factor,
origin + Vector2(6.0, 21.0) * scale_factor,
origin + Vector2(11.0, 25.0) * scale_factor,
origin + Vector2(17.0, 27.0) * scale_factor,
])
canvas.draw_polyline(bow, tint, 2.4 * scale_factor, true)
canvas.draw_line(origin + Vector2(17.0, 3.0) * scale_factor, origin + Vector2(17.0, 27.0) * scale_factor, tint, 1.6 * scale_factor, true)
canvas.draw_line(origin + Vector2(2.0, 15.0) * scale_factor, origin + Vector2(26.0, 15.0) * scale_factor, tint, 2.6 * scale_factor, true)
canvas.draw_polyline(PackedVector2Array([
origin + Vector2(20.0, 9.0) * scale_factor,
origin + Vector2(27.0, 15.0) * scale_factor,
origin + Vector2(20.0, 21.0) * scale_factor,
]), tint, 2.6 * scale_factor, true)
static func _draw_set_down_prop(canvas: CanvasItem, origin: Vector2, scale_factor: float, tint: Color) -> void:
canvas.draw_line(origin + Vector2(14.0, 3.0) * scale_factor, origin + Vector2(14.0, 12.0) * scale_factor, tint, 2.4 * scale_factor, true)
canvas.draw_polyline(PackedVector2Array([
origin + Vector2(9.0, 8.0) * scale_factor,
origin + Vector2(14.0, 13.0) * scale_factor,
origin + Vector2(19.0, 8.0) * scale_factor,
]), tint, 2.4 * scale_factor, true)
canvas.draw_rect(Rect2(origin + Vector2(6.0, 14.0) * scale_factor, Vector2(16.0, 10.0) * scale_factor), tint, false, 2.2 * scale_factor, true)
canvas.draw_line(origin + Vector2(3.0, 27.0) * scale_factor, origin + Vector2(25.0, 27.0) * scale_factor, tint, 2.0 * scale_factor, true)
static func _draw_use_prop(canvas: CanvasItem, origin: Vector2, scale_factor: float, tint: Color) -> void:
canvas.draw_rect(Rect2(origin + Vector2(3.0, 12.0) * scale_factor, Vector2(16.0, 13.0) * scale_factor), tint, false, 2.2 * scale_factor, true)
var spark := PackedVector2Array([
origin + Vector2(22.0, 3.0) * scale_factor,
origin + Vector2(24.0, 9.0) * scale_factor,
origin + Vector2(28.0, 11.0) * scale_factor,
origin + Vector2(23.0, 13.0) * scale_factor,
origin + Vector2(21.0, 18.0) * scale_factor,
origin + Vector2(19.0, 13.0) * scale_factor,
origin + Vector2(15.0, 11.0) * scale_factor,
origin + Vector2(20.0, 9.0) * scale_factor,
origin + Vector2(22.0, 3.0) * scale_factor,
])
canvas.draw_polyline(spark, tint, 2.0 * scale_factor, true)
@@ -0,0 +1 @@
uid://jv06t4nihqrb
@@ -18,6 +18,7 @@ var _rename_button: Button
var _close_button: Button
var _scroll: ScrollContainer
var _body: VBoxContainer
var _pinned: VBoxContainer
var _collapsible: bool = false
var _collapsed: bool = false
var _close_visible: bool = true
@@ -167,6 +168,10 @@ func body() -> VBoxContainer:
return _body
func pinned_content() -> VBoxContainer:
return _pinned
func set_safe_rect(rect: Rect2) -> void:
_safe_rect = rect
request_fit()
@@ -184,6 +189,22 @@ func reset_scroll() -> void:
_scroll.scroll_vertical = 0
func get_body_scroll_vertical() -> int:
return _scroll.scroll_vertical if _scroll != null else 0
func set_body_scroll_vertical(value: int) -> void:
if _scroll != null:
_scroll.scroll_vertical = maxi(0, value)
func scroll_to_control(control: Control) -> void:
if _scroll == null or control == null or not is_instance_valid(control):
return
var content_y: float = control.global_position.y - _scroll.get_global_rect().position.y + _scroll.scroll_vertical
_scroll.scroll_vertical = maxi(0, int(roundf(content_y)))
func focus_first_control() -> void:
if _body == null:
return
@@ -274,10 +295,10 @@ func _build_shell() -> void:
_header.add_child(_icon)
_title = Label.new()
_title.name = "InspectorTitle"
_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_title.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
_title.max_lines_visible = 2
_title.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL | Control.SIZE_SHRINK_END
_title.mouse_filter = Control.MOUSE_FILTER_IGNORE
_title.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
_header.add_child(_title)
@@ -303,6 +324,10 @@ func _build_shell() -> void:
_close_button.focus_mode = Control.FOCUS_ALL
_close_button.pressed.connect(_on_close_button_pressed)
_header.add_child(_close_button)
_pinned = VBoxContainer.new()
_pinned.name = "InspectorPinnedContent"
_pinned.add_theme_constant_override("separation", 8)
_layout.add_child(_pinned)
_scroll = ScrollContainer.new()
_scroll.name = "InspectorBodyScroll"
@@ -326,7 +351,8 @@ func _fit_to_content() -> void:
rect = get_viewport_rect()
var inset := float(_integer("stage_panels.shell.edge_inset", 12, 0, 48))
var preferred_width := float(_integer("stage_panels.shell.preferred_width", 380, 220, 720))
var card_width := minf(preferred_width, maxf(1.0, rect.size.x - inset * 2.0))
var max_width := float(_integer("stage_panels.shell.max_width", 380, 220, 720))
var card_width := minf(minf(preferred_width, max_width), maxf(1.0, rect.size.x - inset * 2.0))
custom_minimum_size.x = card_width
size.x = card_width
await get_tree().process_frame
@@ -338,7 +364,9 @@ func _fit_to_content() -> void:
rect = _safe_rect
if rect.size.x <= 0.0 or rect.size.y <= 0.0:
rect = get_viewport_rect()
card_width = minf(preferred_width, maxf(1.0, rect.size.x - inset * 2.0))
preferred_width = float(_integer("stage_panels.shell.preferred_width", 380, 220, 720))
max_width = float(_integer("stage_panels.shell.max_width", 380, 220, 720))
card_width = minf(minf(preferred_width, max_width), maxf(1.0, rect.size.x - inset * 2.0))
custom_minimum_size.x = card_width
size.x = card_width
var padding := float(_integer("stage_panels.shell.padding", 12, 4, 28))
@@ -346,7 +374,9 @@ func _fit_to_content() -> void:
var header_height := maxf(44.0, _header.get_combined_minimum_size().y)
var body_height := _body.get_combined_minimum_size().y
var show_body: bool = not _collapsed and _body_visible
var fixed_height := header_height + (gap if show_body else 0.0) + padding * 2.0
var pinned_visible: bool = _pinned.visible and _pinned.get_child_count() > 0
var pinned_height: float = _pinned.get_combined_minimum_size().y if pinned_visible else 0.0
var fixed_height := header_height + (gap if pinned_visible else 0.0) + pinned_height + (gap if show_body else 0.0) + padding * 2.0
var available_height := maxf(72.0, rect.size.y - inset * 2.0)
var natural_height := fixed_height + (body_height if show_body else 0.0)
var card_height := minf(natural_height, available_height)
@@ -355,8 +385,12 @@ func _fit_to_content() -> void:
# previous full-content minimum can clamp the new height until another layout.
_scroll.custom_minimum_size.y = scroll_height
_scroll.update_minimum_size()
position = Vector2(rect.position.x + rect.size.x - inset - card_width, rect.position.y + inset)
size = Vector2(card_width, card_height)
# Containers can raise the actual width to fit their children. Anchor from that
# final width so a content minimum cannot consume the right-edge gutter.
var left_limit := rect.position.x + inset
var right_limit := rect.position.x + rect.size.x - inset - size.x
position = Vector2(clampf(right_limit, left_limit, maxf(left_limit, right_limit)), rect.position.y + inset)
_fit_pending = false