Lights to place indoors (tallow candle, torch stand); docs brought up to date; the journey plan; sticky teleports in net tests; protocol 15

This commit is contained in:
Paul Bailey
2026-11-06 13:56:43 +00:00
parent 5f40fa3f7b
commit eaeb841c1d
15 changed files with 360 additions and 18 deletions

64
scripts/building/lamp.gd Normal file
View File

@@ -0,0 +1,64 @@
extends "res://scripts/building/piece.gd"
## A light to place indoors (or anywhere): a tallow candle or a torch stand.
## Always lit, no fuel. Its item's "lamp" says how: {"range", "energy",
## "height" (of the flame), "flicker" (0 = steady), "color"}.
## Flames are a small additive glow; the light itself is an OmniLight3D.
var _light: OmniLight3D
var _flame: MeshInstance3D
var _noise := FastNoiseLite.new()
var _t := 0.0
var _base := 1.0
var _flicker := 0.0
var _h := 0.5
func _build() -> void:
add_to_group("lamp")
var l: Dictionary = def.get("lamp", {})
_base = float(l.get("energy", 1.0))
_flicker = float(l.get("flicker", 0.2))
_h = float(l.get("height", 0.5))
_noise.seed = int(get_instance_id() % 1000)
_noise.frequency = 2.0
_t = randf() * 100.0
_light = OmniLight3D.new()
var c: Array = l.get("color", [1.0, 0.7, 0.4])
_light.light_color = Color(c[0], c[1], c[2])
_light.light_energy = _base
_light.omni_range = float(l.get("range", 6.0))
_light.omni_attenuation = 1.3
_light.shadow_enabled = bool(l.get("shadows", false))
_light.position = Vector3(0, _h + 0.05, 0)
add_child(_light)
# The flame: a small glowing teardrop at the wick.
var m := StandardMaterial3D.new()
m.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
m.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
m.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
m.albedo_color = Color(1.0, 0.72, 0.32, 0.85)
var s := SphereMesh.new()
var fr := float(l.get("flame", 0.03))
s.radius = fr
s.height = fr * 3.2
s.radial_segments = 8
s.rings = 4
s.material = m
_flame = MeshInstance3D.new()
_flame.mesh = s
_flame.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
_flame.position = Vector3(0, _h, 0)
add_child(_flame)
func display_name() -> String:
return String(def.get("name", "Light"))
func interact_prompt(_player: Node) -> String:
return "%s (X to take it up)" % display_name()
func _process(delta: float) -> void:
if _flicker <= 0.0 or _light == null:
return
_t += delta
var f := _noise.get_noise_1d(_t * 9.0)
_light.light_energy = _base * (1.0 + f * _flicker)
_flame.scale = Vector3.ONE * (1.0 + f * _flicker * 0.8)

View File

@@ -118,6 +118,8 @@ static func build(key: String) -> ArrayMesh:
"pelt_rug": m = _rug(WOLF_FUR, 0.85)
"bear_cloak": m = _cloak(BEAR_FUR)
"clay_jar": m = _jar()
"tallow_candle": m = _candle()
"torch_stand": m = _torch_stand()
"bedroll": m = _bedroll()
"fang_necklace": m = _necklace()
"pemmican": m = _pebble(331, Color(0.42, 0.24, 0.16), Vector3(0.1, 0.05, 0.07), 0.15)
@@ -1033,6 +1035,29 @@ static func _jar() -> ArrayMesh:
_box(st, Vector3(0, 0.63, 0), Vector3(0.08, 0.04, 0.05), PLANK_DARK)
return P._commit(st)
## A squat tallow candle on a clay dish; the wick's top is at y = 0.2
## (lamp.gd puts the flame there).
static func _candle() -> ArrayMesh:
var st := P._begin()
var r := _rng(359)
P._cylinder(st, Vector3(0, 0.0, 0), Vector3(0, 0.02, 0), 0.08, 0.09, 8, CLAY_FIRED, r, -1.0, P._mul(CLAY_FIRED, 1.1))
P._cylinder(st, Vector3(0, 0.02, 0), Vector3(0, 0.16, 0), 0.04, 0.036, 8, Color(0.93, 0.89, 0.74), r, -1.0, Color(0.96, 0.93, 0.8))
P._cylinder(st, Vector3(0, 0.16, 0), Vector3(0, 0.19, 0), 0.004, 0.003, 4, ASH, r)
return P._commit(st)
## A torch in a wooden stand: three splayed feet, a post, the head at 1.4 m
## (lamp.gd's flame sits just above it).
static func _torch_stand() -> ArrayMesh:
var st := P._begin()
var r := _rng(367)
for i in range(3):
var a := TAU * i / 3.0
P._cylinder(st, Vector3(cos(a) * 0.28, 0.0, sin(a) * 0.28), Vector3(0, 0.45, 0), 0.03, 0.035, 5, _board(r, PLANK_DARK), r)
P._cylinder(st, Vector3(0, 0.0, 0), Vector3(0, 1.3, 0), 0.04, 0.035, 6, _board(r), r)
_box(st, Vector3(0, 0.5, 0), Vector3(0.12, 0.05, 0.12), BINDING)
P._cylinder(st, Vector3(0, 1.28, 0), Vector3(0, 1.42, 0), 0.05, 0.06, 6, CLOTH, r, -1.0, ASH)
return P._commit(st)
## A hide bedroll: a flat mat with its rolled-up end and a feather pillow.
static func _bedroll() -> ArrayMesh:
var st := P._begin()

View File

@@ -32,7 +32,7 @@ signal clock(day: int, time: float) ## the server's time of day
const Identity = preload("res://scripts/net/identity.gd")
const ServerConfig = preload("res://scripts/net/server_config.gd")
const PROTOCOL := 14 ## 3: shared world; 4: the server decides; 5: wildlife; 6: rules and chat (M2.6); 7: the alpha, broken doors; 8: the bear; 9: crows and foxes; 10: the barrow (P3); 11: food spoilage, lightning (P4); 12: the standing stones and the Old Stag (P5); 13: sounds sent to players nearby (sfx.gd); 14: dropping items, any bag row in hand, new items (jar, bedroll, necklace, bear cloak, rug, pemmican)
const PROTOCOL := 15 ## 3: shared world; 4: the server decides; 5: wildlife; 6: rules and chat (M2.6); 7: the alpha, broken doors; 8: the bear; 9: crows and foxes; 10: the barrow (P3); 11: food spoilage, lightning (P4); 12: the standing stones and the Old Stag (P5); 13: sounds sent to players nearby (sfx.gd); 14: dropping items, any bag row in hand, new items (jar, bedroll, necklace, bear cloak, rug, pemmican); 15: placed lights (candle, torch stand)
const DEFAULT_PORT := 24242
const HANDSHAKE_TIMEOUT := 10.0
const JOIN_TIMEOUT := 20.0 ## includes the server building its terrain on first join

View File

@@ -31,6 +31,7 @@ const PIECES := {
"hearth": preload("res://scripts/building/hearth.gd"),
"rug": preload("res://scripts/building/rug.gd"),
"jar": preload("res://scripts/building/jar.gd"),
"lamp": preload("res://scripts/building/lamp.gd"),
"stonecutter": preload("res://scripts/building/stonecutter.gd"),
}
const BuildPiece = preload("res://scripts/building/build_piece.gd")