Files
millimeters-of-aluminum/scenes/ship/builder/hull_volume.gd
2025-12-05 15:50:48 +01:00

80 lines
3.0 KiB
GDScript

# hull_volume.gd
# An invisible Area2D that players place to define a "room."
# It checks if its boundaries are completely enclosed by pressurized structural pieces.
class_name HullVolume
extends Area2D
# A small buffer for the raycast distance to avoid floating-point errors at the edge.
const RAYCAST_BUFFER = 5.0
const RAYCAST_INCREMENT = 10.0 # Cast a ray every 10 pixels along the edge.
@onready var collision_shape: CollisionShape2D = $CollisionShape2D
var leak_position: Vector2 = Vector2.ZERO
# Performs the "leak check."
func check_seal() -> bool:
# This is the core logic. A robust way to implement this:
# 1. Get the polygon points of this Area2D's CollisionShape2D in global space.
# 2. For each edge (segment) of the polygon:
# a. Cast several rays (or a shape cast) outwards, perpendicular to the edge.
# b. Check if ALL rays for that edge collide with a `StructuralPiece` where `is_pressurized == true`.
# c. If any edge has rays that escape into empty space, a leak is found.
# 3. If a leak is found, store the position (`leak_position`) and return `false`.
# 4. If all edges are sealed, return `true`.
print("Checking for seal...")
var parent_module = get_parent().get_parent()
if not parent_module or not parent_module is RigidBody2D:
print("HullVolume must be a child of a ModuleBuilderController.")
return false
# Get the polygon points in the module's local space
if not collision_shape or not collision_shape.shape is ConcavePolygonShape2D:
print("Warning: HullVolume must use a ConcavePolygonShape2D.")
return false
var module_points = (collision_shape.shape as ConcavePolygonShape2D).get_segments()
# The points are segments, so we iterate through pairs
for i in range(0, module_points.size(), 2):
var p1 = self.global_position + module_points[i]
var p2 = self.global_position + module_points[i+1]
var edge_length = p1.distance_to(p2)
var num_rays = int(ceil(edge_length / RAYCAST_INCREMENT))
# The normal is perpendicular to the edge, pointing outwards
var normal = (p2 - p1).normalized().rotated(PI/2)
# Cast rays along the edge
for r in range(num_rays):
var ray_start = p1.lerp(p2, float(r) / float(num_rays))
var ray_end = ray_start + normal * RAYCAST_BUFFER
# Use the physics space to check for a collision
var query = PhysicsRayQueryParameters2D.create(ray_start, ray_end)
var result = get_world_2d().direct_space_state.intersect_ray(query)
if result.is_empty():
# Ray went into empty space, this is a leak!
print("Leak found: Ray escaped to space.")
leak_position = ray_start
return false
var collider = result.collider
if collider is StructuralPiece and collider.is_pressurized:
# This ray hit a valid, pressurized wall. OK.
continue
else:
# This ray hit something, but it's not a pressurized piece, so it's a leak.
print("Leak found: Ray hit non-pressurized piece: ", collider.name)
leak_position = ray_start
return false
print("Module is successfully sealed.")
return true
func get_leak_position() -> Vector2:
return leak_position