W0: terrain from worldlet building blocks (Valley in data/worldlets, identical heights), WorldInfo API, worldlet in saves and servers; the cliff tunnel plan
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_hints test_updates test_audio test_items_more 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_updates test_audio test_items_more test_worldlets 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,
|
||||
|
||||
89
tests/test_worldlets.gd
Normal file
89
tests/test_worldlets.gd
Normal file
@@ -0,0 +1,89 @@
|
||||
extends SceneTree
|
||||
## W0 (docs/WORLDLETS_DESIGN.md): the terrain is built from a worldlet's
|
||||
## blocks (data/worldlets/<id>.json), and the Valley made that way is the same
|
||||
## island, height for height, as the hand-coded one it replaced (the hashes
|
||||
## below were taken from the old code). Also: the world answers the WorldInfo
|
||||
## questions, a new save remembers its worldlet, an old save without one is
|
||||
## the Valley, and a bad worldlet falls back to the Valley.
|
||||
## Run: godot --headless --path . --script res://tests/test_worldlets.gd
|
||||
|
||||
## SHA-256 of the height grid, from the terrain before W0 (commit f4df2bc).
|
||||
const GRID_HASHES := {
|
||||
1337: "c87679a4ed30bff0c4be1caacda735454ad23f690ada28e7179667a9a7d62aa7",
|
||||
777: "2edef348df09bf1f84ed3f5b0921f28fe54b3c827a698fac0a7d50fcdb95a24e",
|
||||
42: "801430858909739fc8cf46694eee35c0cffa86ff1a9ad1ed379f1dfdea4b133c",
|
||||
9001: "7d509d2508395a0e27e93a3384ac1e63323d588ef3cd1262f1e3f0c1c3e3254b",
|
||||
}
|
||||
|
||||
var _fails := 0
|
||||
var _finished := false
|
||||
|
||||
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:
|
||||
var S: Node = root.get_node("Saves")
|
||||
S.root_dir = "user://test_worlds"
|
||||
for w in S.list_worlds():
|
||||
S.delete_world(w.id)
|
||||
await _body(S)
|
||||
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 _grid_hash(t: Node) -> String:
|
||||
var h := HashingContext.new()
|
||||
h.start(HashingContext.HASH_SHA256)
|
||||
h.update(t._heights.to_byte_array())
|
||||
return h.finish().hex_encode()
|
||||
|
||||
func _body(S: Node) -> void:
|
||||
var T: Script = load("res://scripts/world/terrain.gd")
|
||||
print("the Valley, from data")
|
||||
var list: Array = T.worldlets()
|
||||
check(list.any(func(w): return w.id == "valley"), "the Valley is a worldlet (%s)" % [list.map(func(w): return w.id)])
|
||||
var def: Dictionary = T.load_worldlet("valley")
|
||||
check(def.get("terrain", []).size() == 5, "built from 5 blocks: hills, rim, sea cliff, lake, ocean edge")
|
||||
for s in GRID_HASHES:
|
||||
var t: Node3D = T.new()
|
||||
t.noise_seed = s
|
||||
root.add_child(t)
|
||||
await process_frame
|
||||
check(_grid_hash(t) == GRID_HASHES[s], "seed %d: the same island, height for height" % s)
|
||||
t.queue_free()
|
||||
await process_frame
|
||||
var bad: Node3D = T.new()
|
||||
check(not bad.use_worldlet("nowhere").is_empty() and bad.worldlet_id == "valley", "an unknown worldlet is refused (the Valley stays)")
|
||||
bad.free()
|
||||
|
||||
print("in a world")
|
||||
var id: String = S.create_world("W0", 1337, {})
|
||||
check(String(S.read_meta(id).get("worldlet", "")) == "valley", "a new world remembers its worldlet")
|
||||
var meta: Dictionary = S.read_meta(id)
|
||||
meta.erase("worldlet")
|
||||
S._write_json(S._dir(id).path_join("meta.json"), meta)
|
||||
S.current_id = id
|
||||
var world: Node3D = load("res://scenes/world.tscn").instantiate()
|
||||
root.add_child(world)
|
||||
for _i in range(5):
|
||||
await physics_frame
|
||||
check(world.worldlet_id() == "valley", "an old world with no worldlet is the Valley")
|
||||
var water: Array = world.water_bodies()
|
||||
check(water.size() == 2 and water.any(func(w): return w.type == "lake") and water.any(func(w): return w.type == "sea"),
|
||||
"its water: a lake and the sea")
|
||||
var lake: Dictionary = water.filter(func(w): return w.type == "lake")[0]
|
||||
var lp: Vector2 = lake.center
|
||||
check(world.water_at(lp.x, lp.y).get("type", "") == "lake", "(and the lake is where it says)")
|
||||
check(absf(float(world.climate().snowline) - 40.0) < 0.01, "its snowline: 40 m")
|
||||
check(world.crossings().any(func(c): return c.to == "cove" and c.by == "tunnel"), "its way on: a tunnel to the Cove (planned)")
|
||||
world.queue_free()
|
||||
await process_frame
|
||||
_finished = true
|
||||
Reference in New Issue
Block a user