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