92 lines
3.7 KiB
GDScript
92 lines
3.7 KiB
GDScript
extends SceneTree
|
|
|
|
## Difficulty judgement windows + the dynamic bad-window cap
|
|
## (AnchorV1.0 §9.5 / §21.8).
|
|
|
|
var failures: Array[String] = []
|
|
|
|
|
|
func _init() -> void:
|
|
_run.call_deferred()
|
|
|
|
|
|
func _run() -> void:
|
|
var settings: Node = load("res://autoload/game_settings.gd").new()
|
|
root.add_child(settings)
|
|
var rhythm: Node = load("res://autoload/rhythm_manager.gd").new()
|
|
rhythm.set("starts_on_ready", false)
|
|
root.add_child(rhythm)
|
|
await process_frame
|
|
|
|
# --- §21.8 window tables ---
|
|
_expect_vector(settings.call("difficulty_windows", &"easy"), Vector3(0.090, 0.160, 0.260), "easy windows")
|
|
_expect_vector(settings.call("difficulty_windows", &"normal"), Vector3(0.060, 0.115, 0.215), "normal windows")
|
|
_expect_vector(settings.call("difficulty_windows", &"hard"), Vector3(0.030, 0.070, 0.170), "hard windows")
|
|
|
|
# --- applying a difficulty rewrites the rhythm manager windows ---
|
|
var windows: Vector3 = settings.call("difficulty_windows", &"hard")
|
|
rhythm.call("apply_judgement_windows", windows.x, windows.y, windows.z)
|
|
_expect_float(float(rhythm.get("perfect_window")), 0.030, "hard perfect window applied")
|
|
_expect_float(float(rhythm.get("good_window")), 0.070, "hard good window applied")
|
|
_expect_float(float(rhythm.get("bad_window")), 0.170, "hard bad window applied")
|
|
|
|
# --- default (normal) values live on the manager itself ---
|
|
var fresh: Node = load("res://autoload/rhythm_manager.gd").new()
|
|
fresh.set("starts_on_ready", false)
|
|
root.add_child(fresh)
|
|
await process_frame
|
|
_expect_float(float(fresh.get("perfect_window")), 0.060, "default perfect window is Normal")
|
|
_expect_float(float(fresh.get("good_window")), 0.115, "default good window is Normal")
|
|
_expect_float(float(fresh.get("bad_window")), 0.215, "default bad window is Normal")
|
|
|
|
# --- §9.5 dynamic cap: bad <= beat_interval * 0.48, cascading ---
|
|
fresh.set("bpm", 240.0) # beat_time 0.25s → cap 0.12
|
|
var effective: Vector3 = fresh.call("effective_windows")
|
|
_expect_float(effective.z, 0.12, "bad window capped at 48% of the beat")
|
|
_expect_float(effective.y, 0.115, "good window stays when under the cap")
|
|
_expect_float(effective.x, 0.060, "perfect window stays when under the cap")
|
|
|
|
fresh.set("bpm", 600.0) # beat_time 0.1s → cap 0.048: everything collapses
|
|
effective = fresh.call("effective_windows")
|
|
_expect_float(effective.z, 0.048, "extreme BPM bad cap")
|
|
_expect_float(effective.y, 0.048, "good window can never exceed the capped bad window")
|
|
_expect_float(effective.x, 0.048, "perfect window can never exceed the capped good window")
|
|
|
|
# --- the cap is what judgement actually uses ---
|
|
fresh.set("bpm", 240.0)
|
|
fresh.set("running", true)
|
|
var rating: Dictionary = fresh.call("get_rating_for_time", 0.25 + 0.13)
|
|
_expect_string(str(rating.get("label")), "miss", "an offset outside the capped bad window judges miss")
|
|
rating = fresh.call("get_rating_for_time", 0.25 + 0.118)
|
|
_expect_string(str(rating.get("label")), "bad", "an offset between good and the capped bad window judges bad")
|
|
|
|
settings.free()
|
|
rhythm.free()
|
|
fresh.free()
|
|
_finish()
|
|
|
|
|
|
func _expect_vector(actual: Vector3, expected: Vector3, label: String) -> void:
|
|
if (actual - expected).length() > 0.0005:
|
|
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
|
|
|
|
|
func _expect_float(actual: float, expected: float, label: String) -> void:
|
|
if absf(actual - expected) > 0.0005:
|
|
failures.append("%s: expected %f, got %f" % [label, expected, actual])
|
|
|
|
|
|
func _expect_string(actual: String, expected: String, label: String) -> void:
|
|
if actual != expected:
|
|
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
|
|
|
|
|
func _finish() -> void:
|
|
if failures.is_empty():
|
|
print("PASS difficulty windows")
|
|
quit(0)
|
|
else:
|
|
for failure: String in failures:
|
|
push_error(failure)
|
|
quit(1)
|