extends Node3D ## M0 mood props — a few low-poly trees and rocks built from primitives in code, ## so the scene looks "alive" with no imported assets. These are placeholders; ## real low-poly models (free asset packs, see docs/ROADMAP.md M0) replace them. ## ## place_on(terrain) is called by world.gd once the terrain exists, so each prop ## sits on the real ground height instead of floating at Y=0. var _terrain: Node3D func place_on(terrain: Node3D) -> void: _terrain = terrain var rng := RandomNumberGenerator.new() rng.seed = 42 for i in range(18): var x := rng.randf_range(-55.0, 55.0) var z := rng.randf_range(-55.0, 55.0) var y: float = _terrain.sample_height(x, z) if rng.randf() < 0.65: _make_tree(Vector3(x, y, z), rng) else: _make_rock(Vector3(x, y, z), rng) func _make_tree(pos: Vector3, rng: RandomNumberGenerator) -> void: var root := Node3D.new() root.position = pos add_child(root) var trunk := MeshInstance3D.new() var tm := CylinderMesh.new() tm.top_radius = 0.18 tm.bottom_radius = 0.28 tm.height = 3.0 trunk.mesh = tm trunk.position = Vector3(0, 1.5, 0) trunk.material_override = _mat(Color(0.33, 0.24, 0.16)) root.add_child(trunk) # Collision: a capsule around the trunk so you can't walk through the tree. var body := StaticBody3D.new() root.add_child(body) var col := CollisionShape3D.new() var cap := CapsuleShape3D.new() cap.radius = 0.35 cap.height = 4.0 col.shape = cap col.position = Vector3(0, 2.0, 0) body.add_child(col) # Low-poly conifer canopy: stacked cones. var h := 3.0 for j in range(3): var cone := MeshInstance3D.new() var cm := CylinderMesh.new() cm.top_radius = 0.0 cm.bottom_radius = 1.6 - j * 0.4 cm.height = 1.6 cm.radial_segments = 6 # low-poly cone.mesh = cm cone.position = Vector3(0, h + j * 1.0, 0) cone.material_override = _mat(Color(0.16, 0.34, 0.2)) root.add_child(cone) root.rotate_y(rng.randf_range(0, TAU)) var s := rng.randf_range(0.85, 1.35) root.scale = Vector3(s, s, s) func _make_rock(pos: Vector3, rng: RandomNumberGenerator) -> void: var rock := MeshInstance3D.new() var bm := BoxMesh.new() var box_size := Vector3(rng.randf_range(0.8, 1.8), rng.randf_range(0.6, 1.2), rng.randf_range(0.8, 1.8)) bm.size = box_size rock.mesh = bm rock.position = pos + Vector3(0, 0.3, 0) rock.rotation = Vector3(rng.randf_range(-0.2, 0.2), rng.randf_range(0, TAU), rng.randf_range(-0.2, 0.2)) rock.material_override = _mat(Color(0.42, 0.42, 0.45)) add_child(rock) # Collision: a box matching the rock mesh. var body := StaticBody3D.new() body.position = rock.position body.rotation = rock.rotation add_child(body) var col := CollisionShape3D.new() var box := BoxShape3D.new() box.size = box_size col.shape = box body.add_child(col) func _mat(c: Color) -> StandardMaterial3D: var m := StandardMaterial3D.new() m.albedo_color = c m.roughness = 0.95 return m