Odd-shaped houses (Gitea #7): roofs may run through other roofs (an L, a wing at an angle, roofs at different heights), but not inside an identical one, and nothing else goes through them; the guide explains growing a house
This commit is contained in:
@@ -162,6 +162,23 @@ func recompute() -> Array:
|
|||||||
|
|
||||||
# --- placement --------------------------------------------------------------
|
# --- placement --------------------------------------------------------------
|
||||||
|
|
||||||
|
## Roof pieces: these may pass through one another.
|
||||||
|
const ROOF_KINDS := ["roof", "roof_ridge"]
|
||||||
|
|
||||||
|
## A roof piece of `kind` already sitting at `xf` (the same place, facing
|
||||||
|
## the same way), so a second one isn't put inside the first.
|
||||||
|
func _same_roof_at(kind: String, xf: Transform3D, exclude: Node) -> bool:
|
||||||
|
for p in build_pieces():
|
||||||
|
if p == exclude or p.kind != kind:
|
||||||
|
continue
|
||||||
|
var pxf: Transform3D = p.global_transform
|
||||||
|
var facing: float = pxf.basis.z.normalized().dot(xf.basis.z.normalized())
|
||||||
|
if kind == "roof_ridge":
|
||||||
|
facing = absf(facing) # (it's the same both ways round)
|
||||||
|
if pxf.origin.distance_to(xf.origin) < 0.15 and facing > 0.99:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
## Why a building piece can't go at `xf` ("" = it can).
|
## Why a building piece can't go at `xf` ("" = it can).
|
||||||
func check(def: Dictionary, xf: Transform3D, exclude: Node = null) -> String:
|
func check(def: Dictionary, xf: Transform3D, exclude: Node = null) -> String:
|
||||||
var kind: String = def.place.kind
|
var kind: String = def.place.kind
|
||||||
@@ -172,6 +189,9 @@ func check(def: Dictionary, xf: Transform3D, exclude: Node = null) -> String:
|
|||||||
if c.y < terrain.water_level + 0.1:
|
if c.y < terrain.water_level + 0.1:
|
||||||
return "Can't build in water"
|
return "Can't build in water"
|
||||||
var space := world.get_world_3d().direct_space_state
|
var space := world.get_world_3d().direct_space_state
|
||||||
|
var roofy: bool = kind in ROOF_KINDS
|
||||||
|
if roofy and _same_roof_at(kind, xf, exclude):
|
||||||
|
return "There's already a roof there"
|
||||||
for s in info.shapes:
|
for s in info.shapes:
|
||||||
var made := BuildPiece.make_shape(s, OVERLAP_MARGIN)
|
var made := BuildPiece.make_shape(s, OVERLAP_MARGIN)
|
||||||
var q := PhysicsShapeQueryParameters3D.new()
|
var q := PhysicsShapeQueryParameters3D.new()
|
||||||
@@ -182,9 +202,13 @@ func check(def: Dictionary, xf: Transform3D, exclude: Node = null) -> String:
|
|||||||
if exclude != null:
|
if exclude != null:
|
||||||
ex.append(exclude.get_rid())
|
ex.append(exclude.get_rid())
|
||||||
q.exclude = ex
|
q.exclude = ex
|
||||||
var hits := space.intersect_shape(q, 1)
|
var hits := space.intersect_shape(q, 8)
|
||||||
if not hits.is_empty():
|
for h in hits:
|
||||||
last_blocker = hits[0].collider
|
# Roofs can run through other roofs (Gitea #7: an L, a wing at an
|
||||||
|
# angle, roofs at odd heights), but nothing else.
|
||||||
|
if roofy and h.collider != null and String(h.collider.get("kind")) in ROOF_KINDS:
|
||||||
|
continue
|
||||||
|
last_blocker = h.collider
|
||||||
return "Something's in the way"
|
return "Something's in the way"
|
||||||
var mat := BuildPiece.material_of(def)
|
var mat := BuildPiece.material_of(def)
|
||||||
if support_for(kind, xf, exclude, mat) < MIN_SUPPORT:
|
if support_for(kind, xf, exclude, mat) < MIN_SUPPORT:
|
||||||
|
|||||||
@@ -38,6 +38,31 @@ func _run() -> void:
|
|||||||
await _longer()
|
await _longer()
|
||||||
await _wider()
|
await _wider()
|
||||||
await _six()
|
await _six()
|
||||||
|
print("an L: a wing off the side of the hut")
|
||||||
|
var l: Dictionary = await _l_shape()
|
||||||
|
print(" (wing walls failed: %s)" % [l.get("walls_failed", [])])
|
||||||
|
check(l.failed.is_empty() and l.placed == 8, "the wing's roof and gables, where it meets the hut too (%d placed%s)" % [l.placed, "" if l.failed.is_empty() else ": %s" % [l.failed]])
|
||||||
|
if l.has("c"):
|
||||||
|
var inside := Vector3(l.c.x, l.f0.global_position.y, l.c.z + 4.0)
|
||||||
|
check(B.roofed_at(inside) and B.sheltered_at(inside), "sheltered in the wing (roof %s, walls %.2f)" % [B.roofed_at(inside), B.enclosure_at(inside)])
|
||||||
|
print("haphazard: a wider wing, its roof carried on over the hut's")
|
||||||
|
var o: Dictionary = await _odd_wing()
|
||||||
|
check(o.has("floors_walls") and o.floors_walls.failed.is_empty(), "(the wing's floors and walls: %s)" % [o.get("floors_walls", {}).get("failed", o.failed)])
|
||||||
|
check(o.failed.is_empty() and o.placed == 9, "its roof goes on, the last bay over the hut's roof (%d placed%s)" % [o.placed, "" if o.failed.is_empty() else ": %s" % [o.failed]])
|
||||||
|
if o.has("c"):
|
||||||
|
var want := [Vector3(1, 2, 3), Vector3(1, 2, -3), Vector3(1, 3, 0)]
|
||||||
|
var top: float = o.top
|
||||||
|
check(want.all(func(v): return o.where.any(func(p): return p.distance_to(Vector3(v.x, top + v.y, v.z)) < 0.05)),
|
||||||
|
"(that bay is where it's meant to be, over the hut's roof)")
|
||||||
|
print("roofs through roofs")
|
||||||
|
var hut2: Array = B.build_pieces().filter(func(p): return p.kind == "roof")
|
||||||
|
var r0: Node3D = hut2[0]
|
||||||
|
var roof_def: Dictionary = DB().get_item("wood_roof")
|
||||||
|
var across := Transform3D(r0.global_basis.rotated(Vector3.UP, Q), r0.global_transform * Vector3(0, 0.5, -1.0))
|
||||||
|
check(B.check(roof_def, across) != "Something's in the way", "a roof crossing another at right angles isn't in its way (%s)" % B.check(roof_def, across))
|
||||||
|
check(B.check(roof_def, r0.global_transform) == "There's already a roof there", "but not one roof inside another, the same way round")
|
||||||
|
var wall_def: Dictionary = DB().get_item("wood_wall")
|
||||||
|
check(B.check(wall_def, Transform3D(r0.global_basis, r0.global_transform * Vector3(0, -0.4, -1.0))) == "Something's in the way", "and a wall still can't go through a roof")
|
||||||
_finished = true
|
_finished = true
|
||||||
check(_finished, "test ran to the end (no script errors)")
|
check(_finished, "test ran to the end (no script errors)")
|
||||||
for x in S.list_worlds():
|
for x in S.list_worlds():
|
||||||
@@ -50,6 +75,9 @@ func _run() -> void:
|
|||||||
func _place(out: Dictionary, c: Vector3, id: String, local: Vector3, ryaw: float, on: Node3D) -> Node3D:
|
func _place(out: Dictionary, c: Vector3, id: String, local: Vector3, ryaw: float, on: Node3D) -> Node3D:
|
||||||
return await B._hut_place(out, c, 0.0, id, local, ryaw, on)
|
return await B._hut_place(out, c, 0.0, id, local, ryaw, on)
|
||||||
|
|
||||||
|
func DB() -> Node:
|
||||||
|
return root.get_node("ItemDB")
|
||||||
|
|
||||||
func _new_out() -> Dictionary:
|
func _new_out() -> Dictionary:
|
||||||
return {"placed": 0, "failed": [], "pieces": []}
|
return {"placed": 0, "failed": [], "pieces": []}
|
||||||
|
|
||||||
@@ -209,3 +237,66 @@ func _six() -> void:
|
|||||||
for z in [-2.0, 0.0, 2.0]:
|
for z in [-2.0, 0.0, 2.0]:
|
||||||
var inside := Vector3(c.x, f0.global_position.y, c.z + z)
|
var inside := Vector3(c.x, f0.global_position.y, c.z + z)
|
||||||
check(B.roofed_at(inside) and B.sheltered_at(inside), "sheltered inside, %+.0f m across (roof %s, walls %.2f)" % [z, B.roofed_at(inside), B.enclosure_at(inside)])
|
check(B.roofed_at(inside) and B.sheltered_at(inside), "sheltered inside, %+.0f m across (roof %s, walls %.2f)" % [z, B.roofed_at(inside), B.enclosure_at(inside)])
|
||||||
|
|
||||||
|
## An L: a wing off the side of the 4 x 4 hut, its ridge at right angles to
|
||||||
|
## the hut's. Returns {placed, failed} for the wing's roof and gables.
|
||||||
|
func _l_shape() -> Dictionary:
|
||||||
|
var c := _site(4.5, [Vector3(-30, 0, -30), Vector3(35, 0, 10), Vector3(-10, 0, 40), Vector3(20, 0, -35),
|
||||||
|
Vector3(-40, 0, 5), Vector3(10, 0, 45), Vector3(45, 0, -10), Vector3(-20, 0, -45)])
|
||||||
|
if c == Vector3.INF:
|
||||||
|
return {"placed": 0, "failed": ["no site"]}
|
||||||
|
var hut: Dictionary = await B.debug_build_hut(c, 0.0)
|
||||||
|
var f0: Node3D = hut.pieces[0]
|
||||||
|
var top: float = f0.global_position.y - c.y
|
||||||
|
var out := _new_out()
|
||||||
|
# The wing: 4 m wide, 4 m out from the hut's south wall (z = 2 to 6).
|
||||||
|
for x in [-1.0, 1.0]:
|
||||||
|
for z in [3.0, 5.0]:
|
||||||
|
await _place(out, c, "wood_floor", Vector3(x, 0, z), 0.0, null)
|
||||||
|
for z in [3.0, 5.0]:
|
||||||
|
await _place(out, c, "wood_wall", Vector3(1.9, top, z), Q, f0)
|
||||||
|
await _place(out, c, "wood_wall", Vector3(-1.9, top, z), Q, f0)
|
||||||
|
for x in [-1.0, 1.0]:
|
||||||
|
await _place(out, c, "wood_wall", Vector3(x, top, 5.9), 0.0, f0)
|
||||||
|
var walls_failed: Array = out.failed.duplicate()
|
||||||
|
var roof := _new_out()
|
||||||
|
# Its roof: ridge along z, panels up from the east and west walls.
|
||||||
|
for z in [3.0, 5.0]:
|
||||||
|
await _place(roof, c, "wood_roof", Vector3(2.0, top + 2.0, z), Q, f0)
|
||||||
|
await _place(roof, c, "wood_roof", Vector3(-2.0, top + 2.0, z), -Q, f0)
|
||||||
|
# The far end's gables, and the gables where the wing meets the hut (on
|
||||||
|
# the hut's south wall, under the wing's roof).
|
||||||
|
for zz in [6.0, 2.0]:
|
||||||
|
await _place(roof, c, "wood_gable", Vector3(1.0, top + 2.0, zz), 0.0, f0)
|
||||||
|
await _place(roof, c, "wood_gable", Vector3(-1.0, top + 2.0, zz), PI, f0)
|
||||||
|
return {"placed": roof.placed, "failed": roof.failed, "walls_failed": walls_failed, "c": c, "f0": f0}
|
||||||
|
|
||||||
|
## Haphazard: a 6 m-wide wing off the east end of the 4 x 4 hut. Its roof
|
||||||
|
## is taller than the hut's, so to close the gap where they meet, its roof
|
||||||
|
## carries on one bay over the hut's roof.
|
||||||
|
func _odd_wing() -> Dictionary:
|
||||||
|
var c := _site(4.5, [Vector3(40, 0, 30), Vector3(-45, 0, -20), Vector3(30, 0, 40), Vector3(-15, 0, -50),
|
||||||
|
Vector3(50, 0, 0), Vector3(-50, 0, 15), Vector3(0, 0, 50), Vector3(15, 0, -50)])
|
||||||
|
if c == Vector3.INF:
|
||||||
|
return {"placed": 0, "failed": ["no site"]}
|
||||||
|
var hut: Dictionary = await B.debug_build_hut(c, 0.0)
|
||||||
|
var f0: Node3D = hut.pieces[0]
|
||||||
|
var top: float = f0.global_position.y - c.y
|
||||||
|
var out := _new_out()
|
||||||
|
for x in [3.0, 5.0]:
|
||||||
|
for z in [-2.0, 0.0, 2.0]:
|
||||||
|
await _place(out, c, "wood_floor", Vector3(x, 0, z), 0.0, null)
|
||||||
|
for x in [3.0, 5.0]:
|
||||||
|
await _place(out, c, "wood_wall", Vector3(x, top, -2.9), 0.0, f0)
|
||||||
|
await _place(out, c, "wood_wall", Vector3(x, top, 2.9), 0.0, f0)
|
||||||
|
for z in [-2.0, 0.0, 2.0]:
|
||||||
|
await _place(out, c, "wood_wall", Vector3(5.9, top, z), Q, f0)
|
||||||
|
var roof := _new_out()
|
||||||
|
# The wing's roof (6 m across: a row each side and a ridge), and one bay
|
||||||
|
# more, at x = 1, through the hut's own roof.
|
||||||
|
for x in [5.0, 3.0, 1.0]:
|
||||||
|
await _place(roof, c, "wood_roof", Vector3(x, top + 2.0, 3.0), 0.0, f0)
|
||||||
|
await _place(roof, c, "wood_roof", Vector3(x, top + 2.0, -3.0), PI, f0)
|
||||||
|
await _place(roof, c, "wood_roof_ridge", Vector3(x, top + 3.0, 0.0), 0.0, f0)
|
||||||
|
var where: Array = roof.pieces.map(func(p): return (p.global_position - c).snapped(Vector3(0.01, 0.01, 0.01)))
|
||||||
|
return {"placed": roof.placed, "failed": roof.failed, "floors_walls": out, "c": c, "f0": f0, "where": where, "top": top}
|
||||||
|
|||||||
Reference in New Issue
Block a user