diff --git a/data/worldlets/valley.json b/data/worldlets/valley.json index 53c01ff..a951f03 100644 --- a/data/worldlets/valley.json +++ b/data/worldlets/valley.json @@ -1,8 +1,9 @@ { - "_doc": "A worldlet (docs/WORLDLETS_DESIGN.md): its size, and its terrain as building blocks applied in order (scripts/world/terrain.gd: hills, rim, sea_cliff, lake, ocean_edge). The numbers here are the Valley exactly as it was hand-coded: tests/test_worldlets.gd checks it still makes the same height grid.", + "_doc": "A worldlet (docs/WORLDLETS_DESIGN.md): its name and description (the New World page), whether a new world can start here (\"start\"; the others are reached on the journey), its size, and its terrain as building blocks applied in order (scripts/world/terrain.gd: hills, rim, sea_cliff, lake, ocean_edge). The numbers here are the Valley exactly as it was hand-coded: tests/test_worldlets.gd checks it still makes the same height grid.", "id": "valley", "name": "Valley", "description": "A sheltered valley ringed by mountains, with a lake in the middle and sea cliffs on one side.", + "start": true, "size": 320, "resolution": 256, "terrain": [ diff --git a/scripts/building/bed.gd.uid b/scripts/building/bed.gd.uid new file mode 100644 index 0000000..f0f7abe --- /dev/null +++ b/scripts/building/bed.gd.uid @@ -0,0 +1 @@ +uid://nx08dmty60ny diff --git a/scripts/building/build_piece.gd.uid b/scripts/building/build_piece.gd.uid new file mode 100644 index 0000000..ca44548 --- /dev/null +++ b/scripts/building/build_piece.gd.uid @@ -0,0 +1 @@ +uid://dsbvhwas6wsxf diff --git a/scripts/building/build_system.gd.uid b/scripts/building/build_system.gd.uid new file mode 100644 index 0000000..3ce8558 --- /dev/null +++ b/scripts/building/build_system.gd.uid @@ -0,0 +1 @@ +uid://gj02g6p3xfip diff --git a/scripts/building/door.gd.uid b/scripts/building/door.gd.uid new file mode 100644 index 0000000..5a8aa8e --- /dev/null +++ b/scripts/building/door.gd.uid @@ -0,0 +1 @@ +uid://dhsyx4mrm28oe diff --git a/scripts/building/forge.gd.uid b/scripts/building/forge.gd.uid new file mode 100644 index 0000000..d8a4cef --- /dev/null +++ b/scripts/building/forge.gd.uid @@ -0,0 +1 @@ +uid://cl5ip4achfdwo diff --git a/scripts/building/hearth.gd.uid b/scripts/building/hearth.gd.uid new file mode 100644 index 0000000..b23a08f --- /dev/null +++ b/scripts/building/hearth.gd.uid @@ -0,0 +1 @@ +uid://mjs6qb5p3q1a diff --git a/scripts/building/jar.gd.uid b/scripts/building/jar.gd.uid new file mode 100644 index 0000000..7dd9c68 --- /dev/null +++ b/scripts/building/jar.gd.uid @@ -0,0 +1 @@ +uid://ciqsy8msggcke diff --git a/scripts/building/lamp.gd.uid b/scripts/building/lamp.gd.uid new file mode 100644 index 0000000..abb45ae --- /dev/null +++ b/scripts/building/lamp.gd.uid @@ -0,0 +1 @@ +uid://cwvlv0fbxhlgj diff --git a/scripts/building/processor.gd.uid b/scripts/building/processor.gd.uid new file mode 100644 index 0000000..bf0051f --- /dev/null +++ b/scripts/building/processor.gd.uid @@ -0,0 +1 @@ +uid://jvcfwahbmdsm diff --git a/scripts/building/rug.gd.uid b/scripts/building/rug.gd.uid new file mode 100644 index 0000000..bad5920 --- /dev/null +++ b/scripts/building/rug.gd.uid @@ -0,0 +1 @@ +uid://d21if6sepl888 diff --git a/scripts/building/stonecutter.gd.uid b/scripts/building/stonecutter.gd.uid new file mode 100644 index 0000000..5937918 --- /dev/null +++ b/scripts/building/stonecutter.gd.uid @@ -0,0 +1 @@ +uid://dk1ge2xxgiev1 diff --git a/scripts/core/boot.gd.uid b/scripts/core/boot.gd.uid new file mode 100644 index 0000000..4cca75a --- /dev/null +++ b/scripts/core/boot.gd.uid @@ -0,0 +1 @@ +uid://frm15unnr4w5 diff --git a/scripts/core/guide.gd.uid b/scripts/core/guide.gd.uid new file mode 100644 index 0000000..6dfdc05 --- /dev/null +++ b/scripts/core/guide.gd.uid @@ -0,0 +1 @@ +uid://nva2x7o0ggw1 diff --git a/scripts/core/hints.gd.uid b/scripts/core/hints.gd.uid new file mode 100644 index 0000000..5c049fc --- /dev/null +++ b/scripts/core/hints.gd.uid @@ -0,0 +1 @@ +uid://cd4pyfn6igpx0 diff --git a/scripts/core/music.gd.uid b/scripts/core/music.gd.uid new file mode 100644 index 0000000..58334d6 --- /dev/null +++ b/scripts/core/music.gd.uid @@ -0,0 +1 @@ +uid://jojnurcejyff diff --git a/scripts/core/narrator.gd.uid b/scripts/core/narrator.gd.uid new file mode 100644 index 0000000..17a8759 --- /dev/null +++ b/scripts/core/narrator.gd.uid @@ -0,0 +1 @@ +uid://ctvohlyg6yr5m diff --git a/scripts/core/sfx.gd.uid b/scripts/core/sfx.gd.uid new file mode 100644 index 0000000..059cb0d --- /dev/null +++ b/scripts/core/sfx.gd.uid @@ -0,0 +1 @@ +uid://br583euuk842q diff --git a/scripts/core/updates.gd.uid b/scripts/core/updates.gd.uid new file mode 100644 index 0000000..c6767c1 --- /dev/null +++ b/scripts/core/updates.gd.uid @@ -0,0 +1 @@ +uid://3lflq11qfg00 diff --git a/scripts/creatures/bear.gd.uid b/scripts/creatures/bear.gd.uid new file mode 100644 index 0000000..9a34718 --- /dev/null +++ b/scripts/creatures/bear.gd.uid @@ -0,0 +1 @@ +uid://co3yk3efstys2 diff --git a/scripts/creatures/bear_range.gd.uid b/scripts/creatures/bear_range.gd.uid new file mode 100644 index 0000000..f7db0d5 --- /dev/null +++ b/scripts/creatures/bear_range.gd.uid @@ -0,0 +1 @@ +uid://ce34amx3oagyx diff --git a/scripts/creatures/creature.gd.uid b/scripts/creatures/creature.gd.uid new file mode 100644 index 0000000..45f06cb --- /dev/null +++ b/scripts/creatures/creature.gd.uid @@ -0,0 +1 @@ +uid://cdxggdwnq1ctn diff --git a/scripts/creatures/creature_meshes.gd.uid b/scripts/creatures/creature_meshes.gd.uid new file mode 100644 index 0000000..3b44cf9 --- /dev/null +++ b/scripts/creatures/creature_meshes.gd.uid @@ -0,0 +1 @@ +uid://dy6utmjegblry diff --git a/scripts/creatures/crow.gd.uid b/scripts/creatures/crow.gd.uid new file mode 100644 index 0000000..da7cf7c --- /dev/null +++ b/scripts/creatures/crow.gd.uid @@ -0,0 +1 @@ +uid://bs230fcuvorft diff --git a/scripts/creatures/den.gd.uid b/scripts/creatures/den.gd.uid new file mode 100644 index 0000000..529157d --- /dev/null +++ b/scripts/creatures/den.gd.uid @@ -0,0 +1 @@ +uid://beng43qyj6rke diff --git a/scripts/creatures/fox.gd.uid b/scripts/creatures/fox.gd.uid new file mode 100644 index 0000000..35faf79 --- /dev/null +++ b/scripts/creatures/fox.gd.uid @@ -0,0 +1 @@ +uid://cfailgwvqv2fc diff --git a/scripts/creatures/guard.gd.uid b/scripts/creatures/guard.gd.uid new file mode 100644 index 0000000..ab42e01 --- /dev/null +++ b/scripts/creatures/guard.gd.uid @@ -0,0 +1 @@ +uid://x0xva7tw6ind diff --git a/scripts/creatures/old_stag.gd.uid b/scripts/creatures/old_stag.gd.uid new file mode 100644 index 0000000..e1f1f9e --- /dev/null +++ b/scripts/creatures/old_stag.gd.uid @@ -0,0 +1 @@ +uid://f88v5i7s5e5c diff --git a/scripts/creatures/projectile.gd.uid b/scripts/creatures/projectile.gd.uid new file mode 100644 index 0000000..0ecd8db --- /dev/null +++ b/scripts/creatures/projectile.gd.uid @@ -0,0 +1 @@ +uid://ccpmq0ddkoc8g diff --git a/scripts/creatures/spawner.gd.uid b/scripts/creatures/spawner.gd.uid new file mode 100644 index 0000000..626ae2c --- /dev/null +++ b/scripts/creatures/spawner.gd.uid @@ -0,0 +1 @@ +uid://dukt8gme03lsb diff --git a/scripts/creatures/thieves.gd.uid b/scripts/creatures/thieves.gd.uid new file mode 100644 index 0000000..707e9d2 --- /dev/null +++ b/scripts/creatures/thieves.gd.uid @@ -0,0 +1 @@ +uid://cpwa4sffdi3nt diff --git a/scripts/creatures/tutorial_wolf.gd.uid b/scripts/creatures/tutorial_wolf.gd.uid new file mode 100644 index 0000000..de15044 --- /dev/null +++ b/scripts/creatures/tutorial_wolf.gd.uid @@ -0,0 +1 @@ +uid://b8wfmfnhg2edq diff --git a/scripts/creatures/wolf.gd.uid b/scripts/creatures/wolf.gd.uid new file mode 100644 index 0000000..33d00bd --- /dev/null +++ b/scripts/creatures/wolf.gd.uid @@ -0,0 +1 @@ +uid://024u453fdbml diff --git a/scripts/creatures/wolf_pack.gd.uid b/scripts/creatures/wolf_pack.gd.uid new file mode 100644 index 0000000..ab396f4 --- /dev/null +++ b/scripts/creatures/wolf_pack.gd.uid @@ -0,0 +1 @@ +uid://cbb3g7cjprhy8 diff --git a/scripts/fishing/bobber.gd.uid b/scripts/fishing/bobber.gd.uid new file mode 100644 index 0000000..c9337ed --- /dev/null +++ b/scripts/fishing/bobber.gd.uid @@ -0,0 +1 @@ +uid://br7isyu6hxnsd diff --git a/scripts/fishing/lake_fish.gd.uid b/scripts/fishing/lake_fish.gd.uid new file mode 100644 index 0000000..9f35b54 --- /dev/null +++ b/scripts/fishing/lake_fish.gd.uid @@ -0,0 +1 @@ +uid://cs6ucd2nldqmj diff --git a/scripts/net/avatar.gd.uid b/scripts/net/avatar.gd.uid new file mode 100644 index 0000000..d64f5fd --- /dev/null +++ b/scripts/net/avatar.gd.uid @@ -0,0 +1 @@ +uid://bccgr18dkfhad diff --git a/scripts/net/character_mesh.gd.uid b/scripts/net/character_mesh.gd.uid new file mode 100644 index 0000000..1dbc413 --- /dev/null +++ b/scripts/net/character_mesh.gd.uid @@ -0,0 +1 @@ +uid://w2qc4a4ai4vt diff --git a/scripts/net/chat.gd.uid b/scripts/net/chat.gd.uid new file mode 100644 index 0000000..e14b103 --- /dev/null +++ b/scripts/net/chat.gd.uid @@ -0,0 +1 @@ +uid://cj8ew4d4huen5 diff --git a/scripts/net/creature_net.gd.uid b/scripts/net/creature_net.gd.uid new file mode 100644 index 0000000..45e076b --- /dev/null +++ b/scripts/net/creature_net.gd.uid @@ -0,0 +1 @@ +uid://c3amf3mwhetmf diff --git a/scripts/net/identity.gd.uid b/scripts/net/identity.gd.uid new file mode 100644 index 0000000..1b1727b --- /dev/null +++ b/scripts/net/identity.gd.uid @@ -0,0 +1 @@ +uid://x0plvp658wne diff --git a/scripts/net/net.gd b/scripts/net/net.gd index 66cef13..a6a3c4b 100644 --- a/scripts/net/net.gd +++ b/scripts/net/net.gd @@ -304,14 +304,14 @@ var _host_out := "" var _host_port := DEFAULT_PORT ## Settings for a hosted game: name, password, port, a rules preset -## (co-op / rivals / adversarial) and the seed for a new island. +## (co-op / rivals / adversarial) and the seed and worldlet for a new island. const PRESETS := { "co-op": {"pvp": "false", "raiding": "false", "locks": "owner_and_friends", "death_penalty": "grave"}, "rivals": {"pvp": "true", "raiding": "false", "locks": "owner_and_friends", "death_penalty": "grave"}, "adversarial": {"pvp": "true", "raiding": "true", "locks": "owner", "death_penalty": "drop_all"}, } -func host(server_name: String, password: String, port: int, preset: String, seed: int) -> void: +func host(server_name: String, password: String, port: int, preset: String, seed: int, worldlet: String = "valley") -> void: if hosting or state != "offline": return if _host_pid != 0 and OS.is_process_running(_host_pid): @@ -324,8 +324,9 @@ func host(server_name: String, password: String, port: int, preset: String, seed c.data.password = password c.data.port = port var fresh := not FileAccess.file_exists(host_config.get_base_dir().path_join("worlds/server-world/meta.json")) - if fresh: + if fresh: # (an island keeps its seed and worldlet once it's made) c.data.seed = seed + c.data.worldlet = worldlet c.save() _host_port = port var args := PackedStringArray(["--headless"]) diff --git a/scripts/net/net.gd.uid b/scripts/net/net.gd.uid new file mode 100644 index 0000000..3704ec9 --- /dev/null +++ b/scripts/net/net.gd.uid @@ -0,0 +1 @@ +uid://d1153ihwyw01t diff --git a/scripts/net/net_sync.gd.uid b/scripts/net/net_sync.gd.uid new file mode 100644 index 0000000..9db02d9 --- /dev/null +++ b/scripts/net/net_sync.gd.uid @@ -0,0 +1 @@ +uid://bgyhqtmwppt4g diff --git a/scripts/net/player_net.gd.uid b/scripts/net/player_net.gd.uid new file mode 100644 index 0000000..a99bdae --- /dev/null +++ b/scripts/net/player_net.gd.uid @@ -0,0 +1 @@ +uid://dim4cfgbtabvf diff --git a/scripts/net/rules.gd.uid b/scripts/net/rules.gd.uid new file mode 100644 index 0000000..984cc68 --- /dev/null +++ b/scripts/net/rules.gd.uid @@ -0,0 +1 @@ +uid://d2fotty6gceeh diff --git a/scripts/net/server_config.gd.uid b/scripts/net/server_config.gd.uid new file mode 100644 index 0000000..6a69170 --- /dev/null +++ b/scripts/net/server_config.gd.uid @@ -0,0 +1 @@ +uid://cykv4lse4g62a diff --git a/scripts/net/server_player.gd.uid b/scripts/net/server_player.gd.uid new file mode 100644 index 0000000..e7ea769 --- /dev/null +++ b/scripts/net/server_player.gd.uid @@ -0,0 +1 @@ +uid://dkdnvh8f18o2x diff --git a/scripts/net/world_net.gd.uid b/scripts/net/world_net.gd.uid new file mode 100644 index 0000000..594ab28 --- /dev/null +++ b/scripts/net/world_net.gd.uid @@ -0,0 +1 @@ +uid://cmk7ho2ij6esq diff --git a/scripts/ui/guide.gd.uid b/scripts/ui/guide.gd.uid new file mode 100644 index 0000000..96f851f --- /dev/null +++ b/scripts/ui/guide.gd.uid @@ -0,0 +1 @@ +uid://dbijp4r843nyk diff --git a/scripts/ui/hint_card.gd.uid b/scripts/ui/hint_card.gd.uid new file mode 100644 index 0000000..7e8c11b --- /dev/null +++ b/scripts/ui/hint_card.gd.uid @@ -0,0 +1 @@ +uid://0npv7fub184o diff --git a/scripts/ui/hut_outline.gd.uid b/scripts/ui/hut_outline.gd.uid new file mode 100644 index 0000000..f66c291 --- /dev/null +++ b/scripts/ui/hut_outline.gd.uid @@ -0,0 +1 @@ +uid://87b73wsdjp7t diff --git a/scripts/ui/title_screen.gd b/scripts/ui/title_screen.gd index 7487bda..bb83785 100644 --- a/scripts/ui/title_screen.gd +++ b/scripts/ui/title_screen.gd @@ -370,6 +370,7 @@ func _host_page() -> Control: _host_preset.add_item(n) _host_preset.custom_minimum_size = Vector2(420, 36) v.add_child(_host_preset) + _host_worldlet = _worldlet_picker(v) var row := HBoxContainer.new() row.add_theme_constant_override("separation", 10) var c1 := VBoxContainer.new() @@ -424,6 +425,7 @@ func _fill_host() -> void: _host_seed.editable = not existing _host_seed_label.text = "Seed" if not existing else "Seed (kept)" _host_seed.text = "" if not existing else str(int(c.data.seed)) + _show_host_worldlet(existing, meta_path) _host_msg.text = "Your hosted island carries on where you left it." if existing else "A new island will be made." _host_msg.add_theme_color_override("font_color", UITheme.TEXT_DIM) _host_btn.disabled = false @@ -439,7 +441,8 @@ func _start_host() -> void: if name.is_empty(): name = "%s's island" % Net.get_identity().name _host_btn.disabled = true - Net.host(name, _host_pw.text, int(port_t), PRESET_IDS[maxi(_host_preset.selected, 0)], seed) + Net.host(name, _host_pw.text, int(port_t), PRESET_IDS[maxi(_host_preset.selected, 0)], seed, + picked_worldlet(_host_worldlet)) func _on_host_status(text: String, failed: bool) -> void: if not _pages["host"].visible: @@ -591,6 +594,7 @@ func _new_page() -> Control: _rot_pick.add_item(t[1]) _rot_pick.custom_minimum_size = Vector2(360, 36) v.add_child(_rot_pick) + _worldlet_pick = _worldlet_picker(v) var h := HBoxContainer.new() h.add_theme_constant_override("separation", 10) h.add_child(_button("Create & Play", _create, 170)) @@ -633,7 +637,8 @@ func _fill_list() -> void: info.add_child(UITheme.label(w.name, 18)) var played := int(w.get("play_time", 0.0)) var when := Time.get_datetime_string_from_unix_time(int(w.get("last_played", 0)), true) - info.add_child(UITheme.label("seed %d · played %dh %02dm · %s" % [int(w.seed), played / 3600, (played / 60) % 60, when], 13, UITheme.TEXT_DIM)) + info.add_child(UITheme.label("%s · seed %d · played %dh %02dm · %s" % [Terrain.worldlet_name(String(w.get("worldlet", "valley"))), + int(w.seed), played / 3600, (played / 60) % 60, when], 13, UITheme.TEXT_DIM)) h.add_child(info) h.add_child(_button("Play", _play.bind(w.id), 80)) var del := Button.new() @@ -681,7 +686,68 @@ func _create() -> void: var seed_text := _seed_edit.text.strip_edges() var seed := int(seed_text) if seed_text.is_valid_int() else (seed_text.hash() if not seed_text.is_empty() else randi()) var rot: String = ROT_CHOICES[maxi(_rot_pick.selected, 0)][0] - _play(Saves.create_world(world_name, seed, {"rot": rot})) + _play(Saves.create_world(world_name, seed, {"rot": rot, "worldlet": picked_worldlet(_worldlet_pick)})) + +# --- worldlets (docs/WORLDLETS_DESIGN.md, W1) ----------------------------------------- + +const Terrain = preload("res://scripts/world/terrain.gd") +var _worldlet_pick: OptionButton +var _host_worldlet: OptionButton + +## "Where": the worldlets a new world can start in, with the chosen one's +## description under it. (Only the Valley today: the others are reached on +## the journey, so the list is short and the picker sits still.) +func _worldlet_picker(v: VBoxContainer) -> OptionButton: + var starts: Array = Terrain.starting_worldlets() + v.add_child(UITheme.label("Where", 15, UITheme.TEXT_DIM)) + var pick := OptionButton.new() + pick.custom_minimum_size = Vector2(360, 36) + pick.focus_mode = Control.FOCUS_NONE + for w in starts: + pick.add_item(String(w.name)) + pick.set_item_metadata(pick.item_count - 1, String(w.id)) + pick.disabled = starts.size() < 2 + v.add_child(pick) + var about := UITheme.label(String(starts[0].description) if not starts.is_empty() else "", 13, UITheme.TEXT_DIM) + about.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + about.custom_minimum_size = Vector2(360, 0) + v.add_child(about) + pick.item_selected.connect(func(i: int): + about.text = String(Terrain.load_worldlet(String(pick.get_item_metadata(i))).get("description", ""))) + return pick + +## The Host page's "Where": a hosted island keeps its worldlet, like its +## seed, so once it exists the picker shows that one and stays put. +func _show_host_worldlet(existing: bool, meta_path: String) -> void: + var id := "valley" + if existing: + var meta: Variant = JSON.parse_string(FileAccess.get_file_as_string(meta_path)) + if meta is Dictionary: + id = String(meta.get("worldlet", "valley")) + var i := select_worldlet(_host_worldlet, id) + if i >= 0: + _host_worldlet.item_selected.emit(i) + _host_worldlet.disabled = existing or _host_worldlet.item_count < 2 + +## Selects worldlet `id` in `pick`, adding it if it isn't a starting one +## (an island made somewhere else). Returns its index, or -1. +static func select_worldlet(pick: OptionButton, id: String) -> int: + for i in pick.item_count: + if String(pick.get_item_metadata(i)) == id: + pick.select(i) + return i + if Terrain.load_worldlet(id).is_empty(): + return -1 + pick.add_item(Terrain.worldlet_name(id)) + pick.set_item_metadata(pick.item_count - 1, id) + pick.select(pick.item_count - 1) + return pick.item_count - 1 + +## The id of the worldlet chosen in `pick` (the Valley if nothing is). +static func picked_worldlet(pick: OptionButton) -> String: + if pick == null or pick.item_count == 0 or pick.selected < 0: + return "valley" + return String(pick.get_item_metadata(pick.selected)) ## The tutorial (docs/TUTORIAL_DESIGN.md): its own world on the original ## island (seed 1337), made the first time and carried on after that. diff --git a/scripts/world/barrow.gd.uid b/scripts/world/barrow.gd.uid new file mode 100644 index 0000000..2a740e9 --- /dev/null +++ b/scripts/world/barrow.gd.uid @@ -0,0 +1 @@ +uid://5vs0iggnxpuc diff --git a/scripts/world/hazards.gd.uid b/scripts/world/hazards.gd.uid new file mode 100644 index 0000000..9926faf --- /dev/null +++ b/scripts/world/hazards.gd.uid @@ -0,0 +1 @@ +uid://bmcefncielej3 diff --git a/scripts/world/standing_stones.gd.uid b/scripts/world/standing_stones.gd.uid new file mode 100644 index 0000000..28f3d32 --- /dev/null +++ b/scripts/world/standing_stones.gd.uid @@ -0,0 +1 @@ +uid://c4dyxek30amhh diff --git a/scripts/world/terrain.gd b/scripts/world/terrain.gd index 348a849..91b2693 100644 --- a/scripts/world/terrain.gd +++ b/scripts/world/terrain.gd @@ -169,9 +169,20 @@ static func worldlets() -> Array: if f.ends_with(".json"): var d := load_worldlet(f.get_basename()) if not d.is_empty(): - out.append({"id": String(d.id), "name": String(d.get("name", d.id)), "description": String(d.get("description", ""))}) + out.append({"id": String(d.id), "name": String(d.get("name", d.id)), "description": String(d.get("description", "")), + "start": bool(d.get("start", false))}) + out.sort_custom(func(a, b): return a.id == "valley" or (b.id != "valley" and a.name < b.name)) return out +## The worldlets a new world can start in (the rest are reached on the journey). +static func starting_worldlets() -> Array: + return worldlets().filter(func(w): return w.start) + +## A worldlet's display name ("Valley"), or its id if there's no such worldlet. +static func worldlet_name(id: String) -> String: + var d := load_worldlet(id) + return String(d.get("name", id)) if not d.is_empty() else id + ## Takes on worldlet `id`: its size and every block's numbers. Returns "" ## on success, else what's wrong (an unknown worldlet or block keeps the old ## settings, so the game still has an island). diff --git a/scripts/world/weather.gd.uid b/scripts/world/weather.gd.uid new file mode 100644 index 0000000..68186c4 --- /dev/null +++ b/scripts/world/weather.gd.uid @@ -0,0 +1 @@ +uid://b5x4pld4ecyr4 diff --git a/scripts/world/weather_fx.gd.uid b/scripts/world/weather_fx.gd.uid new file mode 100644 index 0000000..ae7cf72 --- /dev/null +++ b/scripts/world/weather_fx.gd.uid @@ -0,0 +1 @@ +uid://ch20dfnan2yed diff --git a/tests/net_bot.gd.uid b/tests/net_bot.gd.uid new file mode 100644 index 0000000..fc09af5 --- /dev/null +++ b/tests/net_bot.gd.uid @@ -0,0 +1 @@ +uid://coc65tapbhoan diff --git a/tests/net_world_bot.gd.uid b/tests/net_world_bot.gd.uid new file mode 100644 index 0000000..6635b4a --- /dev/null +++ b/tests/net_world_bot.gd.uid @@ -0,0 +1 @@ +uid://c770mv6jl6dbb diff --git a/tests/run_all.sh b/tests/run_all.sh index 78efe4a..3284a07 100755 --- a/tests/run_all.sh +++ b/tests/run_all.sh @@ -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_worldlets 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_worldlet_pick 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, diff --git a/tests/test_alpha.gd.uid b/tests/test_alpha.gd.uid new file mode 100644 index 0000000..c3f9aa2 --- /dev/null +++ b/tests/test_alpha.gd.uid @@ -0,0 +1 @@ +uid://ck4x0gw0w6q54 diff --git a/tests/test_audio.gd.uid b/tests/test_audio.gd.uid new file mode 100644 index 0000000..6fbb038 --- /dev/null +++ b/tests/test_audio.gd.uid @@ -0,0 +1 @@ +uid://dkwpi1kjp11us diff --git a/tests/test_barrow.gd.uid b/tests/test_barrow.gd.uid new file mode 100644 index 0000000..00634d6 --- /dev/null +++ b/tests/test_barrow.gd.uid @@ -0,0 +1 @@ +uid://bveecfelviv2q diff --git a/tests/test_bear.gd.uid b/tests/test_bear.gd.uid new file mode 100644 index 0000000..61004e9 --- /dev/null +++ b/tests/test_bear.gd.uid @@ -0,0 +1 @@ +uid://bwv2gjno475ns diff --git a/tests/test_boss.gd.uid b/tests/test_boss.gd.uid new file mode 100644 index 0000000..0d8dfb4 --- /dev/null +++ b/tests/test_boss.gd.uid @@ -0,0 +1 @@ +uid://emp4g3jo3n6u diff --git a/tests/test_creatures_combat.gd.uid b/tests/test_creatures_combat.gd.uid new file mode 100644 index 0000000..65b02df --- /dev/null +++ b/tests/test_creatures_combat.gd.uid @@ -0,0 +1 @@ +uid://b181ahffmk3oy diff --git a/tests/test_den_sites.gd.uid b/tests/test_den_sites.gd.uid new file mode 100644 index 0000000..e7cbc4d --- /dev/null +++ b/tests/test_den_sites.gd.uid @@ -0,0 +1 @@ +uid://dpmqwgnwum26x diff --git a/tests/test_fishing_diet.gd.uid b/tests/test_fishing_diet.gd.uid new file mode 100644 index 0000000..a4e95f8 --- /dev/null +++ b/tests/test_fishing_diet.gd.uid @@ -0,0 +1 @@ +uid://dlm83u70hui08 diff --git a/tests/test_food.gd.uid b/tests/test_food.gd.uid new file mode 100644 index 0000000..9018942 --- /dev/null +++ b/tests/test_food.gd.uid @@ -0,0 +1 @@ +uid://ba0oxbbweqnos diff --git a/tests/test_hazards.gd.uid b/tests/test_hazards.gd.uid new file mode 100644 index 0000000..2bf9c24 --- /dev/null +++ b/tests/test_hazards.gd.uid @@ -0,0 +1 @@ +uid://b5jjbmkuhcyu2 diff --git a/tests/test_hints.gd.uid b/tests/test_hints.gd.uid new file mode 100644 index 0000000..ac5b98d --- /dev/null +++ b/tests/test_hints.gd.uid @@ -0,0 +1 @@ +uid://c2sl304vqgqjm diff --git a/tests/test_host.gd b/tests/test_host.gd index 2ef8509..c095d56 100644 --- a/tests/test_host.gd +++ b/tests/test_host.gd @@ -46,7 +46,7 @@ func _body() -> void: var statuses: Array[String] = [] Net.host_status.connect(func(t, _f): statuses.append(t)) print("hosting") - Net.host("Alice's island", "", PORT, "rivals", 4242) + Net.host("Alice's island", "", PORT, "rivals", 4242, "valley") check(Net.hosting, "Host Game starts a server (%s)" % ", ".join(statuses)) await _until(func(): return Net.state == "connected", 40.0) check(Net.state == "connected", "and joins it (%s)" % " -> ".join(statuses)) @@ -56,6 +56,7 @@ func _body() -> void: "with the name and the rules picked (friendly rivals: PvP on)") check(bool(Net.players.get(Net.local_peer, {}).get("admin", false)), "and Alice is its admin") check(int(Net.world_info.get("seed", 0)) == 4242, "a new island from the seed given (%d)" % int(Net.world_info.get("seed", 0))) + check(String(Net.world_info.get("worldlet", "")) == "valley", "in the worldlet picked (%s)" % Net.world_info.get("worldlet", "-")) var addrs: Array[String] = Net.my_addresses() check(not addrs.is_empty(), "the addresses friends can use: %s" % ", ".join(addrs)) var log_path := ProjectSettings.globalize_path(DIR + "/server.log") @@ -105,10 +106,12 @@ func _body() -> void: c.data.backups_kept = 3 c.save() statuses.clear() - Net.host("Alice's island", "", PORT, "co-op", 999) + Net.host("Alice's island", "", PORT, "co-op", 999, "cove") await _until(func(): return Net.state == "connected", 40.0) check(Net.state == "connected", "Host Game again: joined (%s)" % " -> ".join(statuses)) check(int(Net.world_info.get("seed", 0)) == 4242, "the same island (seed %d, not the new one typed)" % int(Net.world_info.get("seed", 0))) + check(String(load("res://scripts/net/server_config.gd").open(DIR + "/server.json").data.worldlet) == "valley", + "and the same worldlet (the Valley, not the new one picked)") check(not bool(Net.server_info.get("pvp", true)), "with the rules picked this time (co-op: PvP off)") check(_backups().size() == 3, "start-up made a backup, and only backups_kept (3) are kept (%d)" % _backups().size()) _finished = true diff --git a/tests/test_host.gd.uid b/tests/test_host.gd.uid new file mode 100644 index 0000000..95ff596 --- /dev/null +++ b/tests/test_host.gd.uid @@ -0,0 +1 @@ +uid://ci1cmi2muyyhm diff --git a/tests/test_iron.gd.uid b/tests/test_iron.gd.uid new file mode 100644 index 0000000..6ceb0c4 --- /dev/null +++ b/tests/test_iron.gd.uid @@ -0,0 +1 @@ +uid://diaexq7aujgcy diff --git a/tests/test_items_more.gd.uid b/tests/test_items_more.gd.uid new file mode 100644 index 0000000..fbc8cd9 --- /dev/null +++ b/tests/test_items_more.gd.uid @@ -0,0 +1 @@ +uid://c14jw5ojloao8 diff --git a/tests/test_net_boss.gd.uid b/tests/test_net_boss.gd.uid new file mode 100644 index 0000000..d47e9d8 --- /dev/null +++ b/tests/test_net_boss.gd.uid @@ -0,0 +1 @@ +uid://clqu3m2ic4h1y diff --git a/tests/test_net_life.gd.uid b/tests/test_net_life.gd.uid new file mode 100644 index 0000000..1d60d4f --- /dev/null +++ b/tests/test_net_life.gd.uid @@ -0,0 +1 @@ +uid://dxjnvqs81vu4l diff --git a/tests/test_net_lobby.gd.uid b/tests/test_net_lobby.gd.uid new file mode 100644 index 0000000..209a32b --- /dev/null +++ b/tests/test_net_lobby.gd.uid @@ -0,0 +1 @@ +uid://bf4kcu6iapdmv diff --git a/tests/test_net_rules.gd.uid b/tests/test_net_rules.gd.uid new file mode 100644 index 0000000..c8fc7ea --- /dev/null +++ b/tests/test_net_rules.gd.uid @@ -0,0 +1 @@ +uid://cq738mqhslnq6 diff --git a/tests/test_net_shared.gd.uid b/tests/test_net_shared.gd.uid new file mode 100644 index 0000000..478ad02 --- /dev/null +++ b/tests/test_net_shared.gd.uid @@ -0,0 +1 @@ +uid://dypssblmpnt01 diff --git a/tests/test_net_world.gd.uid b/tests/test_net_world.gd.uid new file mode 100644 index 0000000..cc5cbc3 --- /dev/null +++ b/tests/test_net_world.gd.uid @@ -0,0 +1 @@ +uid://bdcf2xkerrlw1 diff --git a/tests/test_stone.gd.uid b/tests/test_stone.gd.uid new file mode 100644 index 0000000..23b66a6 --- /dev/null +++ b/tests/test_stone.gd.uid @@ -0,0 +1 @@ +uid://b2jwpmnhtrv2p diff --git a/tests/test_survival_stats.gd.uid b/tests/test_survival_stats.gd.uid new file mode 100644 index 0000000..9693c2c --- /dev/null +++ b/tests/test_survival_stats.gd.uid @@ -0,0 +1 @@ +uid://rj7p5l0et5vo diff --git a/tests/test_thieves.gd.uid b/tests/test_thieves.gd.uid new file mode 100644 index 0000000..3a20dbc --- /dev/null +++ b/tests/test_thieves.gd.uid @@ -0,0 +1 @@ +uid://dlh2tpy6p4cub diff --git a/tests/test_tutorial.gd.uid b/tests/test_tutorial.gd.uid new file mode 100644 index 0000000..1dd7897 --- /dev/null +++ b/tests/test_tutorial.gd.uid @@ -0,0 +1 @@ +uid://d0kc40mfvtcnx diff --git a/tests/test_tutorial_full.gd.uid b/tests/test_tutorial_full.gd.uid new file mode 100644 index 0000000..7734f13 --- /dev/null +++ b/tests/test_tutorial_full.gd.uid @@ -0,0 +1 @@ +uid://c2cnl3i4arfv0 diff --git a/tests/test_updates.gd.uid b/tests/test_updates.gd.uid new file mode 100644 index 0000000..dd6753f --- /dev/null +++ b/tests/test_updates.gd.uid @@ -0,0 +1 @@ +uid://34ao8ilbht52 diff --git a/tests/test_weather_building.gd.uid b/tests/test_weather_building.gd.uid new file mode 100644 index 0000000..fa831c8 --- /dev/null +++ b/tests/test_weather_building.gd.uid @@ -0,0 +1 @@ +uid://dy1iyx1vqbbrg diff --git a/tests/test_wolves.gd.uid b/tests/test_wolves.gd.uid new file mode 100644 index 0000000..ba08170 --- /dev/null +++ b/tests/test_wolves.gd.uid @@ -0,0 +1 @@ +uid://8mmbdrbtg53y diff --git a/tests/test_worldlet_pick.gd b/tests/test_worldlet_pick.gd new file mode 100644 index 0000000..0a524dc --- /dev/null +++ b/tests/test_worldlet_pick.gd @@ -0,0 +1,110 @@ +extends SceneTree +## W1 (docs/WORLDLETS_DESIGN.md): choosing a worldlet. The New World and Host +## Game pages have a "Where" picker listing the worldlets a new world can +## start in (only the Valley today, so it sits still); a world made from it +## remembers it; the Load list says where each world is; and a hosted island +## keeps its worldlet, like its seed, once it's made. +## Run: godot --headless --path . --script res://tests/test_worldlet_pick.gd + +const DIR := "user://test_worldlet_pick" + +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") + var Net: Node = root.get_node("Net") + S.root_dir = DIR + "/worlds" + _rm_tree(DIR) + DirAccess.make_dir_recursive_absolute(DIR) + Net.host_config = DIR + "/server/server.json" + var me = load("res://scripts/net/identity.gd").open(DIR + "/me") + me.name = "Alice" + Net.identity = me + await _body(S, Net) + check(_finished, "test ran to the end (no script errors)") + _rm_tree(DIR) + print("RESULT: %s (%d failures)" % ["PASS" if _fails == 0 else "FAIL", _fails]) + quit(1 if _fails > 0 else 0) + +func _body(S: Node, Net: Node) -> void: + var T: Script = load("res://scripts/world/terrain.gd") + var TS: Script = load("res://scripts/ui/title_screen.gd") + print("the worldlets to start in") + var starts: Array = T.starting_worldlets() + check(starts.size() == 1 and String(starts[0].id) == "valley", "only the Valley (%s)" % [starts.map(func(w): return w.id)]) + check(T.worldlets().all(func(w): return w.has("start")), "every worldlet says whether it's a start") + check(T.worldlet_name("valley") == "Valley", "the Valley's name, for the menus") + check(T.worldlet_name("nowhere") == "nowhere", "(an unknown one: its id)") + check(TS.picked_worldlet(null) == "valley", "nothing picked: the Valley") + + print("the New World page") + var title: Node = load("res://scenes/title.tscn").instantiate() + root.add_child(title) + for _i in range(3): + await process_frame + var pick: OptionButton = title._worldlet_pick + check(pick != null and pick.item_count == 1 and pick.get_item_text(0) == "Valley", "a \"Where\" picker with the Valley in it") + check(pick.disabled, "which sits still with only one choice") + check(TS.picked_worldlet(pick) == "valley", "and gives the Valley") + var id: String = S.create_world("Pick test", 4242, {"worldlet": TS.picked_worldlet(pick)}) + var meta: Dictionary = S.list_worlds().filter(func(w): return w.id == id)[0] if not id.is_empty() else {} + check(String(meta.get("worldlet", "")) == "valley", "a world made from it remembers the Valley (%s)" % meta.get("worldlet", "-")) + + print("the Load list") + title._show("load") + await process_frame + check(_texts(title._list).any(func(t): return t.begins_with("Valley · seed 4242")), "says where each world is (%s)" % [_texts(title._list)]) + + print("the Host Game page") + var hp: OptionButton = title._host_worldlet + check(hp != null and hp.get_item_text(hp.selected) == "Valley", "a \"Where\" picker too, on the Valley") + title._show("host") + await process_frame + check(hp.disabled and TS.picked_worldlet(hp) == "valley", "a new island: the Valley (the only start)") + # Once the island exists it keeps its worldlet: the picker shows that one. + DirAccess.make_dir_recursive_absolute(DIR + "/server/worlds/server-world") + var f := FileAccess.open(DIR + "/server/worlds/server-world/meta.json", FileAccess.WRITE) + f.store_string(JSON.stringify({"seed": 7, "worldlet": "valley"})) + f.close() + title._show("host") + await process_frame + check(hp.disabled and TS.picked_worldlet(hp) == "valley", "an island already made: shows its worldlet, locked") + check(title._host_seed_label.text == "Seed (kept)", "(like its seed)") + # A worldlet that isn't a start (an island made elsewhere) is still shown. + var extra := OptionButton.new() + extra.add_item("Valley") + extra.set_item_metadata(0, "valley") + check(TS.select_worldlet(extra, "valley") == 0, "select_worldlet finds the Valley") + check(TS.select_worldlet(extra, "nowhere") == -1 and extra.item_count == 1, "and adds nothing for an unknown one") + extra.free() + title.queue_free() + await process_frame + + print("hosting records the worldlet") + var C: Script = load("res://scripts/net/server_config.gd") + check(String(C.open(DIR + "/server/server.json").data.worldlet) == "valley", "server.json's worldlet is the Valley by default") + _finished = true + +func _texts(n: Node) -> Array: + var out: Array = [] + for c in n.find_children("*", "Label", true, false): + out.append(String(c.text)) + return out + +static func _rm_tree(d: String) -> void: + if not DirAccess.dir_exists_absolute(d): + return + for f in DirAccess.get_files_at(d): + DirAccess.remove_absolute(d.path_join(f)) + for s in DirAccess.get_directories_at(d): + _rm_tree(d.path_join(s)) + DirAccess.remove_absolute(d) diff --git a/tests/test_worldlet_pick.gd.uid b/tests/test_worldlet_pick.gd.uid new file mode 100644 index 0000000..cdfbf60 --- /dev/null +++ b/tests/test_worldlet_pick.gd.uid @@ -0,0 +1 @@ +uid://bq1s57q65wp3n diff --git a/tests/test_worldlets.gd.uid b/tests/test_worldlets.gd.uid new file mode 100644 index 0000000..733cf17 --- /dev/null +++ b/tests/test_worldlets.gd.uid @@ -0,0 +1 @@ +uid://s1e6yemcmng2 diff --git a/tools/site/dump_data.gd.uid b/tools/site/dump_data.gd.uid new file mode 100644 index 0000000..6154cab --- /dev/null +++ b/tools/site/dump_data.gd.uid @@ -0,0 +1 @@ +uid://5ok583rm7c4a diff --git a/tools/trailer/trailer.gd.uid b/tools/trailer/trailer.gd.uid new file mode 100644 index 0000000..ff8bf14 --- /dev/null +++ b/tools/trailer/trailer.gd.uid @@ -0,0 +1 @@ +uid://xrbxktkso4gb