M1c: weather, snap building, shelter, beds and regrowth

- Seeded weather (clear/cloudy/rain/storm, snow and blizzards on cold days
  and above the snowline), lightning, synthesised rain/wind/thunder sound
- Rain/snow particles stopped by roofs; wet ground, settled snow and wind
  via global shader uniforms
- Snap-together wood floor, wall, door, roof and gable with structural
  support, collapse and X to dismantle
- Shelter detection: dry under a roof, warmer and Rested inside with a fire;
  rain burns an uncovered campfire faster
- Bed sets the spawn point and lets you sleep through the night
- Trees, boulders and ground pickups regrow; timers saved
- Grass cleared under floors; shorter HUD hint
- New test_weather_building suite (62 checks) and tests/run_all.sh, which
  also fails on script errors
This commit is contained in:
Paul Bailey
2026-11-04 10:16:41 +00:00
parent fc7eb3cfed
commit fed96ef118
36 changed files with 2421 additions and 58 deletions

View File

@@ -49,6 +49,14 @@ static func build(key: String) -> ArrayMesh:
"roasted_mushroom": m = _mushroom(MUSH_ROAST)
"torch": m = _torch()
"grave": m = _grave()
"wood_floor": m = _floor()
"wood_wall": m = _wall()
"wood_door": m = _door(true)
"wood_door_frame": m = _door(false)
"wood_door_panel": m = _door_panel()
"wood_roof": m = _roof()
"wood_gable": m = _gable()
"bed": m = _bed()
"berry_bush": m = _berry_bush(true)
"berry_bush_bare": m = _berry_bush(false)
_:
@@ -245,3 +253,123 @@ static func _berry_bush(ripe: bool) -> ArrayMesh:
var d := Vector3(r.randf_range(-1, 1), r.randf_range(0.2, 1), r.randf_range(-1, 1)).normalized() * 0.4
P._blob(st, c + d, Vector3.ONE * 0.045, n, 0.1, BERRY, r, 0)
return P._commit(st)
# --- building pieces ----------------------------------------------------------
# Dimensions must match the colliders in scripts/building/build_piece.gd.
const BEAM := Color(0.40, 0.27, 0.15)
const SHINGLE := Color(0.46, 0.33, 0.20)
const THATCH := Color(0.62, 0.52, 0.30)
const FUR := Color(0.45, 0.30, 0.20)
const LINEN := Color(0.80, 0.76, 0.64)
## Per-board tint so planks don't read as one flat sheet.
static func _board(r: RandomNumberGenerator, base: Color = PLANK) -> Color:
return P._mul(base, r.randf_range(0.82, 1.08))
## 2 x 2 m, top surface at y = 0, 0.2 m deep.
static func _floor() -> ArrayMesh:
var st := P._begin()
var r := _rng(71)
for i in range(5):
_box(st, Vector3(-0.8 + 0.4 * i, -0.05, 0), Vector3(0.385, 0.1, 2.0), _board(r))
for z in [-0.85, 0.0, 0.85]:
_box(st, Vector3(0, -0.15, z), Vector3(2.0, 0.1, 0.22), BEAM)
return P._commit(st)
## Horizontal boards between two posts, 2 x 2 m, 0.2 m thick, bottom centre at origin.
static func _wall_body(st: SurfaceTool, r: RandomNumberGenerator, x0: float, x1: float, y0: float, y1: float) -> void:
var rows := maxi(roundi((y1 - y0) / 0.31), 1)
var h := (y1 - y0) / rows
for i in range(rows):
var y := y0 + h * (i + 0.5)
_box(st, Vector3((x0 + x1) * 0.5, y, 0), Vector3(x1 - x0, h - 0.015, 0.12), _board(r))
static func _wall() -> ArrayMesh:
var st := P._begin()
var r := _rng(73)
_wall_body(st, r, -0.84, 0.84, 0.1, 1.9)
for x in [-0.92, 0.92]:
_box(st, Vector3(x, 1.0, 0), Vector3(0.16, 2.0, 0.2), BEAM)
for y in [0.06, 1.94]:
_box(st, Vector3(0, y, 0), Vector3(1.68, 0.12, 0.2), BEAM)
return P._commit(st)
## Door frame (1.1 x 1.9 m opening); `with_panel` adds the closed door, for the
## inventory icon and the placement ghost.
static func _door(with_panel: bool) -> ArrayMesh:
var st := P._begin()
var r := _rng(79)
for side in [-1.0, 1.0]:
_wall_body(st, r, side * 0.55 if side > 0 else -0.84, 0.84 if side > 0 else -0.55, 0.1, 1.9)
_box(st, Vector3(side * 0.92, 1.0, 0), Vector3(0.16, 2.0, 0.2), BEAM)
_box(st, Vector3(side * 0.6, 0.95, 0), Vector3(0.1, 1.9, 0.2), BEAM) # jamb
_box(st, Vector3(0, 1.95, 0), Vector3(1.68, 0.1, 0.2), BEAM)
_box(st, Vector3(-0.7, 0.06, 0), Vector3(0.28, 0.12, 0.2), BEAM)
_box(st, Vector3(0.7, 0.06, 0), Vector3(0.28, 0.12, 0.2), BEAM)
if with_panel:
_door_panel_into(st, Vector3(-0.54, 0, 0))
return P._commit(st)
## The door leaf, hinge at the origin, spanning +X.
static func _door_panel() -> ArrayMesh:
var st := P._begin()
_door_panel_into(st, Vector3.ZERO)
return P._commit(st)
static func _door_panel_into(st: SurfaceTool, o: Vector3) -> void:
var r := _rng(83)
for i in range(4):
_box(st, o + Vector3(0.135 + 0.27 * i, 0.95, 0), Vector3(0.26, 1.86, 0.07), _board(r))
for y in [0.35, 1.55]:
_box(st, o + Vector3(0.54, y, 0.05), Vector3(0.96, 0.12, 0.04), BEAM)
_box(st, o + Vector3(0.54, 0.95, 0.05), Vector3(0.1, 1.2, 0.04), BEAM, Basis(Vector3.BACK, 0.62))
_box(st, o + Vector3(0.95, 1.0, 0.07), Vector3(0.05, 0.14, 0.05), IRON)
_box(st, o + Vector3(0.95, 1.0, -0.07), Vector3(0.05, 0.14, 0.05), IRON)
## Sloped roof: low edge centre at the origin, rising 1 m over 2 m toward -Z,
## with a short eave hanging past the low edge.
static func _roof() -> ArrayMesh:
var st := P._begin()
var r := _rng(89)
var a := 0.4636476
var b := Basis(Vector3.RIGHT, a)
var n := Vector3(0, cos(a), sin(a))
var along := Vector3(0, sin(a), -cos(a)) # up the slope
var length := sqrt(5.0)
# Boarded deck, then overlapping rows of shingles on top.
_box(st, along * (length * 0.5 - 0.15) + n * 0.03, Vector3(2.0, 0.06, length + 0.3), _board(r, BEAM), b)
var rows := 6
for i in range(rows):
var s := -0.3 + (length + 0.3) * (i + 0.5) / rows
var tilt := Basis(Vector3.RIGHT, a + 0.06)
_box(st, along * s + n * 0.085, Vector3(2.04, 0.05, (length + 0.3) / rows + 0.1), _board(r, SHINGLE), tilt)
return P._commit(st)
## Triangular gable end: 0 m tall at -X, 1 m tall at +X.
static func _gable() -> ArrayMesh:
var st := P._begin()
var r := _rng(97)
for i in range(3):
var y0 := i / 3.0
var x0 := 2.0 * y0 - 1.0 + 0.1
_box(st, Vector3((x0 + 0.92) * 0.5, y0 + 1.0 / 6.0, 0), Vector3(0.92 - x0, 1.0 / 3.0 - 0.015, 0.12), _board(r))
_box(st, Vector3(0.92, 0.5, 0), Vector3(0.16, 1.0, 0.2), BEAM)
_box(st, Vector3(0, 0.06, 0), Vector3(2.0, 0.12, 0.2), BEAM)
return P._commit(st)
## Bed: 1 x 2.1 m frame with a straw mattress, pillow and fur blanket.
static func _bed() -> ArrayMesh:
var st := P._begin()
var r := _rng(101)
for sx in [-0.45, 0.45]:
for sz in [-0.98, 0.98]:
_box(st, Vector3(sx, 0.22, sz), Vector3(0.1, 0.44, 0.1), BEAM)
_box(st, Vector3(sx, 0.28, 0), Vector3(0.08, 0.14, 1.9), _board(r))
for sz in [-0.98, 0.98]:
_box(st, Vector3(0, 0.28, sz), Vector3(0.84, 0.14, 0.08), _board(r))
_box(st, Vector3(0, 0.62, -1.0), Vector3(0.96, 0.4, 0.08), _board(r)) # headboard
_box(st, Vector3(0, 0.38, 0), Vector3(0.86, 0.14, 1.9), THATCH)
_box(st, Vector3(0, 0.5, -0.72), Vector3(0.6, 0.1, 0.32), LINEN)
_box(st, Vector3(0, 0.47, 0.25), Vector3(0.9, 0.08, 1.3), FUR)
return P._commit(st)