Skip to content

Commit

Permalink
Guided torpedoes
Browse files Browse the repository at this point in the history
  • Loading branch information
jspahrsummers authored Sep 8, 2024
2 parents 08c2f02 + f892da6 commit d1732b1
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 35 deletions.
2 changes: 2 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@ As mentioned before, the one stipulation is that these images aren’t to be use

[Laser Bullets Pack 2020](https://wenrexa.itch.io/laser2020) released by [Wenrexa](https://wenrexa.itch.io/), licensed under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/):
- blaster_bolt
- torpedo

[Digital sound effects](https://opengameart.org/content/63-digital-sound-effects-lasers-phasers-space-etc) created by [Kenney](https://www.kenney.nl/), licensed under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/):
- laser1
- laser4

[Xenon Star](https://kodiakgraphics.itch.io/xenon-star) created by [Kodiak Graphics](https://kodiakgraphics.itch.io/), purchased for use in this project:
Expand Down
1 change: 0 additions & 1 deletion mechanics/combat/weapons/blaster/blaster_bolt.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ linear_damp_mode = 1
angular_velocity = Vector3(3.63316e-14, 3.63316e-14, 3.63316e-14)
angular_damp_mode = 1
script = ExtResource("1_yrcn8")
lifetime_msec = null
damage = SubResource("Resource_a466u")
explosion = ExtResource("3_0y0wg")

Expand Down
43 changes: 41 additions & 2 deletions mechanics/combat/weapons/projectile.gd
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ class_name Projectile
## An explosion to instantiate upon collision.
@export var explosion: PackedScene

## The force (in N) with which this projectile follows its [member target].
##
## Set to 0 to disable target tracking.
@export var thrust_force: float

## The maximum turning rate, in rad/s, if this projectile has [member target] tracking.
##
## At the moment, this affects visuals only (not physics).
@export var turning_rate: float

## The [CombatObject] that was being targeted when this projectile was fired.
var target: CombatObject

## The [CombatObject] which launched this projectile.
var fired_by: CombatObject

Expand All @@ -18,14 +31,40 @@ var _spawn_time_msec: int
func _ready() -> void:
self._spawn_time_msec = Time.get_ticks_msec()

func _process(_delta: float) -> void:
if self.target:
self.target.hull.hull_destroyed.connect(_target_destroyed)

func _target_destroyed(_hull: Hull) -> void:
self.target = null

func _physics_process(_delta: float) -> void:
if Time.get_ticks_msec() - self._spawn_time_msec > self.lifetime_msec:
self.queue_free()
return

if not self.target or is_zero_approx(self.thrust_force):
return

# TODO: This is kinda EZ mode, because the projectile is not constrained by a particular rotation speed or velocity limiter.
var desired_direction := (self.target.global_transform.origin - self.global_transform.origin).normalized()
self.apply_central_force(desired_direction * self.thrust_force)

func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
if state.get_contact_count() == 0:
if state.get_contact_count() > 0:
self._explode_on_contact(state)

if not self.target or is_zero_approx(self.turning_rate):
return

var desired_basis := Basis.looking_at(state.linear_velocity.normalized(), Vector3.UP)
var desired_rotation := desired_basis.get_rotation_quaternion()
var current_rotation := state.transform.basis.get_rotation_quaternion()

var angle_delta := current_rotation.angle_to(desired_rotation)
var slerp_weight := clampf(self.turning_rate * state.step / angle_delta, 0.0, 1.0)
state.transform.basis = state.transform.basis.slerp(desired_basis, slerp_weight)

func _explode_on_contact(state: PhysicsDirectBodyState3D) -> void:
var explosion_instance: AnimatedSprite3D = self.explosion.instantiate()
self.add_sibling(explosion_instance)
explosion_instance.global_position = self.global_position
Expand Down
Binary file added mechanics/combat/weapons/torpedo/laser1.mp3
Binary file not shown.
19 changes: 19 additions & 0 deletions mechanics/combat/weapons/torpedo/laser1.mp3.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[remap]

importer="mp3"
type="AudioStreamMP3"
uid="uid://cb2xi2uin55r1"
path="res://.godot/imported/laser1.mp3-3344d66c1f8fd48ab53c33b3ac56e3c8.mp3str"

[deps]

source_file="res://mechanics/combat/weapons/torpedo/laser1.mp3"
dest_files=["res://.godot/imported/laser1.mp3-3344d66c1f8fd48ab53c33b3ac56e3c8.mp3str"]

[params]

loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
Binary file added mechanics/combat/weapons/torpedo/torpedo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions mechanics/combat/weapons/torpedo/torpedo.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://b6338fvv2c7c2"
path.s3tc="res://.godot/imported/torpedo.png-4a0a8d422c14cf953d57dd81ae28908c.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}

[deps]

source_file="res://mechanics/combat/weapons/torpedo/torpedo.png"
dest_files=["res://.godot/imported/torpedo.png-4a0a8d422c14cf953d57dd81ae28908c.s3tc.ctex"]

[params]

compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=1
roughness/src_normal=""
process/fix_alpha_border=false
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
12 changes: 12 additions & 0 deletions mechanics/combat/weapons/torpedo/torpedo.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[gd_resource type="Resource" script_class="Weapon" load_steps=3 format=3 uid="uid://x4cvy30r6rdc"]

[ext_resource type="Script" path="res://mechanics/combat/weapons/weapon.gd" id="1_l24ix"]
[ext_resource type="PackedScene" uid="uid://djo5waum4hiyn" path="res://mechanics/combat/weapons/torpedo/torpedo.tscn" id="1_xfgdh"]

[resource]
script = ExtResource("1_l24ix")
fire_interval = 1.0
fire_force = 6.0
power_consumption = 10.0
heat = 20.0
projectile = ExtResource("1_xfgdh")
71 changes: 71 additions & 0 deletions mechanics/combat/weapons/torpedo/torpedo.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
[gd_scene load_steps=10 format=3 uid="uid://djo5waum4hiyn"]

[ext_resource type="Script" path="res://mechanics/combat/weapons/projectile.gd" id="1_lgstg"]
[ext_resource type="Script" path="res://mechanics/combat/damage.gd" id="2_nykuf"]
[ext_resource type="PackedScene" uid="uid://cp0b07jvhi5a" path="res://fx/explosions/explosion001.tscn" id="3_kxh65"]
[ext_resource type="Texture2D" uid="uid://b6338fvv2c7c2" path="res://mechanics/combat/weapons/torpedo/torpedo.png" id="4_ej81m"]
[ext_resource type="AudioStream" uid="uid://cb2xi2uin55r1" path="res://mechanics/combat/weapons/torpedo/laser1.mp3" id="5_ayg2o"]

[sub_resource type="Resource" id="Resource_a466u"]
script = ExtResource("2_nykuf")
shield_damage = 100.0
hull_damage = 100.0
heat = 40.0

[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_5b1vh"]
radius = 0.33408
height = 1.10555

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_04jjf"]
shading_mode = 0
albedo_color = Color(0.359069, 0.359069, 0.359069, 1)

[sub_resource type="QuadMesh" id="QuadMesh_fxvid"]
material = SubResource("StandardMaterial3D_04jjf")
orientation = 1

[node name="Torpedo" type="RigidBody3D"]
collision_layer = 2
axis_lock_linear_y = true
axis_lock_angular_x = true
axis_lock_angular_y = true
axis_lock_angular_z = true
gravity_scale = 1.66533e-16
lock_rotation = true
continuous_cd = true
max_contacts_reported = 1
contact_monitor = true
linear_damp_mode = 1
angular_velocity = Vector3(3.63316e-14, 3.63316e-14, 3.63316e-14)
angular_damp_mode = 1
script = ExtResource("1_lgstg")
lifetime_msec = 10000
damage = SubResource("Resource_a466u")
explosion = ExtResource("3_kxh65")
thrust_force = 6.0
turning_rate = 3.0

[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(0.3, 0, -4.57379e-24, -4.57379e-24, -1.31134e-08, -0.3, 0, 0.3, -1.31134e-08, 0, 0, -0.486987)
shape = SubResource("CapsuleShape3D_5b1vh")

[node name="Sprite3D" type="Sprite3D" parent="."]
transform = Transform3D(-4.37114e-08, 3.48787e-16, 1, 3.48787e-16, 1, -3.48787e-16, -1, 3.48787e-16, -4.37114e-08, 0, 0, -0.510476)
gi_mode = 2
axis = 1
texture = ExtResource("4_ej81m")

[node name="OmniLight3D" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.08165e-12, 1, -0.510476)

[node name="MinimapIcon" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.510476)
layers = 2
cast_shadow = 0
gi_mode = 0
mesh = SubResource("QuadMesh_fxvid")

[node name="AudioStreamPlayer3D" type="AudioStreamPlayer3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.510476)
stream = ExtResource("5_ayg2o")
autoplay = true
9 changes: 5 additions & 4 deletions mechanics/combat/weapons/weapon_mount.gd
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ var battery: Battery
## The [HeatSink] to dump heat into.
var heat_sink: HeatSink

## The [CombatObject] controlling this weapon in combat.
var combat_object: CombatObject
## The [TargetingSystem] of the ship that has mounted this weapon.
var targeting_system: TargetingSystem

## Set to true when this weapon should automatically fire; set to false to stop firing.
var firing: bool = false:
Expand Down Expand Up @@ -66,9 +66,10 @@ func _physics_process(_delta: float) -> void:
return

var projectile_instance: Projectile = self.weapon.projectile.instantiate()
get_parent().add_sibling(projectile_instance)
projectile_instance.target = self.targeting_system.target
projectile_instance.fired_by = self.targeting_system.combat_object
projectile_instance.add_collision_exception_with(self._rigid_body)
projectile_instance.fired_by = self.combat_object
get_parent().add_sibling(projectile_instance)
projectile_instance.global_transform = self.global_transform
projectile_instance.linear_velocity = self._rigid_body.linear_velocity
projectile_instance.apply_central_impulse(projectile_instance.transform.basis * self.weapon.fire_force * Vector3.FORWARD)
Expand Down
20 changes: 20 additions & 0 deletions mechanics/outfitting/outfits/torpedo_launcher.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[gd_resource type="Resource" script_class="Outfit" load_steps=3 format=3 uid="uid://cyx3v8jagovnv"]

[ext_resource type="Script" path="res://mechanics/outfitting/outfit.gd" id="1_um0on"]
[ext_resource type="Resource" uid="uid://x4cvy30r6rdc" path="res://mechanics/combat/weapons/torpedo/torpedo.tres" id="2_c22kq"]

[resource]
script = ExtResource("1_um0on")
name = "Torpedo Launcher"
description = "Installed on less-maneuverable capital ships, torpedoes acquire and track their targets over longer distances. Although they are slower than many other weapons, torpedoes pack a significant punch, as long as your ship is well-equipped to discard the excess waste heat they generate."
mass = 10.0
price_in_credits = 20000.0
modified_cargo_capacity = 0.0
modified_passenger_capacity = 0
modified_fuel_capacity = -1.0
modified_hull_capacity = 0.0
modified_shield_capacity = 0.0
shield_recharge_multiplier = 1.0
modified_power_generation = 0.0
modified_heat_capacity = 0.0
weapon = ExtResource("2_c22kq")
15 changes: 9 additions & 6 deletions mechanics/physics/rigid_body_direction.gd
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,27 @@ class_name RigidBodyDirection
@export var spin_thruster: SpinThruster

## A vector representing the direction to rotate toward, or zero to stop rotating.
@export var direction: Vector3 = Vector3.ZERO:
var direction: Vector3 = Vector3.ZERO:
set(value):
direction = value

## The [RigidBody3D] to rotate.
@onready var _rigid_body := get_parent() as RigidBody3D

## The [Battery] to power the thruster from.
## An optional [Battery] to power the thruster from.
var battery: Battery

func _physics_process(delta: float) -> void:
if is_zero_approx(self.battery.power) or self.direction.is_zero_approx():
if self.direction.is_zero_approx():
return


if self.battery and is_zero_approx(self.battery.power):
return

var desired_basis := Basis.looking_at(self.direction)
if self._rigid_body.global_basis.is_equal_approx(desired_basis):
return

if not self._rigid_body.angular_velocity.is_zero_approx():
self._rigid_body.angular_velocity = Vector3.ZERO

Expand All @@ -35,7 +38,7 @@ func _physics_process(delta: float) -> void:
# this function, also taking into account that the error will be bounded by
# `delta`.
var desired_power := self.spin_thruster.power_consumption_rate * delta
var consumed_power := self.battery.consume_up_to(desired_power)
var consumed_power := self.battery.consume_up_to(desired_power) if self.battery else desired_power
var consumed_percentage := consumed_power / desired_power

self._rigid_body.global_basis = self._rigid_body.global_basis.slerp(desired_basis, self.spin_thruster.turning_rate * delta * consumed_percentage)
38 changes: 21 additions & 17 deletions mechanics/physics/rigid_body_thruster.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class_name RigidBodyThruster

@export var thruster: Thruster

## An audio clip to play while the thruster is active.
## An optional audio clip to play while the thruster is active.
##
## This audio stream should be a looping sound. Rather than restarting from the beginning each time the thruster is used, the audio stream is paused and resumed, and evenutally loops.
@export var thruster_audio: AudioStreamPlayer3D
Expand All @@ -20,10 +20,10 @@ class_name RigidBodyThruster
@export var animation_transition: Tween.TransitionType = Tween.TRANS_CUBIC
@export var animation_ease: Tween.EaseType = Tween.EASE_IN_OUT

## The [Battery] to power the thruster from.
## An optional [Battery] to power the thruster from.
var battery: Battery

## The [HeatSink] to dump heat to.
## An optional [HeatSink] to dump heat to.
var heat_sink: HeatSink

## The current level of throttle (where 1.0 is full throttle), which corresponds to the magnitude of the thrust to apply, as well as the amount of power to be consumed.
Expand Down Expand Up @@ -52,34 +52,38 @@ func _enter_tree() -> void:
var geometry_instance := self.get_child(child_index) as GeometryInstance3D
if not geometry_instance:
continue

self._animation_geometry.push_back(geometry_instance)

for geometry in self._animation_geometry:
geometry.transparency = 1.0

if self._tween:
self._tween.kill()
self._tween = null

self._target_transparency = 1.0

func _physics_process(delta: float) -> void:
if is_zero_approx(self.throttle) or is_zero_approx(self.battery.power):
self.thruster_audio.stream_paused = true
if is_zero_approx(self.throttle) or (self.battery and is_zero_approx(self.battery.power)):
if self.thruster_audio:
self.thruster_audio.stream_paused = true
self._animate_transparency(1.0)
return

if self.thruster_audio.stream_paused:
self.thruster_audio.stream_paused = false
if !self.thruster_audio.playing:
self.thruster_audio.play()


if self.thruster_audio:
if self.thruster_audio.stream_paused:
self.thruster_audio.stream_paused = false
if !self.thruster_audio.playing:
self.thruster_audio.play()

var desired_power := self.thruster.power_consumption_rate * self.throttle * delta
var power_consumed := self.battery.consume_up_to(desired_power)
var power_consumed := self.battery.consume_up_to(desired_power) if self.battery else desired_power
var magnitude := self.throttle * (power_consumed / desired_power)

self.heat_sink.heat += self.thruster.heat_generation_rate * magnitude * delta
if self.heat_sink:
self.heat_sink.heat += self.thruster.heat_generation_rate * magnitude * delta

self._animate_transparency(1.0 - magnitude)
self._rigid_body.apply_central_force(self._rigid_body.transform.basis * Vector3.FORWARD * self.thruster.thrust_force * magnitude)

Expand All @@ -101,6 +105,6 @@ func _animate_transparency(to_transparency: float) -> void:

for geometry in self._animation_geometry:
tween.tween_property(geometry, "transparency", to_transparency, duration)

self._tween = tween
self._target_transparency = to_transparency
Loading

0 comments on commit d1732b1

Please sign in to comment.