Randoloot/scripts/game/ship_controller.gd

198 lines
7.2 KiB
GDScript

extends CharacterBody2D
const DEFAULT_SHIP_TEXTURE := "res://assets/Ships/Spaceship_0.png"
const IMPACT_EFFECT_SCENE := preload("res://scenes/game/impact_effect.tscn")
@export var max_speed := 76.0
@export var reverse_max_speed := 210.0
@export var max_strafe_speed := 210.0
@export var thrust_acceleration := 90.0
@export var brake_acceleration := 540.0
@export var space_drag := 24.0
@export var max_turn_speed := 2.8
@export var turn_acceleration := 7.0
@export var turn_damping := 4.0
@export var base_max_hull := 100.0
@export var base_max_shields := 100.0
@export var base_max_energy := 100.0
@export var base_max_speed := 90.0
@export var base_energy_regeneration := 12.0
@export var base_shield_regeneration := 0.0
@export var max_hull := 100.0
@export var max_shields := 100.0
@export var max_energy := 100.0
var hull := 100.0
var shields := 100.0
var energy := 100.0
var energy_regeneration := 12.0
var shield_regeneration := 0.0
var angular_velocity := 0.0
var _sprite: Sprite2D
var _contact_damage_timer := 0.0
var _is_destroyed := false
@onready var _shield_visual: Node2D = $ShieldVisual
func apply_equipment_bonuses(bonuses: Dictionary) -> void:
max_hull = base_max_hull + float(bonuses.get("max_hull", 0.0))
max_shields = base_max_shields + float(bonuses.get("max_shields", 0.0))
max_energy = base_max_energy + float(bonuses.get("max_energy", 0.0))
max_speed = base_max_speed + float(bonuses.get("max_speed", 0.0))
energy_regeneration = base_energy_regeneration + float(bonuses.get("energy_regeneration", 0.0))
shield_regeneration = base_shield_regeneration + float(bonuses.get("shield_regeneration", 0.0))
reverse_max_speed = max_speed * 0.5
max_strafe_speed = max_speed * 0.5
hull = minf(hull, max_hull)
shields = minf(shields, max_shields)
energy = minf(energy, max_energy)
_update_shield_visual()
func _ready() -> void:
_ensure_input_action(&"ship_thrust", KEY_W)
_ensure_input_action(&"ship_brake", KEY_S)
_ensure_input_action(&"ship_turn_left", KEY_A)
_ensure_input_action(&"ship_turn_right", KEY_D)
_ensure_input_action(&"ship_strafe_left", KEY_Q)
_ensure_input_action(&"ship_strafe_right", KEY_E)
_sprite = $Sprite2D
var texture_path := GameState.selected_ship_path
if texture_path.is_empty() or not ResourceLoader.exists(texture_path):
texture_path = DEFAULT_SHIP_TEXTURE
var ship_texture := load(texture_path) as Texture2D
if ship_texture:
_sprite.texture = ship_texture
var largest_dimension := maxf(ship_texture.get_width(), ship_texture.get_height())
_sprite.scale = Vector2.ONE * (64.0 / largest_dimension)
_update_shield_visual()
func _ensure_input_action(action_name: StringName, physical_key: Key) -> void:
if not InputMap.has_action(action_name):
InputMap.add_action(action_name, 0.5)
var event := InputEventKey.new()
event.physical_keycode = physical_key
if not InputMap.action_has_event(action_name, event):
InputMap.action_add_event(action_name, event)
func take_damage(amount: float) -> void:
if _is_destroyed:
return
var remaining_damage := maxf(0.0, amount)
var shield_damage := minf(shields, remaining_damage)
shields -= shield_damage
remaining_damage -= shield_damage
hull = maxf(0.0, hull - remaining_damage)
if shield_damage > 0.0 and shields > 0.0:
_shield_visual.call("flash_hit")
_update_shield_visual()
if hull <= 0.0:
_destroy_ship()
func register_enemy_collision(enemy_position: Vector2) -> void:
if _is_destroyed or _contact_damage_timer > 0.0:
return
var push_direction := global_position - enemy_position
if push_direction.length_squared() < 0.001:
push_direction = -velocity if velocity.length_squared() > 0.001 else Vector2.UP.rotated(rotation)
push_direction = push_direction.normalized()
velocity = velocity.bounce(push_direction) * 0.55 + push_direction * 110.0
_clamp_velocity_to_max_speed()
_contact_damage_timer = 0.8
_spawn_hit_effect(global_position, Color(0.65, 0.9, 1.0))
take_damage(max_hull * 0.01)
func _spawn_hit_effect(at_position: Vector2, effect_color: Color) -> void:
var effect := IMPACT_EFFECT_SCENE.instantiate() as Node2D
effect.global_position = at_position
get_parent().add_child(effect)
effect.call("configure", false, effect_color)
func _update_shield_visual() -> void:
if is_instance_valid(_shield_visual):
_shield_visual.call("set_shield_values", shields, max_shields)
func _destroy_ship() -> void:
_is_destroyed = true
velocity = Vector2.ZERO
collision_layer = 0
collision_mask = 0
var tween := create_tween()
tween.tween_property(self, "modulate:a", 0.0, 1.1)
_restart_level_after_destruction()
func _restart_level_after_destruction() -> void:
await get_tree().create_timer(1.1).timeout
if is_inside_tree():
get_tree().reload_current_scene()
func _physics_process(delta: float) -> void:
if _is_destroyed:
return
_contact_damage_timer = maxf(0.0, _contact_damage_timer - delta)
energy = minf(max_energy, energy + energy_regeneration * delta)
if shield_regeneration > 0.0 and shields < max_shields:
var previous_shields := shields
shields = minf(max_shields, shields + shield_regeneration * delta)
if shields != previous_shields:
_update_shield_visual()
var turn_input := Input.get_axis("ship_turn_left", "ship_turn_right")
if not is_zero_approx(turn_input):
angular_velocity = move_toward(
angular_velocity,
turn_input * max_turn_speed,
turn_acceleration * delta
)
else:
angular_velocity = move_toward(angular_velocity, 0.0, turn_damping * delta)
rotation += angular_velocity * delta
var forward := Vector2.UP.rotated(rotation)
if Input.is_action_pressed("ship_brake"):
var forward_speed := velocity.dot(forward)
if forward_speed > 0.0:
velocity = velocity.move_toward(Vector2.ZERO, brake_acceleration * delta)
else:
velocity -= forward * thrust_acceleration * delta
var reverse_speed := velocity.dot(forward)
if reverse_speed < -reverse_max_speed:
velocity += forward * (-reverse_max_speed - reverse_speed)
elif Input.is_action_pressed("ship_thrust"):
velocity += forward * thrust_acceleration * delta
var forward_speed := velocity.dot(forward)
if forward_speed > max_speed:
velocity += forward * (max_speed - forward_speed)
var strafe_input := Input.get_axis("ship_strafe_left", "ship_strafe_right")
var right := Vector2.RIGHT.rotated(rotation)
if not is_zero_approx(strafe_input):
velocity += right * strafe_input * thrust_acceleration * delta
var strafe_speed := velocity.dot(right)
velocity += right * (clampf(strafe_speed, -max_strafe_speed, max_strafe_speed) - strafe_speed)
if (
not Input.is_action_pressed("ship_brake")
and not Input.is_action_pressed("ship_thrust")
and is_zero_approx(strafe_input)
):
velocity = velocity.move_toward(Vector2.ZERO, space_drag * delta)
_clamp_velocity_to_max_speed()
move_and_slide()
_check_enemy_collisions()
_clamp_velocity_to_max_speed()
func _clamp_velocity_to_max_speed() -> void:
var speed_squared := velocity.length_squared()
var max_speed_squared := max_speed * max_speed
if speed_squared > max_speed_squared:
velocity = velocity.normalized() * max_speed
func _check_enemy_collisions() -> void:
for collision_index in get_slide_collision_count():
var collision := get_slide_collision(collision_index)
var collider := collision.get_collider()
if collider is Node and collider.is_in_group("enemies"):
register_enemy_collision(collider.global_position)