130 lines
3.7 KiB
GDScript
130 lines
3.7 KiB
GDScript
class_name StructuralPiece extends ShipPiece
|
|
|
|
# The definition of this piece.
|
|
@export var structure_data: StructureData
|
|
|
|
# Track who we are welded to so we don't double-weld
|
|
var connected_neighbors: Array[StructuralPiece] = []
|
|
var mount_areas: Array[PieceMount] = []
|
|
|
|
# Flag to distinguish between a preview piece (no physics) and a placed piece
|
|
var is_preview: bool = false:
|
|
set(value):
|
|
is_preview = value
|
|
_update_preview_visuals()
|
|
|
|
func _init():
|
|
base_mass = 50.0
|
|
physics_mode = PhysicsMode.INDEPENDENT
|
|
auto_proxy_gravity = true
|
|
|
|
func _ready():
|
|
super._ready()
|
|
|
|
if is_preview:
|
|
collision_layer = 0
|
|
collision_mask = 0
|
|
return
|
|
|
|
sleeping = false
|
|
can_sleep = false
|
|
|
|
# If we have data, generate the mount points
|
|
if structure_data:
|
|
_generate_mount_detectors()
|
|
|
|
# Attempt to weld to anything we are already touching
|
|
_initial_weld_scan()
|
|
|
|
func _generate_mount_detectors():
|
|
for i in range(structure_data.mounts.size()):
|
|
var mount_def = structure_data.mounts[i]
|
|
|
|
var area = PieceMount.new() # Uses the class_name
|
|
area.name = "Mount_%d" % i
|
|
area.mount_type = mount_def.get("type", 0) # Set the type!
|
|
add_child(area)
|
|
|
|
# Position
|
|
area.position = mount_def.get("position", Vector3.ZERO)
|
|
|
|
# Orientation: Align Area -Z with Mount Normal
|
|
var normal = mount_def.get("normal", Vector3.BACK)
|
|
var up = mount_def.get("up", Vector3.UP)
|
|
|
|
if normal.cross(up).is_zero_approx():
|
|
up = Vector3.RIGHT if abs(normal.y) > 0.9 else Vector3.UP
|
|
|
|
if normal.length_squared() > 0.01:
|
|
area.transform.basis = Basis.looking_at(normal, up)
|
|
|
|
# Shape
|
|
var shape = CollisionShape3D.new()
|
|
var sphere = SphereShape3D.new()
|
|
sphere.radius = 0.15
|
|
shape.shape = sphere
|
|
area.add_child(shape)
|
|
|
|
# Layer setup is handled in PieceMount._ready() now, or explicit here:
|
|
area.collision_layer = 1 << 14
|
|
area.collision_mask = 1 << 14
|
|
area.monitoring = true
|
|
area.monitorable = true
|
|
|
|
mount_areas.append(area)
|
|
|
|
func try_weld():
|
|
_scan_and_weld_neighbors()
|
|
|
|
func _initial_weld_scan():
|
|
await get_tree().physics_frame
|
|
await get_tree().physics_frame
|
|
if not is_instance_valid(self): return
|
|
_scan_and_weld_neighbors()
|
|
|
|
func _scan_and_weld_neighbors():
|
|
for my_area in mount_areas:
|
|
for other_area in my_area.get_overlapping_areas():
|
|
var other_piece = other_area.get_parent()
|
|
if other_piece is StructuralPiece and other_piece != self:
|
|
# Here we could check mount compatibility (types, alignment)
|
|
if not other_piece in connected_neighbors:
|
|
_create_weld_to(other_piece)
|
|
|
|
func _create_weld_to(neighbor: StructuralPiece):
|
|
var joint = Generic6DOFJoint3D.new()
|
|
_lock_joint_axis(joint)
|
|
add_child(joint)
|
|
|
|
# Position joint halfway between pieces
|
|
joint.global_position = (self.global_position + neighbor.global_position) / 2.0
|
|
|
|
joint.node_a = self.get_path()
|
|
joint.node_b = neighbor.get_path()
|
|
|
|
connected_neighbors.append(neighbor)
|
|
neighbor.connected_neighbors.append(self)
|
|
|
|
func _lock_joint_axis(joint: Generic6DOFJoint3D):
|
|
for axis in [Generic6DOFJoint3D.PARAM_LINEAR_LOWER_LIMIT, Generic6DOFJoint3D.PARAM_LINEAR_UPPER_LIMIT]:
|
|
joint.set_param_x(axis, 0.0)
|
|
joint.set_param_y(axis, 0.0)
|
|
joint.set_param_z(axis, 0.0)
|
|
for axis in [Generic6DOFJoint3D.PARAM_ANGULAR_LOWER_LIMIT, Generic6DOFJoint3D.PARAM_ANGULAR_UPPER_LIMIT]:
|
|
joint.set_param_x(axis, 0.0)
|
|
joint.set_param_y(axis, 0.0)
|
|
joint.set_param_z(axis, 0.0)
|
|
|
|
func _update_preview_visuals():
|
|
var mesh_instance = find_child("MeshInstance3D")
|
|
if mesh_instance and mesh_instance.mesh:
|
|
var mat = StandardMaterial3D.new()
|
|
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
|
mat.albedo_color = Color(0.5, 0.8, 1.0, 0.5)
|
|
mesh_instance.material_override = mat
|
|
|
|
func _exit_tree():
|
|
for neighbor in connected_neighbors:
|
|
if is_instance_valid(neighbor):
|
|
neighbor.connected_neighbors.erase(self)
|