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

@@ -19,6 +19,7 @@ func _ready() -> void:
_bind("interact", [KEY_E])
_bind("inventory", [KEY_TAB, KEY_I])
_bind("rotate_piece", [KEY_R])
_bind("dismantle", [KEY_X])
for i in range(8):
_bind("hotbar_%d" % (i + 1), [KEY_1 + i])
_bind_mouse("attack", MOUSE_BUTTON_LEFT)
@@ -147,6 +148,49 @@ func place(player: Node3D, slot: int, xf: Transform3D, world: Node3D) -> bool:
world.spawn_piece(def, xf)
return true
## Takes a placed piece back into your inventory. Building pieces that lose
## their support collapse, and come back to you too.
func dismantle(player: Node3D, piece: Node3D) -> bool:
if not is_instance_valid(piece) or not piece.is_in_group("piece") or piece.is_queued_for_deletion():
return false
if player.global_position.distance_to(piece.global_position) > 8.0:
return false
if piece.item_id == "grave":
notify.emit("Take your things out and it will crumble")
return false
var why: String = piece.dismantle_block_reason()
if not why.is_empty():
notify.emit(why)
return false
if player.inventory.space_for(piece.item_id) < 1:
notify.emit("Inventory full")
return false
_give(player, piece.item_id, 1)
piece.get_parent().get_parent().remove_piece(piece, player)
return true
func toggle_door(player: Node3D, door: Node3D) -> bool:
if not is_instance_valid(door) or not _in_reach(player, door, 1.0):
return false
door.set_open(not door.open)
return true
## E on a bed: claim it as your spawn point, or (if it's already yours) sleep.
func use_bed(player: Node3D, bed: Node3D) -> bool:
if not is_instance_valid(bed) or not _in_reach(player, bed, 1.0):
return false
var world: Node = bed.get_parent().get_parent()
if not bed.is_spawn():
world.set_bed_spawn(bed)
notify.emit("Spawn point set")
return true
var why: String = world.sleep_block_reason(bed)
if not why.is_empty():
notify.emit(why)
return false
world.sleep(player)
return true
func forage(player: Node3D, bush: Node3D) -> bool:
if not is_instance_valid(bush) or not _in_reach(player, bush) or not bush.ripe():
return false