149 lines
5.4 KiB
GDScript
149 lines
5.4 KiB
GDScript
extends Node
|
|
|
|
const PROJECTILE_SCENE := preload("res://scenes/game/weapon_projectile.tscn")
|
|
const RAY_COLLISION_MASK := 5 # World geometry and enemies on physics layer 3.
|
|
|
|
@onready var ship: CharacterBody2D = get_parent()
|
|
@onready var inventory: ShipInventory = ship.get_node("ShipInventory")
|
|
|
|
var _shot_timers := [0.0, 0.0]
|
|
var _laser_pulse_timers := [0.0, 0.0]
|
|
var _laser_lines: Array[Line2D] = []
|
|
var _fire_buttons_down := [false, false]
|
|
var _fire_just_pressed := [false, false]
|
|
|
|
func _ready() -> void:
|
|
_ensure_input_action(&"ship_fire_primary", MOUSE_BUTTON_LEFT)
|
|
_ensure_input_action(&"ship_fire_secondary", MOUSE_BUTTON_RIGHT)
|
|
for slot_index in 2:
|
|
var beam := Line2D.new()
|
|
beam.width = 4.0
|
|
beam.default_color = Color(0.35, 0.9, 1.0, 0.92)
|
|
beam.z_index = 20
|
|
beam.visible = false
|
|
ship.get_parent().call_deferred("add_child", beam)
|
|
_laser_lines.append(beam)
|
|
|
|
func _ensure_input_action(action_name: StringName, button: MouseButton) -> void:
|
|
if not InputMap.has_action(action_name):
|
|
InputMap.add_action(action_name, 0.5)
|
|
var event := InputEventMouseButton.new()
|
|
event.button_index = button
|
|
if not InputMap.action_has_event(action_name, event):
|
|
InputMap.action_add_event(action_name, event)
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if not event is InputEventMouseButton:
|
|
return
|
|
var slot_index := -1
|
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
|
slot_index = 0
|
|
elif event.button_index == MOUSE_BUTTON_RIGHT:
|
|
slot_index = 1
|
|
if slot_index < 0:
|
|
return
|
|
_fire_buttons_down[slot_index] = event.pressed
|
|
if event.pressed:
|
|
_fire_just_pressed[slot_index] = true
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
var mouse_over_ui := ship.get_viewport().gui_get_hovered_control() != null
|
|
for slot_index in 2:
|
|
_shot_timers[slot_index] = maxf(0.0, _shot_timers[slot_index] - delta)
|
|
_laser_pulse_timers[slot_index] = maxf(0.0, _laser_pulse_timers[slot_index] - delta)
|
|
var action_name := "ship_fire_primary" if slot_index == 0 else "ship_fire_secondary"
|
|
var is_pressed: bool = not mouse_over_ui and (
|
|
Input.is_action_pressed(action_name) or _fire_buttons_down[slot_index]
|
|
)
|
|
var is_just_pressed: bool = not mouse_over_ui and (
|
|
Input.is_action_just_pressed(action_name) or _fire_just_pressed[slot_index]
|
|
)
|
|
var item: ItemInstance = inventory.weapon_slots[slot_index]
|
|
if not is_pressed or item == null or item.definition == null:
|
|
_laser_lines[slot_index].visible = (
|
|
item != null
|
|
and item.definition != null
|
|
and item.definition.weapon_kind == ItemDefinition.WeaponKind.LASER
|
|
and _laser_pulse_timers[slot_index] > 0.0
|
|
)
|
|
continue
|
|
|
|
var definition := item.definition
|
|
if definition.weapon_kind != ItemDefinition.WeaponKind.LASER:
|
|
_laser_pulse_timers[slot_index] = 0.0
|
|
_laser_lines[slot_index].visible = false
|
|
match definition.weapon_kind:
|
|
ItemDefinition.WeaponKind.LASER:
|
|
_laser_lines[slot_index].visible = _laser_pulse_timers[slot_index] > 0.0
|
|
_fire_laser(slot_index, definition, item.get_stat_total("damage"))
|
|
ItemDefinition.WeaponKind.MISSILE:
|
|
if (
|
|
is_just_pressed
|
|
and _shot_timers[slot_index] <= 0.0
|
|
and _consume_energy(definition.energy_cost)
|
|
):
|
|
_fire_projectile(definition, item.get_stat_total("damage"))
|
|
_shot_timers[slot_index] = definition.weapon_cooldown
|
|
_:
|
|
if _shot_timers[slot_index] <= 0.0 and _consume_energy(definition.energy_cost):
|
|
_fire_projectile(definition, item.get_stat_total("damage"))
|
|
_shot_timers[slot_index] = definition.fire_interval
|
|
for slot_index in 2:
|
|
_fire_just_pressed[slot_index] = false
|
|
|
|
func _fire_projectile(definition: ItemDefinition, projectile_damage: float) -> void:
|
|
var direction := _aim_direction()
|
|
var projectile := PROJECTILE_SCENE.instantiate()
|
|
projectile.call(
|
|
"configure",
|
|
ship.global_position + direction * 30.0,
|
|
direction,
|
|
definition.weapon_kind,
|
|
definition.projectile_speed,
|
|
projectile_damage,
|
|
definition.weapon_range
|
|
)
|
|
ship.get_parent().add_child(projectile)
|
|
|
|
func _fire_laser(slot_index: int, definition: ItemDefinition, beam_damage: float) -> void:
|
|
var line := _laser_lines[slot_index]
|
|
if _shot_timers[slot_index] > 0.0 or float(ship.get("energy")) < definition.energy_cost:
|
|
return
|
|
var direction := _aim_direction()
|
|
var endpoint := ship.global_position + direction * definition.weapon_range
|
|
var query := PhysicsRayQueryParameters2D.create(
|
|
ship.global_position,
|
|
endpoint,
|
|
RAY_COLLISION_MASK,
|
|
[ship.get_rid()]
|
|
)
|
|
var result := ship.get_world_2d().direct_space_state.intersect_ray(query)
|
|
if not result.is_empty():
|
|
endpoint = result["position"]
|
|
line.clear_points()
|
|
line.add_point(ship.global_position)
|
|
line.add_point(endpoint)
|
|
line.visible = true
|
|
if not _consume_energy(definition.energy_cost):
|
|
line.visible = false
|
|
return
|
|
if not result.is_empty():
|
|
_apply_damage(result.get("collider"), beam_damage)
|
|
_laser_pulse_timers[slot_index] = definition.laser_pulse_duration
|
|
_shot_timers[slot_index] = definition.fire_interval
|
|
|
|
func _aim_direction() -> Vector2:
|
|
var offset := ship.get_global_mouse_position() - ship.global_position
|
|
return offset.normalized() if offset.length_squared() > 0.001 else Vector2.UP.rotated(ship.rotation)
|
|
|
|
func _consume_energy(amount: float) -> bool:
|
|
var current_energy := float(ship.get("energy"))
|
|
if current_energy < amount:
|
|
return false
|
|
ship.set("energy", current_energy - amount)
|
|
return true
|
|
|
|
func _apply_damage(target: Variant, damage: float) -> void:
|
|
if target is Node and target.has_method("take_damage"):
|
|
target.call("take_damage", damage)
|