extends RefCounted
## The parametric shape TYPES, by id (docs/parametric-shapes-design.md). The id is
## what a shape.create record names, so it is durable: never rename one.
static var _types: Dictionary = {}
## IN TAB ORDER — the create tool cycles ids() as listed (§13: it makes only these).
## box / cylinder / cone / sphere are the baked generators in the local frame
## (param_basic.gd); the arch is the tunnel's round profile.
static func _ensure() -> void:
if not _types.is_empty():
return
var basic := preload("res://scenes/maps/shapes/param/param_basic.gd")
var list: Array = [basic.new("box", BoxShape.new()),
preload("res://scenes/maps/shapes/param/param_stairs.gd").new(),
preload("res://scenes/maps/shapes/param/param_spiral_stairs.gd").new(),
preload("res://scenes/maps/shapes/param/param_tunnel.gd").new(),
basic.new("cylinder", CylinderShape.new()),
basic.new("cone", ConeShape.new()),
basic.new("sphere", SphereShape.new())]
for t: Object in list:
_types[t.id()] = t
# Generated FROM a brush (Make parametric), never drawn with the create tool, so it is
# known to the fold but not in ids() (#120 F2).
_types["hull"] = preload("res://scenes/maps/shapes/param/param_hull.gd").new()
# Generated ON a curve (Add → Cable, #120 L3), not drawn as a box: also not in ids().
_types["cable"] = preload("res://scenes/maps/shapes/param/param_cable.gd").new()
# Drawn by BoxCutter (#120 L2 §13.1), not by the create tool: also not in ids().
_types["prism"] = preload("res://scenes/maps/shapes/param/param_prism.gd").new()
static func get_type(type_id: String) -> Object:
_ensure()
return _types.get(type_id, null)
## The create tool's types, in tab order — every registered type but those that only
## come from a brush (hull), a curve (cable) or the BoxCutter (prism).
static func ids() -> Array:
_ensure()
return _types.keys().filter(func(k: String) -> bool: return k != "hull" and k != "cable" and k != "prism")
## A type's params as ToolSettingsBar rows ("p.<id>"), valued from `values` (resolved).
## The bar draws an enum from `choices` + an INDEX; a descriptor names `options` + a
## string — converted here and back in value_of, so the create tool and the Shape panel
## cannot disagree about it.
static func rows(t: Object, values: Dictionary) -> Array:
var out: Array = []
for d: Dictionary in (t.params() as Array):
if bool(d.get("hidden", false)):
continue # data, not a knob (the hull's planes)
var row: Dictionary = d.duplicate()
row["id"] = "p." + String(d["id"])
var v: Variant = values.get(String(d["id"]), d["default"])
if String(d.get("type")) == "enum":
var opts: Array = d.get("options", [])
row["choices"] = opts.map(func(o: Variant) -> String: return String(o).capitalize())
row["value"] = maxi(0, opts.find(String(v)))
else:
row["value"] = v
out.append(row)
return out
## A bar value for param `pid` -> the record's value (an enum index -> its option), or
## null for an unknown param.
static func value_of(t: Object, pid: String, v: Variant) -> Variant:
for d: Dictionary in (t.params() as Array):
if String(d["id"]) != pid:
continue
if String(d.get("type")) == "enum":
var opts: Array = d.get("options", [])
if v is int or v is float:
return String(opts[clampi(int(v), 0, opts.size() - 1)]) if not opts.is_empty() else ""
return String(v)
return t.resolve({pid: v})[pid]
return null