80 lines
3.0 KiB
GDScript
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
|