169 lines
5.3 KiB
GDScript
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"]
|
|
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
|