weapon slots and loadout done
This commit is contained in:
+17
-1
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
uid://i5lxsp35533c
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://bcedvro1n83c0
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://ba5ums3xj42h
|
||||
Reference in New Issue
Block a user