weapon slots and loadout done

This commit is contained in:
bee
2026-07-07 23:04:20 +02:00
parent a40d0bfdd3
commit c4f2c0a202
21 changed files with 227 additions and 3 deletions
+17 -1
View File
@@ -7,10 +7,21 @@ extends FloatingBody
@export var throttle_response := 1.5 # how fast the engine spools up/down
@export var rudder_strength := 2.5 # turning torque at speed
@export var keel_grip := 3.0 # resistance to sliding sideways
@export var loadout: Dictionary[StringName, WeaponDef] = {}
var desired_throttle := 0.0 # move_toward target for actual throttle
var steer_input := 0.0
var throttle := 0.0
var throttle := 0.0 # current throttle state of ship
var _slots: Array[WeaponSlot] = []
func _ready() -> void:
super()
for child in get_children():
if child is WeaponSlot:
_slots.append(child)
var def: WeaponDef = loadout.get(child.name)
if def != null:
child.mount(def)
func _physics_process(delta: float) -> void:
super(delta)
@@ -40,3 +51,8 @@ func _physics_process(delta: float) -> void:
right = right.normalized()
var lateral_speed := linear_velocity.dot(right)
apply_central_force(-right * lateral_speed * keel_grip * mass * submerged_ratio)
func fire_weapons() -> void:
for slot in _slots:
if slot.weapon != null:
slot.weapon.try_fire()
+1 -1
View File
@@ -24,7 +24,7 @@ extends RigidBody3D
var _probe_offsets: Array[Vector3] = [] # local space
var _ocean: Ocean
var _probes: Array[Marker3D] = []
# var _probes: Array[Marker3D] = [] TODO: Probably unused
var _gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
var submerged_ratio := 0.0
+4
View File
@@ -31,6 +31,7 @@ extends Node
var desired_throttle := 0.0
func _ready() -> void:
if current_controlled != null: # Only happens on intial loading if boat already moves
desired_throttle = current_controlled.throttle
@@ -52,6 +53,9 @@ func _process(delta: float) -> void:
current_controlled.desired_throttle = desired_throttle
current_controlled.steer_input = steer_input
if Input.is_action_pressed("fire"):
current_controlled.fire_weapons()
func _possess() -> void:
camera_rig.target = current_controlled
world_manager.focus = current_controlled
+31
View File
@@ -0,0 +1,31 @@
class_name Projectile
extends RigidBody3D
var damage := 0.0 # set by launch(), read by whatever we hit
var _time_left := 10.0
func _ready() -> void:
add_to_group("shift_with_origin")
body_entered.connect(_on_body_entered)
## Call after add_child(). Configures this projectile from a def and sends it flying.
func launch(def: AmmoDef, muzzle: Transform3D, speed_scale := 1.0) -> void:
damage = def.damage
gravity_scale = def.gravity_scale
_time_left = def.lifetime
global_transform = muzzle
linear_velocity = -muzzle.basis.z * def.muzzle_speed * speed_scale
reset_physics_interpolation()
func _physics_process(delta: float) -> void:
if Vector3.UP.cross(linear_velocity).length_squared() > 0.01:
look_at(global_position + linear_velocity)
_time_left -= delta
if _time_left <= 0.0:
queue_free()
func _on_body_entered(body: Node) -> void:
if body.has_method("take_damage"):
body.take_damage(damage)
queue_free()
+1
View File
@@ -0,0 +1 @@
uid://i5lxsp35533c
+11
View File
@@ -0,0 +1,11 @@
class_name AmmoDef
extends Resource
## Data-only description of a weapon type. Never mutated at runtime.
@export var display_name := "Ammo Shell"
@export var projectile_scene: PackedScene
@export var damage := 10.0
@export var muzzle_speed := 30.0 # m/s, imparted at spawn
@export var gravity_scale := 1.0 # 1.0 = arcs like a real cannonball
@export var lifetime := 8.0 # seconds before self-despawn
@@ -0,0 +1 @@
uid://k4add7rmdxw3
@@ -0,0 +1,15 @@
class_name WeaponDef
extends Resource
## Data-only description of a weapon type. Never mutated at runtime.
enum Size {ANCILLIARY, SMALL, MEDIUM, LARGE, HUGE}
enum Type {BALLISTIC, ENERGY, MISSILE, TORPEDO}
@export var display_name := "Weapon Cannon"
@export var weapon_scene: PackedScene # the mounted body — consumed by slots in Part 3
@export var ammo: AmmoDef # what it fires
@export var fire_interval := 1.5 # seconds between shots
@export var muzzle_speed_scale := 1.0 # multiplier over ammo.muzzle_speed (barrel length)
@export var size := Size.MEDIUM
@export var type := Type.BALLISTIC
@@ -0,0 +1 @@
uid://vccbk7tbujhn
+31
View File
@@ -0,0 +1,31 @@
class_name Weapon
extends Node3D
@export var def: WeaponDef
var shooter: PhysicsBody3D # Resolved in _ready(): nearest ancestor body
var _cooldown := 0.0
@onready var _muzzle: Marker3D = %Muzzle
func _ready() -> void:
# Fallback: nearest ancestor body is the shooter (hand-placed weapons).
var n := get_parent()
while shooter == null and n != null:
shooter = n as PhysicsBody3D
n = n.get_parent()
func _physics_process(delta: float) -> void:
_cooldown = maxf(_cooldown - delta, 0.0)
func try_fire() -> bool:
if _cooldown > 0.0 or def == null or def.ammo == null:
return false
_cooldown = def.fire_interval
var p: Projectile = def.ammo.projectile_scene.instantiate()
get_tree().current_scene.add_child(p)
if shooter != null:
p.add_collision_exception_with(shooter)
p.launch(def.ammo, _muzzle.global_transform, def.muzzle_speed_scale)
return true
+1
View File
@@ -0,0 +1 @@
uid://bcedvro1n83c0
+25
View File
@@ -0,0 +1,25 @@
class_name WeaponSlot
extends Node3D
@export var allowed_sizes: Array[WeaponDef.Size] = [WeaponDef.Size.MEDIUM]
@export var allowed_types: Array[WeaponDef.Type] = [WeaponDef.Type.BALLISTIC]
var weapon: Weapon = null # currently mounted, if any
func can_mount(def: WeaponDef) -> bool:
return def != null and def.weapon_scene != null \
and def.size in allowed_sizes and def.type in allowed_types
func mount(def: WeaponDef) -> bool:
if not can_mount(def):
return false
unmount()
weapon = def.weapon_scene.instantiate()
weapon.def = def
add_child(weapon)
return true
func unmount() -> void:
if weapon != null:
weapon.queue_free()
weapon = null
+1
View File
@@ -0,0 +1 @@
uid://ba5ums3xj42h