Uses for every material (clay jar, bedroll, pelt rug, fang necklace, bear cloak, pemmican, bait), Q to drop, K/M bag rows on 1-8, help pages; protocol 14
This commit is contained in:
234
tests/test_items_more.gd
Normal file
234
tests/test_items_more.gd
Normal file
@@ -0,0 +1,234 @@
|
||||
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
|
||||
Reference in New Issue
Block a user