Wolf den search steps into the valley when the rim is steep everywhere

This commit is contained in:
Paul Bailey
2026-11-04 16:02:40 +00:00
parent d489bb57ed
commit 74cbf96458
3 changed files with 88 additions and 33 deletions

View File

@@ -57,40 +57,53 @@ func _find_site(world: Node3D) -> Vector3:
var space := world.get_world_3d().direct_space_state
var box := BoxShape3D.new()
box.size = Vector3(HALF_SIZE * 2.0, 6.0, HALF_SIZE * 2.0)
var ex: Array[RID] = [terrain.get_node("TerrainBody").get_rid(), world.player.get_rid()]
for step in range(52):
var ang := deg_to_rad(start + step * 7.0)
var dir := Vector2(cos(ang), sin(ang))
# Walk inward from the rim until the den sits inside the walkable
# area, a little way short of the wall.
var r: float = terrain.play_radius + 10.0
while r > terrain.play_radius - 40.0:
var x := dir.x * r
var z := dir.y * r
if terrain.inside_boundary(x, z, 11.0):
break
r -= 1.0
var c := Vector3(dir.x * r, 0, dir.y * r)
if terrain.sea_weight(c.x, c.z) > 0.02 or Vector2(c.x, c.z).length() < 50.0:
continue
var hs: Array[float] = []
for d in [Vector2(-1, -1), Vector2(1, -1), Vector2(-1, 1), Vector2(1, 1), Vector2.ZERO]:
hs.append(terrain.sample_height(c.x + d.x * 4.0, c.z + d.y * 4.0))
if hs.max() - hs.min() > 2.2 or hs.min() < terrain.water_level + 1.0:
continue
c.y = hs[4]
# Trees and boulders in the way are cleared (_clear_area); just avoid
# the thickest woods. Counted by position, because their colliders
# were only just created and the physics space hasn't seen them yet.
var crowd := 0
for uid in world.props.resource_nodes:
var n: Node3D = world.props.resource_nodes[uid]
if Vector2(n.global_position.x - c.x, n.global_position.z - c.z).length() < HALF_SIZE:
crowd += 1
if crowd <= 5:
return c
# Pass 1 tries only the foot of the rim wall in each direction (the
# original search, so islands that already had a den keep it in the same
# place). Where the rim is steep all the way round (seed 777, seed 4),
# pass 2 steps further into the valley, a few metres at a time.
for pass_insets in [[0.0], [3.0, 6.0, 9.0, 12.0, 16.0, 20.0, 25.0, 30.0]]:
for step in range(52):
var ang := deg_to_rad(start + step * 7.0)
var dir := Vector2(cos(ang), sin(ang))
var r_foot := _rim_foot(dir)
for inset in pass_insets:
var c := _check_site(world, dir * (r_foot - inset))
if c != Vector3.INF:
return c
return Vector3.INF
## Walks inward from the rim until the den sits inside the walkable area, a
## little way short of the wall. Returns that distance from the centre.
func _rim_foot(dir: Vector2) -> float:
var r: float = terrain.play_radius + 10.0
while r > terrain.play_radius - 40.0:
if terrain.inside_boundary(dir.x * r, dir.y * r, 11.0):
break
r -= 1.0
return r
## The site at `p` (x, z) with its height, or INF if it's no good: near the
## sea or the spawn, too sloped, too low, or in thick woods.
func _check_site(world: Node3D, p: Vector2) -> Vector3:
if terrain.sea_weight(p.x, p.y) > 0.02 or p.length() < 50.0:
return Vector3.INF
var hs: Array[float] = []
for d in [Vector2(-1, -1), Vector2(1, -1), Vector2(-1, 1), Vector2(1, 1), Vector2.ZERO]:
hs.append(terrain.sample_height(p.x + d.x * 4.0, p.y + d.y * 4.0))
if hs.max() - hs.min() > 2.2 or hs.min() < terrain.water_level + 1.0:
return Vector3.INF
# Trees and boulders in the way are cleared (_clear_area); just avoid
# the thickest woods. Counted by position, because their colliders
# were only just created and the physics space hasn't seen them yet.
var crowd := 0
for uid in world.props.resource_nodes:
var n: Node3D = world.props.resource_nodes[uid]
if Vector2(n.global_position.x - p.x, n.global_position.z - p.y).length() < HALF_SIZE:
crowd += 1
if crowd > 5:
return Vector3.INF
return Vector3(p.x, hs[4], p.y)
## Removes the trees, boulders, bushes and pickups the den is built over.
## Deterministic (the site comes from the seed), and the other scenery keeps
## its save ids because those come from creation order.