Files
Cinderwild/tests/test_hints.gd

169 lines
5.3 KiB
GDScript

extends SceneTree
## T4 (docs/TUTORIAL_DESIGN.md §5): Old Tam's tips in a normal world. Each
## fits its moment, shows once, waits its turn, stays put while you're dead,
## is remembered across sessions, can be switched off or shown again, and
## never runs in the tutorial world.
## Run: godot --headless --path . --script res://tests/test_hints.gd
var _fails := 0
var _finished := false
var H: Node
var S: Node
var world: Node3D
var player: CharacterBody3D
var sv: Node
var got: Array = []
var card: Control
func check(cond: bool, what: String) -> void:
print((" ok " if cond else " FAIL ") + what)
if not cond:
_fails += 1
func _initialize() -> void:
_run.call_deferred()
func _run() -> void:
H = root.get_node("Hints")
S = root.get_node("Saves")
H.path = "user://test_hints.json"
H.allow_headless = true
DirAccess.remove_absolute(H.path)
H.load_prefs()
H.shown.connect(func(h): got.append(String(h.id)))
S.root_dir = "user://test_worlds"
for w in S.list_worlds():
S.delete_world(w.id)
await _body()
check(_finished, "test ran to the end (no script errors)")
for w in S.list_worlds():
S.delete_world(w.id)
DirAccess.remove_absolute(S.root_dir)
DirAccess.remove_absolute(H.path)
print("RESULT: %s (%d failures)" % ["PASS" if _fails == 0 else "FAIL", _fails])
quit(1 if _fails > 0 else 0)
func _load(tutorial: bool) -> void:
if world != null:
world.queue_free()
await process_frame
S.current_id = S.create_world("Tips" if not tutorial else "Tutorial", 1337, {"tutorial": tutorial})
world = load("res://scenes/world.tscn").instantiate()
root.add_child(world)
for _i in range(5):
await physics_frame
player = world.player
player.set_physics_process(false)
sv = player.survival
sv.set_process(false)
world.day_night.frozen = true
world.day_night.time = 10.0 / 24.0
world.day_night.apply(true)
world.weather.set_process(false)
world.weather.force("clear")
if world.wolves != null:
world.wolves.set_physics_process(false)
world.creatures.enabled = false
## Runs the tips for `secs` (as frames of 0.5 s), with your statuses fresh.
func _for(secs: float) -> void:
var t := 0.0
while t < secs:
sv._update_statuses()
H.step(0.5)
t += 0.5
func _body() -> void:
print("the tips")
var kinds := ["event", "status", "weather", "has", "night", "stamina_low", "wolf_moon", "hard_winter", "worldlet", "seam"]
var bad: Array = []
for h in H.hints:
if not String(h.when).get_slice(":", 0) in kinds or String(h.title).is_empty() or String(h.text).is_empty():
bad.append(h.id)
check(H.hints.size() >= 12 and bad.is_empty(), "%d tips, each with a title, text and a condition it knows %s" % [H.hints.size(), bad])
check(H.enabled and H.seen.is_empty(), "on by default, none seen yet")
print("a normal world")
await _load(false)
check(H.active and H.world == world, "they run in your own world")
card = load("res://scripts/ui/hint_card.gd").new()
root.add_child(card)
card.setup()
await _for(10.0)
check(got.is_empty(), "a calm, dry, fed morning: nothing to say")
sv.wet = 60.0
await _for(3.0)
check(got == ["wet"], "soaked: the wet tip")
check(card.is_showing() and card._title.text == "Soaked" and card._text.text.begins_with("\"Wet clothes"), "the card shows it, in Old Tam's words")
sv.wet = 0.0
sv.hunger = 10.0
await _for(6.0)
check(got.size() == 1, "hungry straight after: it waits its turn")
await _for(14.0)
check(got.back() == "hungry", "then the hungry tip")
sv.hunger = 90.0
sv.wet = 60.0
await _for(20.0)
check(got.count("wet") == 1, "soaked again: once is enough")
sv.wet = 0.0
print("the setting")
H.set_enabled(false)
world.day_night.time = 23.0 / 24.0
world.day_night.apply(true)
await _for(25.0)
check(got.size() == 2, "tips off: nightfall goes by without one")
var f: Variant = JSON.parse_string(FileAccess.get_file_as_string(H.path))
check(f is Dictionary and f.enabled == false and f.seen == ["wet", "hungry"], "the setting and the seen tips are saved")
H.set_enabled(true)
await _for(6.0)
check(got.back() == "night", "tips on: the night tip")
world.day_night.time = 10.0 / 24.0
world.day_night.apply(true)
print("events")
H.event("howl")
await _for(17.0)
check(got.back() == "howl", "the wolves howl: the wolf tip")
world.dead_waiting = true
H.event("died")
await _for(25.0)
check(got.back() == "howl", "dead, on the death screen: no tip yet")
world.dead_waiting = false
await _for(1.0)
check(got.back() == "died", "back on your feet: the death tip")
player.inventory.add("rotten_food", 1)
await _for(17.0)
check(got.back() == "rotten", "something rots in your bag: the food tip")
player.inventory.remove("rotten_food", 1)
print("next time")
H.load_prefs()
check(H.seen.size() == 6 and H.seen.has("died"), "the seen tips come back from the file (%d)" % H.seen.size())
await _load(false)
got.clear()
sv.wet = 60.0
sv.hunger = 10.0
await _for(25.0)
check(got.is_empty(), "a new session: wet and hungry again, but they've been said")
H.reset_seen()
await _for(5.0)
check(got == ["wet"], "Show again: the wet tip comes back")
sv.wet = 0.0
sv.hunger = 90.0
print("not in the tutorial")
await _load(true)
check(not H.active, "the tutorial world has its own guide: no tips")
got.clear()
sv.wet = 60.0
await _for(10.0)
check(got.is_empty(), "(soaked there: nothing)")
card.queue_free()
world.queue_free()
await process_frame
_finished = true