Files
Emberwild/tests/test_items_more.gd

235 lines
9.5 KiB
GDScript

extends SceneTree
## Uses for every material (the clay jar, bedroll, wolf-pelt rug, fang
## necklace, bear cloak, pemmican, and rotten food as bait), dropping items
## (Q), and picking from any bag row with 1-8 (K / M change row).
## Run: godot --headless --path . --script res://tests/test_items_more.gd
var _fails := 0
var _finished := false
var A: Node
var S: Node
var DB: Node
var world: Node3D
var player: CharacterBody3D
var inv: Inventory
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:
A = root.get_node("Actions")
S = root.get_node("Saves")
DB = root.get_node("ItemDB")
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)
print("RESULT: %s (%d failures)" % ["PASS" if _fails == 0 else "FAIL", _fails])
quit(1 if _fails > 0 else 0)
func _ground(p: Vector3) -> Vector3:
return Vector3(p.x, world.terrain.sample_height(p.x, p.z), p.z)
func _place(id: String, at: Vector3) -> Node3D:
return world.spawn_piece(DB.get_item(id), Transform3D(Basis(), _ground(at)))
func _body() -> void:
print("every material has a use")
var items: Dictionary = JSON.parse_string(FileAccess.get_file_as_string("res://data/items.json"))
var recipes: Dictionary = JSON.parse_string(FileAccess.get_file_as_string("res://data/recipes.json"))
var used := {}
for r in recipes.recipes:
for k in r.inputs:
used[k] = true
for i in items.items:
for k in i.get("repair", {}).get("inputs", {}):
used[k] = true
for k in i.get("bow", {}).get("ammo", []):
used[k] = true
if i.has("fuel"):
used[i.id] = true
# (used by a machine or a place rather than a recipe)
for k in ["iron_ore", "iron_nugget", "charcoal", "barrow_key", A.BAIT]:
used[k] = true
var idle: Array = []
for i in items.items:
if i.category == "resource" and not i.get("hidden", false) and not used.has(i.id):
idle.append(i.id)
check(idle.is_empty(), "no material is only good for filling your bag %s" % [idle])
var made := {}
for r in recipes.recipes:
made[r.output] = true
var missing: Array = []
for id in ["clay_jar", "bedroll", "pelt_rug", "fang_necklace", "bear_cloak", "pemmican"]:
if not made.has(id) or DB.get_item(id).is_empty() or DB.mesh_for(id) == null:
missing.append(id)
check(missing.is_empty(), "the six new things each have a recipe and a model %s" % [missing])
S.current_id = S.create_world("Items", 1337, {})
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)
player.survival.set_process(false)
world.day_night.frozen = true
world.weather.set_process(false)
world.weather.force("clear")
world.creatures.enabled = false
if world.wolves != null:
world.wolves.enabled = false
inv = player.inventory
var here := player.global_position
print("the clay jar")
var jar := _place("clay_jar", here + Vector3(3, 0, 0))
var chest := _place("chest", here + Vector3(-3, 0, 0))
check(jar != null and jar.inventory.size() == 6, "a clay jar holds 6 stacks")
jar.inventory.add("raw_meat", 1)
chest.inventory.add("raw_meat", 1)
jar.inventory.spoil()
chest.inventory.spoil()
var keep: float = Inventory.keeps_for("raw_meat")
var c0 := Inventory.clock
for k in range(1, 31): # a day and a half, in steps
Inventory.clock = c0 + keep * 1.5 * k / 30.0
jar.inventory.spoil()
chest.inventory.spoil()
check(chest.inventory.count("rotten_food") == 1, "raw meat in a chest is rotten after 1.5x its keep time")
check(jar.inventory.count("raw_meat") == 1, "in a clay jar it's still good")
var left: float = Inventory.hours_left(jar.inventory.get_slot(0))
# 1.5 keep-times in a jar ages it by half of one: half its life left
# (counted as if it came out now; in the jar that lasts three times as long).
var expect: float = keep * 0.5 / Inventory.day_seconds * 24.0
check(absf(left - expect) < 0.5, "with half its life left (%.0f h, expected %.0f h)" % [left, expect])
Inventory.clock = c0
print("the bedroll")
var roll := _place("bedroll", here + Vector3(0, 0, 6))
var bed := _place("bed", here + Vector3(0, 0, -6))
world.day_night.time = 22.0 / 24.0
world.day_night.apply(true)
check(roll.is_in_group("bed") and roll.open_air(), "a bedroll is a bed you can use outdoors")
check(world.sleep_block_reason(roll).is_empty(), "under the open sky on a clear night: you can sleep")
check(world.sleep_block_reason(bed).contains("roof"), "(a bed out in the open still needs a roof)")
world.weather.force("rain")
for _i in range(3):
world.weather._process(30.0) if world.weather.has_method("_process") else null
world.weather.precip = 1.0
check(not world.sleep_block_reason(roll).is_empty(), "in the rain: not out here")
world.weather.force("clear")
world.weather.precip = 0.0
player.global_position = roll.global_position + Vector3(1.0, 0, 0)
check(A.use_bed(player, roll) and roll.is_spawn(), "E on it: your spawn point")
world.day_night.time = 10.0 / 24.0
world.day_night.apply(true)
print("the fang necklace")
player.survival.health = 100.0
player.survival.damage(10.0, here, "a wolf")
check(absf(player.survival.health - 90.0) < 0.01, "without it a wolf's bite is 10")
inv.add("fang_necklace", 1)
player.survival.health = 100.0
player.survival.damage(10.0, here, "a alpha wolf")
check(absf(player.survival.health - 93.0) < 0.01, "with it in your bag the alpha's is 7")
player.survival.health = 100.0
player.survival.damage(10.0, here, "a boar")
check(absf(player.survival.health - 90.0) < 0.01, "(a boar's isn't softened)")
inv.remove("fang_necklace", 1)
print("the bear cloak, the rug and pemmican")
inv.add("bear_cloak", 1)
var slot := -1
for i in range(inv.size()):
if inv.get_slot(i) != null and inv.get_slot(i).id == "bear_cloak":
slot = i
check(A.wear(player, slot) and player.worn == "bear_cloak", "you can wear the bear cloak")
var warmest := 0.0
for i in items.items:
warmest = maxf(warmest, float(i.get("wear", {}).get("warmth", 0)))
check(float(DB.get_item("bear_cloak").wear.warmth) == warmest, "the warmest cloak there is")
check(_place("pelt_rug", here + Vector3(4, 0, 4)).is_in_group("rug"), "a wolf-pelt rug is a rug (Cozy, like the fur rug)")
player.global_position = here
var fire := _place("campfire", here + Vector3(1.5, 0, 0))
fire.fuel = fire.MAX_FUEL
inv.add("cooked_meat", 2)
inv.add("berries", 3)
inv.add("bear_fat", 1)
check(A.craft(player, "pemmican") and inv.count("pemmican") == 3, "pemmican at the campfire (3 from 2 cooked meat, 3 berries, a lump of fat)")
var d0: Dictionary = player.survival.diet.duplicate()
player.survival.hunger = 30.0
for i in range(inv.size()):
if inv.get_slot(i) != null and inv.get_slot(i).id == "pemmican":
A.eat(player, i)
break
check(player.survival.diet.meat > d0.meat and player.survival.diet.plants > d0.plants, "it counts as meat and greens")
print("bait")
var B: Script = load("res://scripts/fishing/bobber.gd")
var bob: Node3D = B.new()
bob.player = player
bob.world = world
bob.table = {"bite_wait": [10.0, 10.0], "fish": [{"id": "perch", "weight": 1}]}
var plain: float = bob._next_wait()
inv.add("rotten_food", 2)
var baited: float = bob._next_wait()
check(absf(baited - plain * A.BAIT_WAIT) < 0.01 and bob.baited, "rotten food in your bag: bites come sooner (%.0f s, not %.0f s)" % [baited, plain])
bob.free()
print("dropping (Q)")
var dropped0: int = world._dropped.get_child_count()
inv.add("stone", 5)
for i in range(inv.size()):
if inv.get_slot(i) != null and inv.get_slot(i).id == "stone":
slot = i
break
check(A.drop(player, slot, 1) and inv.count("stone") == 4, "shift+Q drops one")
check(A.drop(player, slot) and inv.count("stone") == 0, "Q drops the rest of the stack")
check(world._dropped.get_child_count() == dropped0 + 2, "both lie on the ground in front of you")
var rot0: int = inv.count("rotten_food")
for i in range(inv.size()):
if inv.get_slot(i) != null and inv.get_slot(i).id == "rotten_food":
A.drop(player, i)
check(rot0 > 0 and inv.count("rotten_food") == 0, "and at last you can get rid of rotten food")
inv.add("stone_axe", 1)
for i in range(inv.size()):
if inv.get_slot(i) != null and inv.get_slot(i).id == "stone_axe":
inv.get_slot(i).dur = 7
slot = i
A.drop(player, slot)
var axe: Node3D = world._dropped.get_child(world._dropped.get_child_count() - 1)
check(axe.item_id == "stone_axe" and int(axe.extra.get("dur", -1)) == 7, "a worn tool keeps its wear on the ground")
print("bag rows on 1-8 (K / M)")
inv.load_data(Inventory.new(inv.size()).to_data())
inv.set_slot(11, {"id": "torch", "count": 1})
player.select(0)
player.select_in_row(3)
check(player.selected == 3 and player.row() == 0, "4 on the top row: slot 4")
player.change_row(1)
check(player.selected == 11 and player.row() == 1 and player.held_item_id() == "torch", "M: the row below, same key: you're holding its torch")
player.select_in_row(8)
check(player.selected == 8, "the wheel wraps round within the row")
player.select(0)
player.change_row(-1)
check(player.row() == player.rows() - 1 and player.rows() == 5, "K from the top row: the bottom one (5 rows)")
var SP: Script = load("res://scripts/net/server_player.gd")
var sp: Node = SP.new()
sp.select(27)
check(sp.selected == 27, "a server lets you hold from any row")
sp.free()
world.queue_free()
await process_frame
_finished = true