117 lines
5.3 KiB
GDScript
117 lines
5.3 KiB
GDScript
extends Node2D
|
|
|
|
const PROJECTILE := preload("res://scenes/game/weapon_projectile.tscn")
|
|
const FIRE_INTERVAL := 0.25
|
|
const HULL_TEXTURE := preload("res://assets/Ships/combat_drone.png")
|
|
var mother: CharacterBody2D
|
|
var formation_index := 0
|
|
var damage_per_shot := 1.0
|
|
var remaining := 10.0
|
|
var _fire_timer := 0.0
|
|
var _elapsed := 0.0
|
|
var _flash := 0.0
|
|
var velocity := Vector2.ZERO
|
|
var _turn_velocity := 0.0
|
|
const FORMATION := [Vector2(0, -110), Vector2(-35, -78), Vector2(35, -78)]
|
|
|
|
func _ready() -> void:
|
|
add_to_group("player_combat_drones")
|
|
z_index = 4
|
|
scale = Vector2.ONE * 0.5
|
|
rotation = mother.rotation
|
|
velocity = mother.velocity
|
|
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
|
_fire_timer = formation_index * FIRE_INTERVAL / 3.0
|
|
queue_redraw()
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
remaining -= delta
|
|
if remaining <= 0.0 or not is_instance_valid(mother) or bool(mother.get("_is_destroyed")) or bool(mother.get("is_docked")):
|
|
queue_free()
|
|
return
|
|
_elapsed += delta
|
|
_flash = maxf(0.0, _flash - delta)
|
|
_fire_timer = maxf(0.0, _fire_timer - delta)
|
|
var target := _find_target()
|
|
var destination := formation_position()
|
|
var escort_velocity := mother.velocity
|
|
if target != null:
|
|
var anchor := mother.global_position.lerp(target.global_position, 0.45)
|
|
destination = anchor + Vector2.from_angle(TAU * formation_index / 3.0 + _elapsed * 0.7) * 85.0
|
|
escort_velocity *= 0.55
|
|
else:
|
|
# Rotating formation slots have tangential velocity as the mother turns.
|
|
var offset := destination - mother.global_position
|
|
escort_velocity += Vector2(-offset.y, offset.x) * float(mother.get("angular_velocity"))
|
|
var error := destination - global_position
|
|
var correction := (error * 3.0).limit_length(260.0)
|
|
var desired_velocity := (escort_velocity + correction).limit_length(maxf(360.0, mother.velocity.length() + 200.0))
|
|
velocity = velocity.move_toward(desired_velocity, maxf(650.0, mother.velocity.length()) * delta)
|
|
global_position += velocity * delta
|
|
var desired_rotation := mother.rotation
|
|
if target != null:
|
|
var aim := target.global_position - global_position
|
|
desired_rotation = aim.angle() + PI * 0.5
|
|
if _fire_timer <= 0.0 and aim.length() <= 650.0 and _clear_shot(target):
|
|
_fire(target)
|
|
_fire_timer = FIRE_INTERVAL
|
|
elif error.length() > 55.0 and velocity.length() > 30.0:
|
|
desired_rotation = velocity.angle() + PI * 0.5
|
|
var angle_error := angle_difference(rotation, desired_rotation)
|
|
_turn_velocity = move_toward(_turn_velocity, clampf(angle_error * 6.0, -4.5, 4.5), 14.0 * delta)
|
|
var turn := _turn_velocity * delta
|
|
if absf(turn) >= absf(angle_error) and signf(turn) == signf(angle_error):
|
|
rotation = desired_rotation
|
|
_turn_velocity = 0.0
|
|
else:
|
|
rotation += turn
|
|
queue_redraw()
|
|
|
|
func formation_position() -> Vector2:
|
|
return mother.global_position + FORMATION[clampi(formation_index, 0, 2)].rotated(mother.rotation)
|
|
|
|
func _find_target() -> Node2D:
|
|
var closest: Node2D
|
|
var distance := 800.0
|
|
for enemy in get_tree().get_nodes_in_group("enemies"):
|
|
if enemy.is_queued_for_deletion() or float(enemy.get("hull")) <= 0.0:
|
|
continue
|
|
var candidate := global_position.distance_to(enemy.global_position)
|
|
if candidate < distance and mother.global_position.distance_to(enemy.global_position) < 1000.0:
|
|
distance = candidate
|
|
closest = enemy
|
|
return closest
|
|
|
|
func _clear_shot(target: Node2D) -> bool:
|
|
var query := PhysicsRayQueryParameters2D.create(global_position, target.global_position, 13)
|
|
var hit := get_world_2d().direct_space_state.intersect_ray(query)
|
|
return hit.is_empty() or hit.get("collider") == target
|
|
|
|
func _fire(target: Node2D) -> void:
|
|
var direction := (target.global_position - global_position).normalized()
|
|
var bullet := PROJECTILE.instantiate()
|
|
bullet.call("configure", global_position + direction * 9.0, direction, ItemDefinition.WeaponKind.MACHINE_GUN, 1100.0, damage_per_shot, 650.0)
|
|
get_parent().add_child(bullet)
|
|
_flash = 0.06
|
|
mother.get_node("ExtensionSystems").call("break_cloak_on_fire")
|
|
|
|
func _draw() -> void:
|
|
modulate.a = minf(clampf(_elapsed / 0.2, 0.0, 1.0), clampf(remaining / 0.6, 0.0, 1.0))
|
|
var flicker := sin(_elapsed * 33.0 + formation_index * 2.1)
|
|
for x in [-5.5, 5.5]:
|
|
var exhaust := Vector2(x, 19.5)
|
|
draw_circle(exhaust + Vector2(0, 5), 11.0 + flicker, Color(0.08, 0.65, 1.0, 0.08))
|
|
draw_circle(exhaust + Vector2(0, 3), 6.0, Color(0.12, 0.8, 1.0, 0.18))
|
|
draw_colored_polygon(PackedVector2Array([exhaust + Vector2(-3, 0), exhaust + Vector2(0, 15 + flicker * 2), exhaust + Vector2(3, 0)]), Color(0.15, 0.72, 1.0, 0.65))
|
|
draw_colored_polygon(PackedVector2Array([exhaust + Vector2(-1.4, 0), exhaust + Vector2(0, 8 + flicker), exhaust + Vector2(1.4, 0)]), Color("c8fbff"))
|
|
var hull_size := HULL_TEXTURE.get_size() * (60.0 / HULL_TEXTURE.get_height())
|
|
var hull_rect := Rect2(-hull_size * 0.5, hull_size)
|
|
draw_texture_rect(HULL_TEXTURE, Rect2(hull_rect.position + Vector2(3, 5), hull_size), false, Color(0, 0, 0, 0.4))
|
|
draw_texture_rect(HULL_TEXTURE, hull_rect, false)
|
|
if _flash > 0.0:
|
|
for x in [-5.5, 5.5]:
|
|
var muzzle := Vector2(x, -20)
|
|
draw_circle(muzzle, 8.0, Color(1.0, 0.6, 0.18, 0.15))
|
|
draw_colored_polygon(PackedVector2Array([muzzle + Vector2(-3, 0), muzzle + Vector2(-1.5, -4), muzzle + Vector2(0, -12), muzzle + Vector2(1.5, -4), muzzle + Vector2(3, 0)]), Color("ffbd68"))
|
|
draw_line(muzzle, muzzle + Vector2(0, -7), Color("fff2cb"), 2.0, true)
|