OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / maps / operators / drop_cable_operator.gd

120 lines · gd ref:
extends LevelOperator
## DROP TO THE GROUND (#120 L3, docs/node-tree-and-stacks.md §14.3): Cablerator's Drop the
## Cable on the selected cables. Every point but an open cable's two ends falls straight
## down and rests on what it hits; spans longer than `step` get points first, so the cable
## follows the ground. The preview draws the dropped curves; Enter writes ONE curve.set per
## cable (map.drop_cables, one gesture). Adjust Last re-runs it with another step.

var step := 1.0                       # metres between added points (0 = none added)
var _targets: Array = []              # the cables' dots
var _frozen := false                  # Adjust Last: act on the stored cables, not the selection


func op_name() -> String:
	return "drop to ground"


## The mouse drags the focused value (§17.7 Q2): this tool works on the selection.
func drags_value() -> bool:
	return true


func gesture_label() -> String:
	return "Drop to ground"


func preview_color() -> Color:
	return Color(1.0, 0.85, 0.3, 0.9)     # the curve view's amber


func setup_modal(m: ModalControls) -> void:
	m.confirm_label = "drop"
	m.table([["", "cables", func() -> String: return "%d" % _targets.size()]])
	m.bind("step", "add points every", func() -> float: return step,
		func(v: float) -> void:
			step = maxf(0.0, v)
			update_preview(),
		0.25, " m", 0.05)


func status() -> String:
	return modal().status_text()


func on_activate() -> bool:
	if not _frozen:
		_targets = map.selected_cables() if map != null and map.has_method("selected_cables") else []
	if _targets.is_empty():
		print("[drop] select a cable first")
		return false
	update_preview()
	return true


func on_deactivate() -> void:
	clear_preview()


func handle_key(event: InputEvent) -> bool:
	return modal().handle_key(event)


## The dropped curves, as lines (nothing is written until confirm).
func update_preview() -> void:
	clear_preview()
	var lines := PackedVector3Array()
	for d: String in _targets:
		var pts: Array = map.dropped_points(d, step)
		var curves: Dictionary = map.curve_states()
		var cd := String((map._persist.shape_state(d)["params"] as Dictionary).get("curve", ""))
		var closed := bool(curves.get(cd, {}).get("closed", false))
		if pts.size() < 2:
			continue
		var fs := CurveMath.sample(pts, closed, 64)
		for i in range(1, fs.size()):
			lines.append(fs[i - 1]["p"])
			lines.append(fs[i]["p"])
	draw_lines(lines)


func confirm() -> bool:
	var n: int = map.drop_cables(_targets, step)
	if n == 0:
		return false
	ModalControls.summary("Dropped %d cable%s%s" % [n, "" if n == 1 else "s",
		", a point every %.2f m" % step if step > 0.0 else ""])
	return true


func tool_settings() -> Array:
	return [{"id": "step", "label": "Add points every", "type": "float", "value": step, "min": 0.0,
		"max": 20.0, "step": 0.25, "tip": "Spans longer than this get points first, so the cable follows the ground (0 = none)."}]


func set_tool_setting(sid: String, value: Variant) -> bool:
	if sid != "step":
		return false
	step = maxf(0.0, float(value))
	if not _frozen:
		update_preview()
	return true


func adjust_ids() -> Array:
	return ["step"]


func adjust_snapshot() -> Dictionary:
	return {"targets": _targets.duplicate(), "step": step}


func adjust_restore(snap: Dictionary) -> void:
	_targets = (snap.get("targets", []) as Array).duplicate()
	step = float(snap.get("step", step))
	_frozen = true


func adjust_end() -> void:
	_frozen = false
	clear_preview()