WIP commit before reorg

This commit is contained in:
olof.pettersson
2025-09-22 13:41:08 +02:00
parent 81dfa6517b
commit 9670eeb6ac
9 changed files with 240 additions and 0 deletions

View File

@ -0,0 +1,79 @@
# 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

View File

@ -0,0 +1 @@
uid://bdctwdqx66bhp

View File

@ -0,0 +1,11 @@
[gd_scene load_steps=3 format=3 uid="uid://dla2m5j414d3c"]
[ext_resource type="Script" uid="uid://bdctwdqx66bhp" path="res://scenes/spaceship/hull_volume.gd" id="1_jlg8y"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_n63kp"]
[node name="HullVolume" type="Area2D"]
script = ExtResource("1_jlg8y")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("RectangleShape2D_n63kp")

View File

@ -0,0 +1,12 @@
[gd_scene load_steps=2 format=3 uid="uid://ds3qq4yg8y86y"]
[ext_resource type="Script" uid="uid://6co67nfy8ngb" path="res://scenes/spaceship/module_builder_controller.gd" id="1_b1h2b"]
[node name="Module" type="RigidBody2D"]
script = ExtResource("1_b1h2b")
[node name="StructuralContainer" type="Node2D" parent="."]
[node name="HullVolumeContainer" type="Node2D" parent="."]
[node name="AtmosphereVisualizer" type="Node2D" parent="."]

View File

@ -0,0 +1,113 @@
# module_builder_controller.gd
# The root script for a player-constructed ship module. This is a RigidBody2D that
# acts as a container for all the player-placed structural pieces.
class_name ModuleBuilderController
extends RigidBody2D
# These nodes should be children of this RigidBody2D in the scene tree.
@onready var structural_container: Node2D = $StructuralContainer
@onready var hull_volume_container: Node2D = $HullVolumeContainer
@onready var atmosphere_visualizer: Node2D = $AtmosphereVisualizer # For "gas leak" effects
enum ModuleState { IN_CONSTRUCTION, SEALED, PRESSURIZED, BREACHED }
var current_state: ModuleState = ModuleState.IN_CONSTRUCTION
# --- Public API for In-Game Construction ---
# Called by the player's construction tool to add a wall, plate, etc.
func add_structural_piece(piece_scene: PackedScene, pos: Vector2, rot: float):
var piece = piece_scene.instantiate() as Node2D
piece.position = pos
piece.rotation = rot
structural_container.add_child(piece)
# Every time the structure changes, we must update the module's physics.
_recalculate_physics_properties()
# Called by the player to define the area that should contain an atmosphere.
func add_hull_volume(volume_scene: PackedScene, pos: Vector2):
var volume = volume_scene.instantiate() as Node2D
volume.position = pos
hull_volume_container.add_child(volume)
# This function checks if every defined room (Hull Volume) is properly sealed.
func check_for_seal() -> bool:
if hull_volume_container.get_child_count() == 0:
print("Module integrity check failed: No hull volumes defined.")
return false
var all_sealed = true
for volume in hull_volume_container.get_children():
if volume is HullVolume:
if not volume.check_seal():
all_sealed = false
# The volume can be responsible for showing its own leak effect.
if atmosphere_visualizer.has_method("show_leak_at"):
atmosphere_visualizer.show_leak_at(volume.get_leak_position())
if all_sealed:
print("Module is sealed! Ready for pressurization and component installation.")
current_state = ModuleState.SEALED
return true
else:
print("One or more hull volumes reported a leak.")
current_state = ModuleState.IN_CONSTRUCTION
return false
# --- Internal Physics Management ---
# Recalculates the total mass, center of mass, and combines all collision shapes.
func _recalculate_physics_properties():
print("Recalculating physics properties...")
# 1. Sum masses and find a weighted center of mass
var total_mass: float = 0.0
var total_mass_pos = Vector2.ZERO
var all_structural_pieces = structural_container.get_children()
for piece in all_structural_pieces:
if piece is StructuralPiece:
total_mass += piece.piece_mass
# The position is relative to the structural_container.
total_mass_pos += piece.position * piece.piece_mass
if total_mass > 0:
var new_center_of_mass = total_mass_pos / total_mass
# We set the center of mass in the RigidBody2D.
self.set_center_of_mass(new_center_of_mass)
self.mass = total_mass
# --- Combine all collision shapes into one for the RigidBody ---
# This is a key part of your design, allowing for efficient physics calculations.
# Note: This is an expensive operation and should only be done on demand.
# Remove any existing collision shapes from this RigidBody2D
for child in get_children():
if child is CollisionShape2D or child is CollisionPolygon2D:
child.queue_free()
var shape = ConcavePolygonShape2D.new()
var all_points: PackedVector2Array = []
# Iterate through all structural pieces to get their collision polygons
for piece in all_structural_pieces:
if piece is StructuralPiece:
for child in piece.get_children():
if child is CollisionPolygon2D:
# Get the points in the structural piece's local space
var piece_points = child.polygon
var piece_xform = piece.transform
# Transform the points to the module's local space
for p in piece_points:
all_points.append(piece_xform.xform(p))
shape.set_segments(all_points)
var new_collision_shape = CollisionShape2D.new()
new_collision_shape.shape = shape
add_child(new_collision_shape)
print("Physics Recalculated: Total Mass: %.2f kg" % total_mass)
else:
print("No structural pieces found. Physics properties not updated.")

View File

@ -0,0 +1 @@
uid://6co67nfy8ngb

View File

@ -0,0 +1,16 @@
# structural_piece.gd
# A script for the individual, player-placeable pieces like walls, floors, and girders.
class_name StructuralPiece
extends StaticBody2D
# The mass this piece contributes to its parent module.
@export var piece_mass: float = 10.0
# Does this piece block atmosphere? (e.g., a hull plate would, a girder would not).
@export var is_pressurized: bool = true
# The health of this specific piece.
@export var health: float = 100.0
func get_mass() -> float:
return piece_mass

View File

@ -0,0 +1 @@
uid://b7f8x2qimvn37

View File

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://ds4eilbvihjy7"]
[ext_resource type="Script" uid="uid://b7f8x2qimvn37" path="res://scenes/spaceship/structural_piece.gd" id="1_6jsoj"]
[node name="StructuralPiece" type="StaticBody2D"]
script = ExtResource("1_6jsoj")