OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_geiss_editable.gd

142 lines · gd ref:
extends SceneTree
## GATE — the Geiss maps are WORLDS like the mountain: the game's sky with a cloud bank,
## brushes, and terrain CSG (user 2026-10-06: "add a cloudshader and a proper world
## environment to geiss as well as the ability to build brushes and also to do csg
## edits on the terrain").
##   E1  CSG: a left click (player.fire) with the mouse captured carves the terrain —
##       through the REAL input chain. map_geiss overrode map_core's _unhandled_input
##       without calling super, so the universal carve never ran on any Geiss map.
##   E2  brushes: the map is a map_dc_level (brush store, brush editor, journal) with a
##       DCLevel node
##   E3  environment: under the game's shared sky (/root/Game/Sky3DExtended) the map
##       owns NO WorldEnvironment or Sun of its own — a map-owned one makes game.gd
##       detach the shared sky (day/night, Atmosphere panel)
##   E4  clouds: a SunshineClouds effect is on the shared sky's compositor while the map
##       is in, and gone after it leaves
## The shared sky is stood up here as the game builds it: a WorldEnvironment named
## Sky3DExtended under a node named Game, with SunLight / MoonLight children.
##
## ⚠ WINDOWED (the carve needs built collision):
##   env -u CI HYPERTPS_DAEMON_TCP=127.0.0.1:1 DISPLAY=:0 "$HYPER_TPS_GODOT" --path . \
##       --script res://scenes/test/gate_geiss_editable.gd [-- map_geiss_world]

const ProbeWindow := preload("res://scenes/test/probe_window.gd")

var ok := true
var checks := 0
var _ws := ""
var _name := ""
var _map: Node
var _sky: WorldEnvironment
var _f := 0
var _before := -1


func _p(c: bool, m: String, got: Variant = "") -> void:
	checks += 1
	print(("ok   " if c else "FAIL: ") + m + ("" if str(got) == "" else "  (" + str(got) + ")"))
	if not c:
		ok = false


func _initialize() -> void:
	var a := OS.get_cmdline_user_args()
	_name = a[0] if a.size() > 0 else "map_geiss"
	_ws = ProbeWindow.free_workspace()
	var game := Node3D.new()
	game.name = "Game"
	root.add_child(game)
	_sky = WorldEnvironment.new()
	_sky.name = "Sky3DExtended"
	_sky.environment = Environment.new()
	game.add_child(_sky)
	for l in ["SunLight", "MoonLight"]:
		var dl := DirectionalLight3D.new()
		dl.name = l
		_sky.add_child(dl)
	# map_geiss's surface is near y=0; the terrace maps derive their own camera start
	# from the field (a fixed one can sit inside a 300 m terrace and see no surface).
	# map_geiss_terraces' own start is an overview 600 m out — beyond the carve's
	# 200 m reach — so it gets one just over its surface (measured 17.8 m at (0, 16)).
	if _name == "map_geiss":
		OS.set_environment("GEISS_CAM", "0,40,16,0,-60")
	elif _name == "map_geiss_terraces":
		OS.set_environment("GEISS_CAM", "0,60,70,0,-40")   # 2x field: surface ~36 m at (0, 32)
	elif _name == "map_geiss_world":      # its own start is ~200 m up, at the carve's reach
		OS.set_environment("GEISS_CAM", "0,75,70,0,-40")   # 2x field: ground ~51 m at (0, 32)
	_map = (load("res://scenes/maps/%s.tscn" % _name) as PackedScene).instantiate()
	root.add_child(_map)


func _process(_d: float) -> bool:
	if not ProbeWindow.drawing(root):
		ProbeWindow.move_to(_ws, Vector2i(1600, 900))
	_f += 1
	if _f == 2:
		# ---- E2 / E3 / E4: what the map did on entry ----
		_p(_map.get_script() != null and _inherits(_map.get_script(), "res://scenes/maps/map_dc_level.gd"),
			"E2 %s is a map_dc_level (brush store, editor, journal)" % _name)
		_p(_map.get_node_or_null("DCLevel") != null, "E2 it has a DCLevel to build brushes in")
		var own_env := 0
		var own_sun := 0
		for c in _map.get_children():
			if c is WorldEnvironment: own_env += 1
			if c is DirectionalLight3D: own_sun += 1
		_p(own_env == 0 and own_sun == 0, "E3 under the shared sky the map owns no WorldEnvironment / Sun",
			"%d env, %d sun" % [own_env, own_sun])
		_p(_clouds_on_sky() > 0, "E4 a SunshineClouds effect is on the shared sky's compositor", _clouds_on_sky())
		return false
	if _f < 600:     # stream ~10 s so the carve has collision under the crosshair
		return false
	var t: Array = _map.find_children("*", "DCTerrain", true, false)
	if t.is_empty():
		_p(false, "E1 the map has a DCTerrain")
		return _finish()
	if _f == 600:
		_before = int(t[0].call("get_edit_count"))
		Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
		var ev := InputEventMouseButton.new()
		ev.button_index = MOUSE_BUTTON_LEFT
		ev.pressed = true
		ev.position = Vector2(root.size) * 0.5
		ev.global_position = ev.position
		Input.parse_input_event(ev)
		var up := ev.duplicate() as InputEventMouseButton
		up.pressed = false
		Input.parse_input_event(up)
		return false
	if _f < 620:
		return false
	var after := int(t[0].call("get_edit_count"))
	_p(after > _before, "E1 a left click with the mouse captured carves the terrain", "edits %d -> %d" % [_before, after])
	Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
	# ---- E4: leaving takes the clouds with it ----
	root.remove_child(_map)
	_map.free()
	_p(_clouds_on_sky() == 0, "E4 after the map leaves, its clouds are off the shared sky", _clouds_on_sky())
	return _finish()


func _finish() -> bool:
	print("\n%d checks — %s" % [checks, "PASS" if ok else "FAIL"])
	quit(0 if ok else 1)
	return true


func _clouds_on_sky() -> int:
	if _sky.compositor == null:
		return 0
	var n := 0
	for fx in _sky.compositor.compositor_effects:
		if fx != null and fx.get_class() != "" and fx is SunshineCloudsGD:
			n += 1
	return n


func _inherits(s: Script, path: String) -> bool:
	while s != null:
		if s.resource_path == path:
			return true
		s = s.get_base_script()
	return false