weapon slots and loadout done
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
Controller/possession refactor (input out of Boat).
|
DONE Controller/possession refactor (input out of Boat).
|
||||||
WeaponDef resource + WeaponSlot + one dumb gun that prints instead of shooting.
|
WeaponDef resource + WeaponSlot + one dumb gun that prints instead of shooting.
|
||||||
Projectile base (with group self-registration) + real firing.
|
Projectile base (with group self-registration) + real firing.
|
||||||
Loadout-driven mounting.
|
Loadout-driven mounting.
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
[gd_scene format=3 uid="uid://cieiwihia8t2w"]
|
[gd_scene format=3 uid="uid://cieiwihia8t2w"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://c0n1x050jvspr" path="res://scripts/boat.gd" id="1_7h74f"]
|
[ext_resource type="Script" uid="uid://c0n1x050jvspr" path="res://scripts/boat.gd" id="1_7h74f"]
|
||||||
|
[ext_resource type="Script" uid="uid://vccbk7tbujhn" path="res://scripts/resource_definitions/weapon_def.gd" id="2_anwow"]
|
||||||
|
[ext_resource type="Resource" uid="uid://cie5thl0u84ly" path="res://resources/weapons/mark12.tres" id="3_henu8"]
|
||||||
|
[ext_resource type="Script" uid="uid://ba5ums3xj42h" path="res://scripts/weapon_slot.gd" id="4_1jsln"]
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_crate"]
|
[sub_resource type="BoxMesh" id="BoxMesh_crate"]
|
||||||
size = Vector3(1, 1, 5)
|
size = Vector3(1, 1, 5)
|
||||||
@@ -16,6 +19,10 @@ size = Vector3(1, 1, 5)
|
|||||||
collision_layer = 2
|
collision_layer = 2
|
||||||
mass = 5.0
|
mass = 5.0
|
||||||
script = ExtResource("1_7h74f")
|
script = ExtResource("1_7h74f")
|
||||||
|
loadout = Dictionary[StringName, ExtResource("2_anwow")]({
|
||||||
|
&"BowMount": ExtResource("3_henu8"),
|
||||||
|
&"SternMount": ExtResource("3_henu8")
|
||||||
|
})
|
||||||
buoyancy = 4.0
|
buoyancy = 4.0
|
||||||
full_force_depth = 0.8
|
full_force_depth = 0.8
|
||||||
probe_extents = Vector3(0.35, 0, 2.25)
|
probe_extents = Vector3(0.35, 0, 2.25)
|
||||||
@@ -27,3 +34,11 @@ surface_material_override/0 = SubResource("Material_crate")
|
|||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=18200711]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=18200711]
|
||||||
shape = SubResource("BoxShape_crate")
|
shape = SubResource("BoxShape_crate")
|
||||||
|
|
||||||
|
[node name="BowMount" type="Node3D" parent="." unique_id=1196033377]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.56350744, -1.6800777)
|
||||||
|
script = ExtResource("4_1jsln")
|
||||||
|
|
||||||
|
[node name="SternMount" type="Node3D" parent="." unique_id=909881913]
|
||||||
|
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0.5398402, 2.0672112)
|
||||||
|
script = ExtResource("4_1jsln")
|
||||||
|
|||||||
@@ -74,3 +74,4 @@ current_controlled = NodePath("../Boat")
|
|||||||
world_manager = NodePath("../WorldManager")
|
world_manager = NodePath("../WorldManager")
|
||||||
camera_rig = NodePath("../CameraRig")
|
camera_rig = NodePath("../CameraRig")
|
||||||
hud = NodePath("../Hud")
|
hud = NodePath("../Hud")
|
||||||
|
throttle_ramp = null
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
[gd_scene format=3 uid="uid://d1fb1p83uhpwv"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://bcedvro1n83c0" path="res://scripts/weapon.gd" id="1_pko6q"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="BoxMesh_byv2x"]
|
||||||
|
size = Vector3(0.5, 0.5, 0.5)
|
||||||
|
|
||||||
|
[node name="Mark12" type="Node3D" unique_id=2057435618]
|
||||||
|
script = ExtResource("1_pko6q")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=522024219]
|
||||||
|
mesh = SubResource("BoxMesh_byv2x")
|
||||||
|
|
||||||
|
[node name="Muzzle" type="Marker3D" parent="." unique_id=471595781]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.2726481)
|
||||||
@@ -55,6 +55,11 @@ restart={
|
|||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
fire={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":49,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[layer_names]
|
[layer_names]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[gd_resource type="Resource" script_class="AmmoDef" format=3 uid="uid://c85q1rbyiayh1"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://3us2hr60a7ke" path="res://shell.tscn" id="1_upqqq"]
|
||||||
|
[ext_resource type="Script" uid="uid://k4add7rmdxw3" path="res://scripts/resource_definitions/ammo_def.gd" id="2_upqqq"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("2_upqqq")
|
||||||
|
projectile_scene = ExtResource("1_upqqq")
|
||||||
|
metadata/_custom_type_script = "uid://k4add7rmdxw3"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[gd_resource type="Resource" script_class="WeaponDef" format=3 uid="uid://cie5thl0u84ly"]
|
||||||
|
|
||||||
|
[ext_resource type="Resource" uid="uid://c85q1rbyiayh1" path="res://resources/ammo/5inch_shell.tres" id="1_q60cw"]
|
||||||
|
[ext_resource type="Script" uid="uid://vccbk7tbujhn" path="res://scripts/resource_definitions/weapon_def.gd" id="2_q60cw"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://d1fb1p83uhpwv" path="res://mark12.tscn" id="3_omejh"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("2_q60cw")
|
||||||
|
display_name = "Mark 12"
|
||||||
|
weapon_scene = ExtResource("3_omejh")
|
||||||
|
ammo = ExtResource("1_q60cw")
|
||||||
|
metadata/_custom_type_script = "uid://vccbk7tbujhn"
|
||||||
+17
-1
@@ -7,10 +7,21 @@ extends FloatingBody
|
|||||||
@export var throttle_response := 1.5 # how fast the engine spools up/down
|
@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 rudder_strength := 2.5 # turning torque at speed
|
||||||
@export var keel_grip := 3.0 # resistance to sliding sideways
|
@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 desired_throttle := 0.0 # move_toward target for actual throttle
|
||||||
var steer_input := 0.0
|
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:
|
func _physics_process(delta: float) -> void:
|
||||||
super(delta)
|
super(delta)
|
||||||
@@ -40,3 +51,8 @@ func _physics_process(delta: float) -> void:
|
|||||||
right = right.normalized()
|
right = right.normalized()
|
||||||
var lateral_speed := linear_velocity.dot(right)
|
var lateral_speed := linear_velocity.dot(right)
|
||||||
apply_central_force(-right * lateral_speed * keel_grip * mass * submerged_ratio)
|
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 _probe_offsets: Array[Vector3] = [] # local space
|
||||||
|
|
||||||
var _ocean: Ocean
|
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 _gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||||
var submerged_ratio := 0.0
|
var submerged_ratio := 0.0
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ extends Node
|
|||||||
|
|
||||||
var desired_throttle := 0.0
|
var desired_throttle := 0.0
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
if current_controlled != null: # Only happens on intial loading if boat already moves
|
if current_controlled != null: # Only happens on intial loading if boat already moves
|
||||||
desired_throttle = current_controlled.throttle
|
desired_throttle = current_controlled.throttle
|
||||||
@@ -52,6 +53,9 @@ func _process(delta: float) -> void:
|
|||||||
current_controlled.desired_throttle = desired_throttle
|
current_controlled.desired_throttle = desired_throttle
|
||||||
current_controlled.steer_input = steer_input
|
current_controlled.steer_input = steer_input
|
||||||
|
|
||||||
|
if Input.is_action_pressed("fire"):
|
||||||
|
current_controlled.fire_weapons()
|
||||||
|
|
||||||
func _possess() -> void:
|
func _possess() -> void:
|
||||||
camera_rig.target = current_controlled
|
camera_rig.target = current_controlled
|
||||||
world_manager.focus = 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
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
[gd_scene format=3 uid="uid://3us2hr60a7ke"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://i5lxsp35533c" path="res://scripts/projectile.gd" id="1_6fuaf"]
|
||||||
|
|
||||||
|
[sub_resource type="CylinderMesh" id="CylinderMesh_byv2x"]
|
||||||
|
top_radius = 0.1
|
||||||
|
bottom_radius = 0.1
|
||||||
|
height = 0.5
|
||||||
|
|
||||||
|
[sub_resource type="CylinderShape3D" id="CylinderShape3D_byv2x"]
|
||||||
|
height = 0.5
|
||||||
|
radius = 0.1
|
||||||
|
|
||||||
|
[node name="Shell" type="RigidBody3D" unique_id=893187332]
|
||||||
|
collision_layer = 8
|
||||||
|
collision_mask = 7
|
||||||
|
continuous_cd = true
|
||||||
|
contact_monitor = true
|
||||||
|
max_contacts_reported = 1
|
||||||
|
script = ExtResource("1_6fuaf")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1876944354]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0, 0)
|
||||||
|
mesh = SubResource("CylinderMesh_byv2x")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1799909098]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0, 0)
|
||||||
|
shape = SubResource("CylinderShape3D_byv2x")
|
||||||
Reference in New Issue
Block a user