extends VBoxContainer
## THE SHAPE PANEL (docs/parametric-shapes-design.md §4): the selected parametric
## shape's parameters, editable forever — a change writes ONE shape.set (one gesture,
## one Ctrl+Z) and the fold regenerates the pieces. Plus Make Real (§6) and Delete.
##
## Drawn by the same renderer as the tool row (ToolSettingsBar), pointed at a small
## operator-shaped TARGET whose rows are the type's descriptors (ParamShapes.rows), so
## the create tool and this panel show a parameter the same way.
const ParamShapes := preload("res://scenes/maps/shapes/param/param_shapes.gd")
var map: Node = null
var _title: Label = null
var _hint: Label = null
var _bar: Node = null
var _target: ShapeTarget = null
var _dot := ""
class ShapeTarget extends LevelOperator:
var dot := ""
func _type() -> Object:
var st: Dictionary = map._persist.shape_state(dot) if map != null and dot != "" else {}
return ParamShapes.get_type(String(st.get("type", ""))) if not st.is_empty() else null
func op_name() -> String:
var t := _type()
return String(t.label()) if t != null else "shape"
func tool_settings() -> Array:
var t := _type()
if t == null:
return []
var st: Dictionary = map._persist.shape_state(dot)
var out: Array = ParamShapes.rows(t, t.resolve(st.get("params", {})))
if String(st.get("type", "")) == "cable":
# PROFILE CURVE (§14.2): a dropdown of the room's CLOSED curves, as follow-curve
# picks its curve in the Stack panel. Index 0 = none.
var dots := profile_curves()
var names: Array = ["—"]
var curves: Dictionary = map._persist.curve_states()
for d: String in dots:
names.append(String(curves[d].get("name", d)))
out.append({"id": "profile_curve", "label": "Profile curve", "type": "enum", "choices": names,
"value": dots.find(String((st["params"] as Dictionary).get("profile_curve", ""))) + 1,
"tip": "A closed curve whose outline is the cable's cross-section (concave outlines use their hull)"})
out.append({"id": "make_real", "label": "Make Real", "type": "action",
"tip": "Turn the pieces into plain brushes you can edit one by one (Ctrl+Z undoes)."})
out.append({"id": "delete", "label": "Delete", "type": "action", "tip": "Delete the shape (Ctrl+Z undoes)."})
return out
## The room's closed curves, by dot (sorted: the same order on every peer and every frame).
func profile_curves() -> Array:
var curves: Dictionary = map._persist.curve_states()
var out: Array = []
for d: String in curves:
if bool(curves[d].get("closed", false)):
out.append(d)
out.sort()
return out
func adjust_ids() -> Array:
var ids: Array = []
for d: Dictionary in tool_settings():
ids.append(String(d["id"]))
return ids
func setup(map_node: Node) -> void:
map = map_node
_title = Label.new()
add_child(_title)
_hint = Label.new()
_hint.text = "Select a shape (◇) to edit it."
_hint.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7))
add_child(_hint)
_target = ShapeTarget.new()
_target.map = map
_target.level = map.get("level")
_bar = HFlowContainer.new()
_bar.set_script(load("res://scenes/game/tool_settings_bar.gd"))
_bar.target = _target
_bar.apply_override = Callable(self, "apply")
_bar.setup(map)
add_child(_bar)
_follow()
## The shape this panel is showing ("" = none).
func shown_dot() -> String:
_follow()
return _dot
func setting_ids() -> Array:
_follow()
return _target.adjust_ids() if _dot != "" else []
func _process(_d: float) -> void:
_follow()
func _follow() -> void:
var dot: String = map.selected_shape() if map != null and map.has_method("selected_shape") else ""
if dot == _dot:
return
_dot = dot
_target.dot = dot
_bar.visible = dot != ""
_hint.visible = dot == ""
_title.text = "%s ◇ %s" % [_target.op_name(), dot] if dot != "" else ""
## A change from the bar (or a gate): a param -> one shape.set; or an action.
func apply(sid: String, v: Variant) -> void:
_follow()
if _dot == "":
return
match sid:
"make_real":
map.make_real(_dot)
"delete":
map.delete_brushes(Array(map.level.get_group_brush_ids(_dot)))
"profile_curve":
# One shape.set: the curve AND profile custom (none = back to round).
var dots: Array = _target.profile_curves()
var i := int(v) - 1
if i >= 0 and i < dots.size():
map.set_shape_params(_dot, {"profile": "custom", "profile_curve": dots[i]}, "Profile curve")
else:
map.set_shape_params(_dot, {"profile": "round", "profile_curve": ""}, "Profile curve")
_:
if not sid.begins_with("p."):
return
var t := _target._type()
var pv: Variant = ParamShapes.value_of(t, sid.substr(2), v) if t != null else null
if pv != null:
map.set_shape_params(_dot, {sid.substr(2): pv})