Files
Emberwild/tests/test_map.gd

140 lines
5.7 KiB
GDScript

extends SceneTree
## The map and compass (Gitea issue #1). The map starts blank and fills in
## around you as you go; places are marked once you've seen them (the den,
## the barrow, the stones, the tunnel's seam, the Cove's pier and sea cave,
## your beds and buildings); you're a dot, and the compass says which way
## you're facing. What you've seen is saved with each worldlet.
## Run: godot --headless --path . --script res://tests/test_map.gd
var _fails := 0
var _finished := false
var S: Node
var DB: Node
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:
S = root.get_node("Saves")
DB = root.get_node("ItemDB")
S.root_dir = "user://test_map_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)
print("RESULT: %s (%d failures)" % ["PASS" if _fails == 0 else "FAIL", _fails])
quit(1 if _fails > 0 else 0)
func _load_world() -> Node3D:
var w: Node3D = load("res://scenes/world.tscn").instantiate()
root.add_child(w)
for _i in range(8):
await physics_frame
w.creatures.enabled = false
w.player.set_physics_process(false)
return w
func _count(w: Node3D) -> int:
var n := 0
for b in w.explored:
n += 1 if b != 0 else 0
return n
func _body() -> void:
S.current_id = S.create_world("Mapped", 1337)
var w: Node3D = await _load_world()
var player: Node3D = w.player
print("filling in")
await create_timer(0.7).timeout
var start := _count(w)
var all: int = w.MAP_CELLS * w.MAP_CELLS
check(start > 0 and start < all / 8, "at first, only the ground around you (%d of %d cells)" % [start, all])
check(w.seen(player.global_position.x, player.global_position.z), "(including where you stand)")
check(w.den.found and not w.seen(w.den.global_position.x, w.den.global_position.z), "the den isn't on it yet")
check(not w.map_marks().any(func(m): return m.kind == "den"), "(nor marked)")
player.global_position = w.den.global_position + Vector3(6, 1, 0)
w.explore(player.global_position)
check(w.map_marks().any(func(m): return m.kind == "den" and m.label == "Wolf den"), "go near it and it's marked")
check(_count(w) > start, "and the map's filled in on the way")
w.explore(w.tunnel.seam_at)
w.explore(w.barrow.global_position)
w.explore(w.stones.global_position)
var kinds: Array = w.map_marks().map(func(m): return m.kind)
check("seam" in kinds and "barrow" in kinds and "stones" in kinds, "the cracked seam, the barrow and the stones, once seen (%s)" % [kinds])
var bed: Node3D = w.spawn_piece(DB.get_item("bed"), Transform3D(Basis(), w._spawn + Vector3(4, 0, 2)))
var floor_def: Dictionary = DB.get_item("wood_floor")
if not floor_def.is_empty():
w.spawn_piece(floor_def, Transform3D(Basis(), w._spawn + Vector3(-4, 0, 2)))
w.explore(w._spawn)
kinds = w.map_marks().map(func(m): return m.kind)
check("bed" in kinds, "your bed")
check(floor_def.is_empty() or "build" in kinds, "and your buildings")
var below: Vector3 = w.tunnel.ORIGIN
var before := _count(w)
w.explore(below)
check(_count(w) == before, "(nothing fills in down the tunnel)")
print("the screen")
var MS: Script = load("res://scripts/ui/map_screen.gd")
var img: Image = MS.render(w)
check(img.get_width() == MS.RES, "the map image (%d px)" % img.get_width())
var far := Vector2i(2, 2)
var near: Vector2i = Vector2i(((w._spawn.x + w.terrain.size * 0.5) / w.terrain.size * MS.RES), ((w._spawn.z + w.terrain.size * 0.5) / w.terrain.size * MS.RES))
check(img.get_pixelv(far).is_equal_approx(img.get_pixelv(Vector2i(4, 2))) or img.get_pixelv(far).v < 0.25, "blank where you haven't been")
check(img.get_pixelv(near).v > 0.25, "drawn where you have")
var lake: Vector2 = w.terrain.lake_center
check(MS.ground_colour(w.terrain, lake.x, lake.y).b > MS.ground_colour(w.terrain, lake.x, lake.y).g, "water is blue")
var screen: Control = MS.new()
root.add_child(screen)
screen.setup(w, player)
screen.open()
await process_frame
await process_frame
check(screen.is_open() and player.ui_open and screen._title.text == "The Valley", "N opens it: the Valley")
check(screen._facing.text.begins_with("Facing "), "with which way you face (%s)" % screen._facing.text)
screen.close()
check(not screen.is_open() and not player.ui_open, "and closes it")
screen.queue_free()
print("the compass")
var C: Script = load("res://scripts/ui/compass.gd")
player.rotation = Vector3.ZERO # (facing -Z)
await process_frame
check(absf(C.heading(player)) < 1.0 or absf(C.heading(player) - 360.0) < 1.0, "facing -Z is north (%.0f)" % C.heading(player))
player.rotation = Vector3(0, -PI * 0.5, 0) # (facing +X)
await process_frame
check(absf(C.heading(player) - 90.0) < 1.0, "facing +X is east (%.0f)" % C.heading(player))
print("saved")
var seen_n := _count(w)
w.save_now()
w.queue_free()
for _i in range(3):
await process_frame
w = await _load_world()
check(_count(w) >= seen_n, "what you've seen is still on the map after a reload (%d cells)" % _count(w))
check(w.map_marks().any(func(m): return m.kind == "den"), "(the den too)")
w.queue_free()
for _i in range(3):
await process_frame
print("the Cove")
S.current_id = S.create_world("Cove map", 1337, {"worldlet": "cove"})
w = await _load_world()
await create_timer(0.7).timeout
check(_count(w) < start * 2, "a map of its own: only the ground round the start so far (%d cells)" % _count(w))
w.explore(w.pier.end)
w.explore(w.sea_cave.global_position)
kinds = w.map_marks().map(func(m): return m.kind)
check("pier" in kinds and "cave" in kinds, "the Cove's map: the pier and the sea cave (%s)" % [kinds])
w.queue_free()
await process_frame
_finished = true