P1 iron: nuggets, ore, clay, kiln, smelter, forge, iron tools and arrows, lantern, tool wear and repair
This commit is contained in:
@@ -35,6 +35,7 @@ func interact(player: Node) -> void:
|
||||
signal taken_now(pickup: Node)
|
||||
|
||||
var dropped := false ## dropped by the player (arrows, spears): saved, never regrows
|
||||
var extra: Dictionary = {} ## kept when it's picked up (a worn spear's "dur")
|
||||
|
||||
## Picked up: hidden (not freed) so it can reappear later (props.gd regrowth).
|
||||
## Dropped items just go.
|
||||
|
||||
@@ -478,6 +478,19 @@ func _scatter_rocks() -> void:
|
||||
|
||||
_bind_nodes(pending, _flush(groups, meshes, true, "Rocks"))
|
||||
|
||||
## 0..1: how close (x, z) is to the foot of the mountain rim, where scree
|
||||
## and iron collect. 1 within 6 m of the edge of the walkable area, 0 from
|
||||
## 30 m in.
|
||||
func _near_rim(x: float, z: float) -> float:
|
||||
if not terrain.inside_boundary(x, z, 6.0):
|
||||
return 1.0
|
||||
if terrain.inside_boundary(x, z, 30.0):
|
||||
return 0.0
|
||||
for m in [24.0, 18.0, 12.0]: # (inside at margin m = at least m metres from the edge)
|
||||
if terrain.inside_boundary(x, z, m):
|
||||
return 1.0 - (m - 6.0) / 24.0
|
||||
return 1.0
|
||||
|
||||
## Places one rock. Passing `pending` makes it a mineable boulder (if it's
|
||||
## inside the walkable area); otherwise it just gets plain collision.
|
||||
func _place_rock(groups: Dictionary, meshes: Array, hulls: Array, p: Vector2, h: float, n: Vector3,
|
||||
@@ -493,6 +506,12 @@ func _place_rock(groups: Dictionary, meshes: Array, hulls: Array, p: Vector2, h:
|
||||
shape.points = pts
|
||||
if pending != null and terrain.inside_boundary(p.x, p.y, 0.0):
|
||||
var node := _resource_node("rock", Transform3D(b, pos))
|
||||
# Rust-streaked boulders with iron ore in them: mostly near the
|
||||
# rim, a few anywhere. (From the position, so it's the same every
|
||||
# time, without disturbing the scatter's random numbers.)
|
||||
var roll := float(absi(hash(Vector2i(int(p.x * 10.0), int(p.y * 10.0)))) % 1000) / 1000.0
|
||||
if roll < 0.18 + _near_rim(p.x, p.y) * 0.6:
|
||||
node.make_iron(hulls[vi])
|
||||
var cs := CollisionShape3D.new()
|
||||
cs.shape = shape
|
||||
cs.basis = b.orthonormalized()
|
||||
@@ -591,8 +610,10 @@ func _scatter_pickups() -> void:
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.seed = world_seed + 6
|
||||
# Order matters: new kinds go at the END so existing saves keep their ids.
|
||||
var want := {"stick": 260, "stone": 170, "flint": 80, "mushroom": 110, "feather": 70, "fiber": 150}
|
||||
var radius: Dictionary = {"stick": 0.35, "stone": 0.25, "flint": 0.25, "mushroom": 0.25, "feather": 0.25, "fiber": 0.45}
|
||||
var want := {"stick": 260, "stone": 170, "flint": 80, "mushroom": 110, "feather": 70, "fiber": 150,
|
||||
"iron_nugget": 70, "clay": 60}
|
||||
var radius: Dictionary = {"stick": 0.35, "stone": 0.25, "flint": 0.25, "mushroom": 0.25, "feather": 0.25, "fiber": 0.45,
|
||||
"iron_nugget": 0.25, "clay": 0.3}
|
||||
for id: String in want:
|
||||
# Fibre grows as a plant (wild flax) you pick, 2 fibre a clump.
|
||||
var mesh: Mesh = ItemMeshes.build("fiber_plant") if id == "fiber" else ItemDB.mesh_for(id)
|
||||
@@ -625,8 +646,17 @@ func _scatter_pickups() -> void:
|
||||
# Meadows, and thickest along the lake shore.
|
||||
var wet := 1.0 - smoothstep(terrain.water_level + 0.5, terrain.water_level + 3.0, h)
|
||||
chance = 0.05 + (1.0 - _forest(x, z)) * 0.45 + wet * 0.6
|
||||
"iron_nugget":
|
||||
# In the scree at the foot of the mountains (and on rocky slopes).
|
||||
chance = 0.02 + _near_rim(x, z) * 0.9 + (1.0 - n.y) * 1.5
|
||||
"clay":
|
||||
# The lake shore only.
|
||||
var lake: Vector2 = terrain.lake_center
|
||||
var by_lake: bool = Vector2(x - lake.x, z - lake.y).length() < terrain.lake_radius * 1.6
|
||||
var shore: float = 1.0 - smoothstep(terrain.water_level + 0.4, terrain.water_level + 1.8, h)
|
||||
chance = shore * 0.95 if by_lake else 0.0
|
||||
# A few right by the spawn so the first craft is quick.
|
||||
if Vector2(x - _spawn.x, z - _spawn.z).length() < 18.0:
|
||||
if Vector2(x - _spawn.x, z - _spawn.z).length() < 18.0 and not id in ["iron_nugget", "clay"]:
|
||||
chance += 0.5
|
||||
if keep > chance:
|
||||
continue
|
||||
|
||||
@@ -36,8 +36,50 @@ var _orig_shapes: Array = [] ## [CollisionShape3D, shape, position] befor
|
||||
signal depleted_now(node: Node)
|
||||
|
||||
func display_name() -> String:
|
||||
if is_iron:
|
||||
return "Rusty boulder (iron ore)"
|
||||
return "Tree" if kind == "tree" else "Boulder"
|
||||
|
||||
# --- rusty boulders (iron ore) ---------------------------------------------------
|
||||
|
||||
var is_iron := false
|
||||
var ore_vis: Node3D ## the rust streaks, following the boulder's instance
|
||||
static var _patch_mesh: ArrayMesh
|
||||
|
||||
## Turns this boulder into a rusty one: iron ore when it breaks, and red-brown
|
||||
## streaks on its surface (`hull` = the rock mesh's points, unscaled).
|
||||
func make_iron(hull: PackedVector3Array) -> void:
|
||||
is_iron = true
|
||||
drops_final = {"stone": [2, 4], "iron_ore": [2, 4]}
|
||||
chip_color = Color(0.55, 0.3, 0.18)
|
||||
if DisplayServer.get_name() == "headless":
|
||||
return
|
||||
if _patch_mesh == null:
|
||||
var n := FastNoiseLite.new()
|
||||
n.seed = 5
|
||||
n.frequency = 2.0
|
||||
var st := ProcMesh._begin()
|
||||
ProcMesh._blob(st, Vector3.ZERO, Vector3(0.13, 0.1, 0.018), n, 0.25, Color(0.48, 0.24, 0.12), RandomNumberGenerator.new(), 0)
|
||||
_patch_mesh = ProcMesh._commit(st)
|
||||
ore_vis = Node3D.new()
|
||||
ore_vis.top_level = true
|
||||
add_child(ore_vis)
|
||||
# Streaks on the upper surface, spread out.
|
||||
var up: Array = []
|
||||
for q: Vector3 in hull:
|
||||
if q.y > 0.15:
|
||||
up.append(q)
|
||||
up.sort_custom(func(a, b): return a.x + a.z * 0.37 < b.x + b.z * 0.37)
|
||||
var mat := ItemDB.item_material()
|
||||
for i in range(mini(6, up.size())):
|
||||
var q: Vector3 = up[int(float(i) / 6.0 * up.size())]
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = _patch_mesh
|
||||
mi.material_override = mat
|
||||
mi.transform = Transform3D(Basis.looking_at(-q.normalized(), Vector3.UP if absf(q.normalized().y) < 0.95 else Vector3.RIGHT), q * 0.97)
|
||||
ore_vis.add_child(mi)
|
||||
ore_vis.global_transform = base_xf
|
||||
|
||||
func interact_prompt(player: Node) -> String:
|
||||
var tool: Dictionary = ItemDB.get_item(player.held_item_id()).get("tool", {})
|
||||
var verb := "Chop" if kind == "tree" else "Mine"
|
||||
@@ -101,6 +143,8 @@ func _rock_xf() -> Transform3D:
|
||||
func _set_instance(xf: Transform3D) -> void:
|
||||
if mmi != null and index >= 0:
|
||||
mmi.multimesh.set_instance_transform(index, xf)
|
||||
if ore_vis != null:
|
||||
ore_vis.global_transform = xf # (shrinks, shakes, hides and regrows with it)
|
||||
|
||||
## Restores saved damage when a world loads. hp <= 0 = already felled/mined
|
||||
## (no animation, no drops).
|
||||
|
||||
@@ -24,6 +24,9 @@ const PIECES := {
|
||||
"build": preload("res://scripts/building/build_piece.gd"),
|
||||
"door": preload("res://scripts/building/door.gd"),
|
||||
"bed": preload("res://scripts/building/bed.gd"),
|
||||
"kiln": preload("res://scripts/building/processor.gd"),
|
||||
"smelter": preload("res://scripts/building/processor.gd"),
|
||||
"forge": preload("res://scripts/building/forge.gd"),
|
||||
}
|
||||
const BuildPiece = preload("res://scripts/building/build_piece.gd")
|
||||
const UITheme = preload("res://scripts/ui/theme.gd")
|
||||
@@ -311,6 +314,8 @@ func skip_time(secs: float) -> void:
|
||||
day_night.apply(true)
|
||||
weather.snap()
|
||||
props.skip_time(secs)
|
||||
for m in get_tree().get_nodes_in_group("processor"):
|
||||
m.step(secs) # (kilns and smelters work through the night)
|
||||
for f in get_tree().get_nodes_in_group("heat"):
|
||||
if f.has_method("burn"):
|
||||
f.burn(secs)
|
||||
@@ -537,6 +542,8 @@ func piece_entry(piece: Node3D) -> Dictionary:
|
||||
|
||||
func dropped_entry(d: Node3D) -> Dictionary:
|
||||
var e := {"item": d.item_id, "count": d.amount, "xf": _xf_to_array(d.global_transform)}
|
||||
if not d.extra.is_empty():
|
||||
e["extra"] = d.extra
|
||||
if d.has_meta("nid"):
|
||||
e["nid"] = int(d.get_meta("nid"))
|
||||
return e
|
||||
@@ -570,7 +577,10 @@ func add_piece_entry(e: Dictionary) -> Node3D:
|
||||
func add_dropped_entry(e: Dictionary) -> Node3D:
|
||||
if ItemDB.get_item(e.get("item", "")).is_empty():
|
||||
return null
|
||||
var d := _make_dropped(e.item, int(e.get("count", 1)), _array_to_xf(e.xf))
|
||||
var ex: Dictionary = {}
|
||||
if e.get("extra") is Dictionary and e.extra.has("dur"):
|
||||
ex = {"dur": int(e.extra.dur)}
|
||||
var d := _make_dropped(e.item, int(e.get("count", 1)), _array_to_xf(e.xf), ex)
|
||||
if e.has("nid"):
|
||||
d.set_meta("nid", int(e.nid))
|
||||
return d
|
||||
@@ -772,17 +782,20 @@ func _on_net_left(_reason: String) -> void:
|
||||
## Leaves `count` of an item lying in the world at `xf` (an arrow stuck in a
|
||||
## tree, a thrown spear, meat that didn't fit). Saved with the world.
|
||||
## Online only the server drops things (our game never does: returns null).
|
||||
func drop_item(id: String, count: int, xf: Transform3D) -> Node3D:
|
||||
## `extra` is what's special about it (a worn tool's "dur"), kept when it's
|
||||
## picked up again.
|
||||
func drop_item(id: String, count: int, xf: Transform3D, extra: Dictionary = {}) -> Node3D:
|
||||
if net_sync != null:
|
||||
push_warning("[world] drop_item(%s) on a client: only the server drops items" % id)
|
||||
return null
|
||||
return _make_dropped(id, count, xf)
|
||||
return _make_dropped(id, count, xf, extra)
|
||||
|
||||
func _make_dropped(id: String, count: int, xf: Transform3D) -> Node3D:
|
||||
func _make_dropped(id: String, count: int, xf: Transform3D, extra: Dictionary = {}) -> Node3D:
|
||||
var pk: Area3D = preload("res://scripts/world/pickup.gd").new()
|
||||
pk.setup(id, ItemDB.mesh_for(id), ItemDB.item_material(), 0.3)
|
||||
pk.amount = count
|
||||
pk.dropped = true
|
||||
pk.extra = extra.duplicate()
|
||||
_dropped.add_child(pk)
|
||||
pk.global_transform = xf
|
||||
if server_mode and not _applying:
|
||||
|
||||
Reference in New Issue
Block a user