OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_texture_strip.gd

165 lines · gd ref:
extends SceneTree
## GATE — the texture strip (hyper-games #94 P1 §9, first slice): the bottom-row
## browser, picking the current texture, apply-to-selection (U) and the eyedropper
## (Y), on a REAL empty-level map (map_flat) in the tree.
##
## Apply must land LOCALLY (the author never refolds its own echo) and in the
## JOURNAL (a refold reproduces it) — both are asserted, because either alone
## passes while one side of the room sees something different.
##
## Run: "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_texture_strip.gd

var ok := true
var checks := 0
var _frame := 0
var _map: Node = null


func _p(cond: bool, what: String, note: String = "") -> void:
	checks += 1
	if not cond:
		ok = false
	print("  %-62s %s  %s" % [what, "OK" if cond else "**FAIL**", note])


func _process(_delta: float) -> bool:
	_frame += 1
	if _frame == 1:
		var scene := load("res://scenes/maps/map_flat.tscn") as PackedScene
		_map = scene.instantiate()
		root.add_child(_map)
		return false
	if _frame < 30:
		return false
	_run()
	if checks < 20:
		_p(false, "only %d checks ran -- setup died before the assertions" % checks)
	print("GATE texture_strip: %s (%d checks)" % ["PASS" if ok else "FAIL", checks])
	quit(0 if ok else 1)
	return true


func _box(lo: Vector3, hi: Vector3) -> Array:
	return [1, 0, 0, hi.x, -1, 0, 0, -lo.x, 0, 1, 0, hi.y, 0, -1, 0, -lo.y, 0, 0, 1, hi.z, 0, 0, -1, -lo.z]


func _texes(level: Node, id: int) -> Array:
	var out: Array = []
	for t: Dictionary in level.get_brush(id).get("face_tex", []):
		out.append(String(t.get("tex", "")))
	return out


func _run() -> void:
	var dock: Node = root.get_node_or_null("/root/BuildDock")
	var level: Node = _map.get_node_or_null("DCLevel")
	var persist: Node = _map.get("_persist")
	var gs: Node = root.get_node_or_null("/root/GameState")
	var keet: Node = root.get_node_or_null("/root/KeetService")
	if keet != null:
		keet.current_member_id = "gate-author"
	if gs != null:
		gs.route("world:level:*", 1, "peer")

	print("-- the strip is a BOTTOM row of the dock --")
	_p(dock != null and dock.has_panel("textures"), "map_flat registered the 'textures' panel")
	if dock == null or not dock.has_panel("textures"):
		return
	_p(String(dock._panels["textures"]["side"]) == "bottom", "on the BOTTOM side",
		String(dock._panels["textures"]["side"]))
	var strip: Node = _map.get("_tex_strip")
	_p(strip != null, "the map keeps a handle to the strip")
	if strip == null:
		return

	print("\n-- browse: search and pack filter --")
	strip.set_filter("", "")
	# 1196 + 10 trimsheets.com textures + 1 composed trim sheet (2026-10-03).
	_p(strip.shown_ids().size() + strip.hidden_count() == 1207, "no filter: every texture is listed or counted",
		"%d shown + %d more" % [strip.shown_ids().size(), strip.hidden_count()])
	strip.set_filter("brick", "")
	var hits: PackedStringArray = strip.shown_ids()
	var all_brick := hits.size() > 0
	for id in hits:
		all_brick = all_brick and id.to_lower().contains("brick")
	_p(all_brick, "search narrows to matching ids", "%d hits" % hits.size())
	strip.set_filter("", "kenney-prototype")
	_p(strip.shown_ids().size() == 78, "pack filter: Kenney only", str(strip.shown_ids().size()))
	strip.set_filter("", "")
	# The strip makes previews lazily only while VISIBLE (never in a headless gate),
	# so the preview itself is asserted directly.
	var th: Texture2D = TexturePacks.thumbnail("ambientcg/Bricks105", 64)
	_p(th != null and th.get_width() == 64 and th.get_height() == 64, "a 64 px preview, from a 1024x512 albedo")
	_p(TexturePacks.thumbnail_cached("ambientcg/Bricks105") == th, "and it is cached, not re-decoded")

	print("\n-- pick: a click sets the current texture --")
	strip.pick("ambientcg/Bricks105")
	_p(String(_map.get("current_tex")) == "ambientcg/Bricks105", "current_tex follows the click")
	_p(strip.recent().size() >= 1 and strip.recent()[0] == "ambientcg/Bricks105", "and it heads the recent list")
	_p(String(_map.current_face_tex().get("tex", "")) == "ambientcg/Bricks105", "new brushes would get it")

	print("\n-- apply (U) to a BRUSH selection: every face, locally AND in the journal --")
	var ft: Array = []
	for i in 6:
		ft.append({"tex": "kenney-prototype/dark_01", "mode": 1, "off_u": 0.3})
	var bdot: String = persist.record("brush.create", {
		"planes": _box(Vector3(0, 0, 0), Vector3(2, 2, 2)), "proj": [], "material_id": 1,
		"level_id": 0, "sky": false, "face_tex": ft})
	persist.refold()
	var bid: int = level.brush_by_key(bdot)
	_map.select_brushes(PackedInt32Array([bid]))
	var n: int = _map.apply_texture_to_selection()
	_p(n == 6, "applied to all 6 faces", str(n))
	var local: Array = _texes(level, bid)
	_p(local.count("ambientcg/Bricks105") == 6, "LOCAL brush shows it with no refold", str(local))
	var t0: Dictionary = level.get_brush(bid)["face_tex"][0]
	_p(is_equal_approx(float(t0.get("off_u", 0.0)), 0.3), "alignment kept (only the texture changed)", str(t0.get("off_u")))
	var nrec := 0
	for r: Dictionary in persist.records():
		if String(r.get("kind", "")) == "face.tex":
			nrec += 1
	_p(nrec == 1, "ONE face.tex record for the brush", str(nrec))
	persist.refold()
	_p(_texes(level, bid).count("ambientcg/Bricks105") == 6, "a refold reproduces it")

	print("\n-- apply to a FACE selection: that face only --")
	strip.pick("polyhaven/clean_pebbles")
	var keys: PackedStringArray = level.get_brush(bid).get("plane_keys", PackedStringArray())
	_map.set("_sel_brushes", [bid] as Array[int])
	_map.set("_sel_brush", bid)
	_map.set("_sel_kind", "face")
	_map.set("_sel_keys", PackedStringArray([keys[2]]))
	_p(_map.apply_texture_to_selection() == 1, "applied to one face")
	var after: Array = _texes(level, bid)
	_p(after[2] == "polyhaven/clean_pebbles" and after.count("ambientcg/Bricks105") == 5, "only that face changed", str(after))

	print("\n-- an UNTEXTURED face becomes paraxial --")
	var udot: String = persist.record("brush.create", {
		"planes": _box(Vector3(5, 0, 0), Vector3(6, 1, 1)), "proj": [], "material_id": 1,
		"level_id": 0, "sky": false})
	persist.refold()
	var uid: int = level.brush_by_key(udot)
	_map.select_brushes(PackedInt32Array([uid]))
	_map.apply_texture_to_selection()
	var ut: Dictionary = level.get_brush(uid)["face_tex"][0]
	_p(String(ut.get("tex", "")) == "polyhaven/clean_pebbles" and int(ut.get("mode", 0)) == 1,
		"texture AND paraxial mode", str(ut))
	persist.refold()
	_p(int(level.get_brush(uid)["face_tex"][0].get("mode", 0)) == 1, "and the refold agrees")

	print("\n-- eyedropper (Y) --")
	strip.pick("kenney-prototype/dark_01")
	var got: String = _map.eyedrop_texture({"brush": bid, "position": Vector3(1, 1, 2)})   # on the +z face
	_p(got != "" and String(_map.get("current_tex")) == got, "picks the aimed face's texture", got)
	_p(got == "ambientcg/Bricks105" or got == "polyhaven/clean_pebbles", "a texture that face really has", got)

	print("\n-- keys --")
	var km: Node = root.get_node_or_null("/root/Keymap")
	# Keymap step 3: U is the texture menu; apply / pick have their own keys in it.
	var ap: Dictionary = km.binding_of("builder.texture_apply")
	var pk: Dictionary = km.binding_of("builder.texture_pick")
	_p(int(ap.get("key", 0)) == KEY_U and bool(ap.get("ctrl", false)), "Ctrl+U applies", str(ap))
	_p(int(pk.get("key", 0)) == KEY_U and bool(pk.get("alt", false)), "Alt+U picks (eyedropper)", str(pk))
	_p(int(km.binding_of("builder.uv_menu").get("key", 0)) == KEY_U, "U opens the texture menu")
	_p((km.conflicts() as Array).is_empty(), "no key conflicts", str(km.conflicts()))