73f84e7d0d
Meter variant (default; M flips to the original unlimited toggle live): a Freeze charge regenerates while the hand is closed and drains while open. Level maps to bands — full/high: true pause; mid: thaw slow-mo ramping back to realtime as it drains; low: realtime card draw; empty: hand force-closes and will not reopen until regrown past the floor. Opening with a full meter buys the whole ladder (full freeze before thaw); empty-meter Space-spam cannot refreeze. Dim overlay depth telegraphs the band wordlessly; HUD meter bar shows band boundaries and fill colored by openable band. All rates/bands exported. Meter runs on the wall clock (Time.get_ticks_msec) — process delta is scaled by the very time_scale the hand animates, and pause zeroes physics delta. Design rationale (KB-grounded, pressure-tested 2026-08-07): prices the freeze-scout dominant strategy, adds temporal triangularity to chains, and yields a canon-safe difficulty/accessibility axis (meter size, regen, thaw length). Playtest variant — canon ratification pending operator verdict; CONCEPT.md entry lands in the advisor repo after. Smoke gate extended: band ladder (thaw slow-mo in (0,1), realtime x1, depletion force-close, empty-reopen refusal), regen while closed, toggle-mode A/B still hard-freezes. 3/3 green on nh3-dev headless 4.7.1.
62 lines
2.2 KiB
GDScript
62 lines
2.2 KiB
GDScript
extends Control
|
|
## Greybox hand HUD: two card slots (bottom center), charge pips above
|
|
## them, dilation knob readout (dev instrumentation, not game UI).
|
|
## Iconic, not textual — slots are tag-hue blocks, no card text.
|
|
|
|
@onready var _hand: CanvasLayer = get_parent()
|
|
|
|
|
|
func _draw() -> void:
|
|
var slot_w := 24.0
|
|
var slot_h := 32.0
|
|
var gap := 6.0
|
|
var total := slot_w * 2 + gap
|
|
var x0 := (640.0 - total) / 2.0
|
|
var y0 := 320.0
|
|
|
|
for i in _hand.cards.size():
|
|
var card: Dictionary = _hand.cards[i]
|
|
var rect := Rect2(x0 + i * (slot_w + gap), y0, slot_w, slot_h)
|
|
var affordable: bool = _hand.charges >= card.cost_of.call()
|
|
var hue: Color = card.hue
|
|
if not affordable:
|
|
hue = Color(hue.r, hue.g, hue.b, 0.25)
|
|
elif not _hand.hand_open:
|
|
hue = Color(hue.r, hue.g, hue.b, 0.6)
|
|
draw_rect(rect, hue)
|
|
if _hand.hand_open:
|
|
draw_rect(rect.grow(1.0), Color(1, 1, 1, 0.9 if affordable else 0.3), false, 1.0)
|
|
|
|
var pip := 5.0
|
|
var pips_w: float = float(_hand.max_charges) * (pip + 3.0) - 3.0
|
|
var px0: float = (640.0 - pips_w) / 2.0
|
|
for i in int(_hand.max_charges):
|
|
var filled: bool = i < int(_hand.charges)
|
|
var c := Color(0.9, 0.9, 0.95) if filled else Color(0.9, 0.9, 0.95, 0.18)
|
|
draw_rect(Rect2(px0 + i * (pip + 3.0), y0 - 10.0, pip, pip), c)
|
|
|
|
if _hand.meter_mode:
|
|
# Freeze meter: fill color names the band you'd open into.
|
|
var bw := 84.0
|
|
var bh := 4.0
|
|
var bx := (640.0 - bw) / 2.0
|
|
var by := y0 - 20.0
|
|
draw_rect(Rect2(bx, by, bw, bh), Color(0.9, 0.9, 0.95, 0.12))
|
|
var f: float = _hand.frac()
|
|
var col := Color(0.85, 0.95, 1.0)
|
|
if f < _hand.band_unavailable:
|
|
col = Color(0.8, 0.3, 0.25)
|
|
elif f < _hand.band_realtime:
|
|
col = Color(0.55, 0.57, 0.6)
|
|
elif f < _hand.band_thaw:
|
|
col = Color(0.55, 0.75, 0.95)
|
|
draw_rect(Rect2(bx, by, bw * f, bh), col)
|
|
for b: float in [_hand.band_unavailable, _hand.band_realtime, _hand.band_thaw]:
|
|
var tx := bx + bw * b
|
|
draw_line(Vector2(tx, by - 1), Vector2(tx, by + bh + 1), Color(0.1, 0.1, 0.12, 0.9), 1.0)
|
|
|
|
var font := ThemeDB.fallback_font
|
|
var label: String = "frz %.1f/%.1fs" % [_hand.meter, _hand.meter_max] if _hand.meter_mode else "dt x%.2f" % _hand.dilation_scale
|
|
draw_string(font, Vector2(560, 352), label,
|
|
HORIZONTAL_ALIGNMENT_LEFT, -1, 8, Color(0.6, 0.65, 0.7, 0.7))
|