T4: Old Tam's tips in normal worlds, with a setting
This commit is contained in:
@@ -9,7 +9,7 @@ if [ ! -f .godot/global_script_class_cache.cfg ]; then
|
||||
echo "Importing the project first (fresh copy)..."
|
||||
godot --headless --path . --import >/dev/null 2>&1
|
||||
fi
|
||||
for t in test_survival_core test_survival_stats test_save_load test_weather_building test_creatures_combat test_wolves test_alpha test_bear test_thieves test_barrow test_boss test_den_sites test_fishing_diet test_food test_hazards test_iron test_stone test_net_lobby test_net_world test_net_shared test_net_life test_net_boss test_net_rules test_host test_tutorial test_tutorial_full test_terrain_edges test_boundary; do
|
||||
for t in test_survival_core test_survival_stats test_save_load test_weather_building test_creatures_combat test_wolves test_alpha test_bear test_thieves test_barrow test_boss test_den_sites test_fishing_diet test_food test_hazards test_iron test_stone test_net_lobby test_net_world test_net_shared test_net_life test_net_boss test_net_rules test_host test_tutorial test_tutorial_full test_hints test_terrain_edges test_boundary; do
|
||||
extra=""
|
||||
[ "$t" = "test_boundary" ] && extra="--fixed-fps 30"
|
||||
# A suite that waits on a world that failed to load would hang forever,
|
||||
|
||||
168
tests/test_hints.gd
Normal file
168
tests/test_hints.gd
Normal file
@@ -0,0 +1,168 @@
|
||||
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
|
||||
Reference in New Issue
Block a user