Tune and optimize boss encounter
This commit is contained in:
parent
40f284dcdd
commit
68985316e5
16 changed files with 316 additions and 88 deletions
|
|
@ -12,27 +12,29 @@ extends Node2D
|
|||
# the fleet's position while charging.
|
||||
#
|
||||
# Damage: every ship in group "player_ships" whose perpendicular distance
|
||||
# to the beam segment is within beam_half_width, AND whose projection along
|
||||
# the beam falls inside [0, beam_range], takes Ship.take_damage(damage) -
|
||||
# same all-or-nothing shield semantics as every other damage source in this
|
||||
# game (Ship.take_damage already handles the absorb_hit check itself).
|
||||
# to the beam is within beam_half_width takes Ship.take_damage(damage). The
|
||||
# ordinary mothership beam remains a finite segment; bosses opt into a ray
|
||||
# with no upper distance bound. Ship.take_damage handles shields itself.
|
||||
|
||||
signal finished
|
||||
|
||||
@export var charge_time: float = 2.5
|
||||
@export var beam_range: float = 1200.0
|
||||
@export var infinite_range: bool = false
|
||||
@export var beam_half_width: float = 35.0
|
||||
@export var damage: int = 3
|
||||
@export var color: Color = Color(0.55, 0.05, 0.85)
|
||||
|
||||
enum Phase { CHARGING, FLASH }
|
||||
const FLASH_DURATION: float = 0.25
|
||||
const REDRAW_INTERVAL: float = 1.0 / 30.0
|
||||
|
||||
var phase: int = Phase.CHARGING
|
||||
var origin: Vector2 = Vector2.ZERO
|
||||
var aim_direction: Vector2 = Vector2.RIGHT
|
||||
var charge_left: float = 0.0
|
||||
var flash_time: float = 0.0
|
||||
var redraw_left: float = 0.0
|
||||
|
||||
|
||||
func launch(from: Vector2, toward: Vector2) -> void:
|
||||
|
|
@ -46,15 +48,20 @@ func launch(from: Vector2, toward: Vector2) -> void:
|
|||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
redraw_left -= delta
|
||||
match phase:
|
||||
Phase.CHARGING:
|
||||
charge_left -= delta
|
||||
if charge_left <= 0.0:
|
||||
_fire()
|
||||
queue_redraw()
|
||||
if redraw_left <= 0.0:
|
||||
redraw_left = REDRAW_INTERVAL
|
||||
queue_redraw()
|
||||
Phase.FLASH:
|
||||
flash_time -= delta
|
||||
queue_redraw()
|
||||
if redraw_left <= 0.0:
|
||||
redraw_left = REDRAW_INTERVAL
|
||||
queue_redraw()
|
||||
if flash_time <= 0.0:
|
||||
finished.emit()
|
||||
queue_free()
|
||||
|
|
@ -66,16 +73,19 @@ func _fire() -> void:
|
|||
for ship in get_tree().get_nodes_in_group("player_ships"):
|
||||
if not is_instance_valid(ship):
|
||||
continue
|
||||
var to_ship: Vector2 = ship.global_position - origin
|
||||
var along: float = to_ship.dot(aim_direction)
|
||||
if along < 0.0 or along > beam_range:
|
||||
continue
|
||||
var perpendicular: float = (to_ship - aim_direction * along).length()
|
||||
if perpendicular <= beam_half_width:
|
||||
if reaches_point(ship.global_position):
|
||||
ship.take_damage(damage)
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func reaches_point(world_point: Vector2) -> bool:
|
||||
var offset: Vector2 = world_point - origin
|
||||
var along: float = offset.dot(aim_direction)
|
||||
if along < 0.0 or (not infinite_range and along > beam_range):
|
||||
return false
|
||||
return (offset - aim_direction * along).length() <= beam_half_width
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
# Drawn in local space, rotated in launch() so local +X already points
|
||||
# along the locked aim_direction - the same reason the beam segment's
|
||||
|
|
@ -100,7 +110,21 @@ func _draw_beam_line(width: float, alpha: float) -> void:
|
|||
# Three-layer glow along local +X - the same soft-halo / core / bright-
|
||||
# hairline structure as ArtilleryShell._draw_ring, adapted from a ring
|
||||
# to a straight line.
|
||||
var to: Vector2 = Vector2(beam_range, 0.0)
|
||||
var length: float = _visible_ray_length() if infinite_range else beam_range
|
||||
var to: Vector2 = Vector2(length, 0.0)
|
||||
draw_line(Vector2.ZERO, to, Color(color, 0.16 * alpha), width * 2.6, true)
|
||||
draw_line(Vector2.ZERO, to, Color(color, 0.5 * alpha), width, true)
|
||||
draw_line(Vector2.ZERO, to, Color(1.0, 0.92, 0.98, 0.9 * alpha), width * 0.35, true)
|
||||
|
||||
|
||||
func _visible_ray_length() -> float:
|
||||
# Collision is mathematically unbounded, but rasterizing a 20,000-unit
|
||||
# primitive wastes fill work outside the screen. Reaching beyond the
|
||||
# furthest viewport corner looks identical and follows a moving camera.
|
||||
var viewport_world_size: Vector2 = get_viewport_rect().size
|
||||
var camera: Camera2D = get_viewport().get_camera_2d()
|
||||
if camera == null:
|
||||
return viewport_world_size.length() + 256.0
|
||||
viewport_world_size /= camera.zoom
|
||||
return origin.distance_to(camera.global_position) \
|
||||
+ viewport_world_size.length() * 0.5 + 256.0
|
||||
|
|
|
|||
Loading…
Reference in a new issue