extends SceneTree
## Gate: graphics presets (Low / Medium / High) and Cloud Quality
## (hyper-games #93 §2; Medium + cloud tiers 2026-10-03).
##
## Measured on a GTX 1060 before it throttled (#95 correction comment): GPU frame
## 18 ms = opaque pass 6.8 (terrain material) + SunshineClouds2 compositor 5.9 +
## sky 1.7 + shadows 0.4. Clouds are ~⅓ of the frame and the largest optional
## cost, but were only a map export (map_mountain) or a scene node (map_dc_terrain)
## — no player control. Low drops clouds and the per-pixel terrain material.
##
## A preset is an ACTION, not a stored value: choosing one writes the keys it
## covers as player overrides; it reads as selected only while every one matches.
##
## Run: HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_graphics_presets.gd
var ok := true
var _ran := false
func _p(c: bool, m: String) -> void:
print(("ok " if c else "FAIL: ") + m)
if not c:
ok = false
func _process(_delta: float) -> bool:
if _ran:
return true
_ran = true
_run()
print("GATE graphics_presets: %s" % ("PASS" if ok else "FAIL"))
quit(0 if ok else 1)
return true
func _run() -> void:
var s: Node = root.get_node_or_null(^"/root/Settings")
_p(s != null, "Settings autoload present")
if s == null:
return
var saved: Dictionary = s._overrides.duplicate()
print("-- schema")
var preset_row: Dictionary = s.schema_for("preset")
var clouds_row: Dictionary = s.schema_for("clouds")
_p(not preset_row.is_empty() and preset_row.get("choices", []) == ["Low", "Medium", "High"],
"a Preset row offers Low / Medium / High")
# Cloud Quality = SunshineClouds2 resolution_scale. Measured on an RTX 2070, mountain
# clouds (scenes/test/_probe_cloud_quality_cost.gd): off 0.15, Quarter 2.85,
# Half 4.66, Native 11.26 GPU ms.
_p(not clouds_row.is_empty() and clouds_row.get("type", "") == "enum"
and clouds_row.get("choices", []) == ["Off", "Quarter res", "Half res", "Full res"]
and clouds_row.get("values", []) == [-1, 2, 1, 0] and int(clouds_row.get("default", -9)) == 2,
"Clouds Off / Quarter / Half / Full res, Half by default")
_p(s.SCHEMA[0].get("key", "") == "preset", "Preset is the first row of the panel")
print("-- presets")
s._overrides.clear()
_p(int(s.get_value("preset")) == 2, "fresh settings read as High (High = today's defaults)")
s.set_value("preset", 0)
_p(int(s.get_value("terrain_material")) == 1, "Low: Vertex terrain material")
_p(int(s.get_value("clouds")) == 0, "Low: clouds off")
_p(is_equal_approx(float(s.resolved_value("render_scale")), 0.75), "Low: 75% render scale")
_p(int(s.get_value("aa")) == 0 and int(s.get_value("bloom")) == 1, "Low: MSAA off, bloom Low")
_p(int(s.get_value("preset")) == 0, "and it reads as Low")
s.set_value("preset", 1)
_p(int(s.get_value("terrain_material")) == 1 and int(s.resolved_value("clouds")) == 2
and is_equal_approx(float(s.resolved_value("render_scale")), 1.0) and int(s.get_value("aa")) == 0
and int(s.get_value("bloom")) == 2 and int(s.resolved_value("texture_res")) == 1024
and int(s.get_value("surface_detail")) == 0,
"Medium: Vertex / Quarter clouds / 100% / MSAA off / bloom High / 1024 / normal maps")
_p(int(s.get_value("preset")) == 1, "and it reads as Medium")
s.set_value("aa", 3)
_p(int(s.get_value("preset")) == -1, "changing any covered row reads as custom (no preset highlighted)")
s.set_value("preset", 2)
_p(int(s.get_value("terrain_material")) == 0 and int(s.resolved_value("clouds")) == 1
and is_equal_approx(float(s.resolved_value("render_scale")), 1.0) and int(s.get_value("aa")) == 1,
"High restores Full / Half clouds / 100% / MSAA 2X")
_p(int(s.get_value("preset")) == 2, "and reads as High")
s.set_value("clouds", 3)
_p(int(s.get_value("preset")) == -1, "Ultra clouds is no preset (high-end, set by hand)")
_p(not s._overrides.has("preset"), "the preset itself is never stored")
print("-- saved bool migrates")
var sp: String = s.SAVE_PATH
var had_file: bool = FileAccess.file_exists(sp)
var keep: String = FileAccess.get_file_as_string(sp) if had_file else ""
for case: Array in [[true, 2], [false, 0]]:
var f := FileAccess.open(sp, FileAccess.WRITE)
f.store_string(JSON.stringify({"clouds": case[0]}))
f.close()
s.load_settings()
_p(int(s.get_value("clouds")) == int(case[1]),
"a saved clouds=%s loads as %d (not int(bool))" % [str(case[0]), case[1]])
if had_file:
var f2 := FileAccess.open(sp, FileAccess.WRITE)
f2.store_string(keep)
f2.close()
else:
DirAccess.remove_absolute(ProjectSettings.globalize_path(sp))
print("-- clouds switch")
var we := WorldEnvironment.new()
we.environment = Environment.new()
we.compositor = Compositor.new()
var fx: CompositorEffect = SunshineCloudsGD.new()
fx.enabled = true
var broken: CompositorEffect = SunshineCloudsGD.new()
broken.enabled = false # as the addon leaves it when it fails to initialise
var other := CompositorEffect.new()
other.enabled = true
var list: Array[CompositorEffect] = [fx, broken, other]
we.compositor.compositor_effects = list
root.add_child(we)
s._apply_clouds(-1)
_p(not fx.enabled, "clouds off disables the SunshineClouds effect")
_p(other.enabled, "other compositor effects are untouched")
s._apply_clouds(1)
_p(fx.enabled and int(fx.get("resolution_scale")) == 1, "clouds High re-enables it at Half resolution")
_p(not broken.enabled, "an effect the ADDON disabled (failed init) is never forced on")
s._apply_clouds(0)
_p(int(fx.get("resolution_scale")) == 0, "Ultra = Native resolution")
s.set_value("clouds", 0)
s.apply_settings()
_p(not fx.enabled, "apply_settings applies the clouds row")
s.set_value("clouds", 1)
s.apply_settings()
_p(fx.enabled and int(fx.get("resolution_scale")) == 2, "and back on at Low = Quarter resolution")
we.queue_free()
s._overrides = saved