The Cove's shore life: crabs up the beach from dusk till dawn (keep to the sand, pinch if you get close, crab meat to cook), gulls after food on the sand and fish on a smoking rack; online too
This commit is contained in:
106
scripts/creatures/shore.gd
Normal file
106
scripts/creatures/shore.gd
Normal file
@@ -0,0 +1,106 @@
|
||||
extends Node3D
|
||||
## The shore's own wildlife (0.6.0, a land whose worldlet lists "crabs" in
|
||||
## its "wildlife": the Cove). From dusk till dawn crabs come up out of the
|
||||
## sea onto the beach, a few around each player who's down on the sand, at
|
||||
## the waterline and out of the way. At dawn they go back in (crab.gd).
|
||||
## Not saved: like the other wildlife they simply carry on around you.
|
||||
##
|
||||
## On a server this runs for everyone, and players see the crabs through
|
||||
## creature_net.gd.
|
||||
|
||||
const CrabScript = preload("res://scripts/creatures/crab.gd")
|
||||
|
||||
const BUDGET := 5 ## per player on the beach, up to twice it
|
||||
const CHECK_EVERY := 2.0
|
||||
const RING_MIN := 14.0
|
||||
const RING_MAX := 45.0
|
||||
const NOT_WITHIN := 10.0 ## never comes up right beside anyone
|
||||
const DESPAWN := 80.0
|
||||
const BEACH_NEAR := 40.0 ## a player this close to the sand counts as on the beach
|
||||
|
||||
var world: Node3D
|
||||
var terrain: Node3D
|
||||
var enabled := true
|
||||
var _rng := RandomNumberGenerator.new()
|
||||
var _acc := 0.0
|
||||
var _serial := 0
|
||||
|
||||
func setup(w: Node3D) -> void:
|
||||
world = w
|
||||
terrain = w.terrain
|
||||
_rng.seed = terrain.noise_seed + 1201
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
step(delta)
|
||||
|
||||
## Dusk till dawn.
|
||||
func night() -> bool:
|
||||
var h: float = world.day_night.hours()
|
||||
return h >= 17.5 or h < 6.5
|
||||
|
||||
## Living crabs.
|
||||
func alive() -> Array:
|
||||
return get_children().filter(func(c): return is_instance_valid(c) and not c.is_queued_for_deletion() and not c.is_dead())
|
||||
|
||||
func step(dt: float) -> void:
|
||||
if not enabled or world.creatures == null or not world.creatures.enabled:
|
||||
return
|
||||
_acc += dt
|
||||
if _acc < CHECK_EVERY:
|
||||
return
|
||||
_acc = 0.0
|
||||
var everyone: Array = world.players()
|
||||
for c in get_children():
|
||||
if not everyone.any(func(p): return p.global_position.distance_to(c.global_position) <= DESPAWN):
|
||||
c.queue_free()
|
||||
if not night() or everyone.is_empty():
|
||||
return
|
||||
var on_beach := everyone.filter(func(p): return _near_sand(p.global_position))
|
||||
if on_beach.is_empty():
|
||||
return
|
||||
if alive().size() < BUDGET * clampi(on_beach.size(), 1, 2):
|
||||
var who: Node3D = on_beach[_rng.randi() % on_beach.size()]
|
||||
spawn_near(who.global_position) # (one at a time: they trickle up out of the sea)
|
||||
|
||||
func _near_sand(p: Vector3) -> bool:
|
||||
for a in [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]:
|
||||
var q := p + Vector3(cos(a), 0.0, sin(a)) * BEACH_NEAR * 0.5
|
||||
if terrain.on_sand(p.x, p.z) or terrain.on_sand(q.x, q.z):
|
||||
return true
|
||||
return false
|
||||
|
||||
## A spot on the sand at the waterline, RING_MIN..RING_MAX from `around`
|
||||
## and not right beside anyone, or Vector3.INF.
|
||||
func find_spot(around: Vector3) -> Vector3:
|
||||
for _try in range(40):
|
||||
var a := _rng.randf() * TAU
|
||||
var r := _rng.randf_range(RING_MIN, RING_MAX)
|
||||
var x := around.x + cos(a) * r
|
||||
var z := around.z + sin(a) * r
|
||||
if not terrain.inside_boundary(x, z, 6.0) or terrain.beach_weight(x, z) < 0.75:
|
||||
continue
|
||||
var h: float = terrain.sample_height(x, z)
|
||||
if h < terrain.water_level + 0.25 or h > terrain.water_level + 0.9:
|
||||
continue
|
||||
var p := Vector3(x, h, z)
|
||||
if world.players().any(func(pl): return pl.global_position.distance_to(p) < NOT_WITHIN):
|
||||
continue
|
||||
return p
|
||||
return Vector3.INF
|
||||
|
||||
func spawn_near(around: Vector3) -> Node3D:
|
||||
var p := find_spot(around)
|
||||
if p == Vector3.INF:
|
||||
return null
|
||||
return spawn(p)
|
||||
|
||||
func spawn(pos: Vector3) -> Node3D:
|
||||
var c: AnimatableBody3D = CrabScript.new()
|
||||
_serial += 1
|
||||
c.setup("crab", world, pos, _rng.randi() + _serial)
|
||||
add_child(c)
|
||||
c.global_position = Vector3(pos.x, terrain.sample_height(pos.x, pos.z), pos.z)
|
||||
c.state = "wander"
|
||||
c.yaw = _rng.randf() * TAU
|
||||
c.target_yaw = c.yaw
|
||||
return c
|
||||
Reference in New Issue
Block a user