Files
Cinderwild/scripts/world/props.gd

95 lines
2.8 KiB
GDScript

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