Updates for Practice room and Relay
This commit is contained in:
+103
-2
@@ -28,6 +28,12 @@ const BURST_COUNT_P2 := 12
|
|||||||
const BURST_SPEED := 150.0
|
const BURST_SPEED := 150.0
|
||||||
const PROJ_SIZE := 12.0
|
const PROJ_SIZE := 12.0
|
||||||
|
|
||||||
|
const HOMING_COUNT_P1 := 4
|
||||||
|
const HOMING_COUNT_P2 := 6
|
||||||
|
const HOMING_SPEED := 100.0
|
||||||
|
const HOMING_TURN_SPEED := 1.8 # radians per second
|
||||||
|
const HOMING_DELAY := 0.3 # delay after radial burst before homing fires
|
||||||
|
|
||||||
const DRIFTER_SPAWN_MIN := 2
|
const DRIFTER_SPAWN_MIN := 2
|
||||||
const DRIFTER_SPAWN_MAX := 4
|
const DRIFTER_SPAWN_MAX := 4
|
||||||
const DRIFTER_COUNT_P1_MIN := 2
|
const DRIFTER_COUNT_P1_MIN := 2
|
||||||
@@ -57,6 +63,8 @@ var _teleport_count : int = 0
|
|||||||
var _next_drifter_threshold : int = 0
|
var _next_drifter_threshold : int = 0
|
||||||
|
|
||||||
var _burst_projs : Array[Node2D] = []
|
var _burst_projs : Array[Node2D] = []
|
||||||
|
var _homing_projs : Array[Node2D] = []
|
||||||
|
var _homing_delay : float = 0.0
|
||||||
|
|
||||||
var _climax_timer : float = 0.0
|
var _climax_timer : float = 0.0
|
||||||
var _blink_accum : float = 0.0
|
var _blink_accum : float = 0.0
|
||||||
@@ -80,6 +88,7 @@ func _ready() -> void:
|
|||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
_tick_spawn_delay(delta)
|
_tick_spawn_delay(delta)
|
||||||
|
_find_player()
|
||||||
if _phase == Phase.CLIMAX:
|
if _phase == Phase.CLIMAX:
|
||||||
_tick_climax(delta)
|
_tick_climax(delta)
|
||||||
return
|
return
|
||||||
@@ -88,6 +97,8 @@ func _physics_process(delta: float) -> void:
|
|||||||
|
|
||||||
_tick_teleport(delta)
|
_tick_teleport(delta)
|
||||||
_tick_burst_projs(delta)
|
_tick_burst_projs(delta)
|
||||||
|
_tick_homing_delay(delta)
|
||||||
|
_tick_homing_projs(delta)
|
||||||
|
|
||||||
velocity = Vector2.ZERO
|
velocity = Vector2.ZERO
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
@@ -151,8 +162,9 @@ func _reappear() -> void:
|
|||||||
_tp_state = TeleportState.IDLE
|
_tp_state = TeleportState.IDLE
|
||||||
_teleport_count += 1
|
_teleport_count += 1
|
||||||
|
|
||||||
# Fire burst
|
# Fire radial burst immediately, then homing after delay
|
||||||
_fire_burst()
|
_fire_burst()
|
||||||
|
_homing_delay = HOMING_DELAY
|
||||||
|
|
||||||
# Check drifter spawn
|
# Check drifter spawn
|
||||||
if _teleport_count >= _next_drifter_threshold:
|
if _teleport_count >= _next_drifter_threshold:
|
||||||
@@ -186,7 +198,6 @@ func _fire_burst() -> void:
|
|||||||
func _spawn_burst_proj(dir: Vector2) -> void:
|
func _spawn_burst_proj(dir: Vector2) -> void:
|
||||||
var proj := Node2D.new()
|
var proj := Node2D.new()
|
||||||
proj.set_meta("dir", dir)
|
proj.set_meta("dir", dir)
|
||||||
proj.global_position = global_position
|
|
||||||
|
|
||||||
var poly := Polygon2D.new()
|
var poly := Polygon2D.new()
|
||||||
var hs := PROJ_SIZE * 0.5
|
var hs := PROJ_SIZE * 0.5
|
||||||
@@ -209,6 +220,7 @@ func _spawn_burst_proj(dir: Vector2) -> void:
|
|||||||
area.body_entered.connect(_on_burst_hit_player.bind(proj))
|
area.body_entered.connect(_on_burst_hit_player.bind(proj))
|
||||||
|
|
||||||
get_parent().add_child(proj)
|
get_parent().add_child(proj)
|
||||||
|
proj.global_position = global_position # Set AFTER add_child for correct transform
|
||||||
_burst_projs.append(proj)
|
_burst_projs.append(proj)
|
||||||
|
|
||||||
func _on_burst_hit_player(body: Node2D, proj: Node2D) -> void:
|
func _on_burst_hit_player(body: Node2D, proj: Node2D) -> void:
|
||||||
@@ -236,6 +248,89 @@ func _tick_burst_projs(delta: float) -> void:
|
|||||||
|
|
||||||
_burst_projs = still_valid
|
_burst_projs = still_valid
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Homing Projectiles
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
func _tick_homing_delay(delta: float) -> void:
|
||||||
|
if _homing_delay <= 0.0:
|
||||||
|
return
|
||||||
|
_homing_delay -= delta
|
||||||
|
if _homing_delay <= 0.0:
|
||||||
|
_fire_homing()
|
||||||
|
|
||||||
|
func _fire_homing() -> void:
|
||||||
|
var count := HOMING_COUNT_P2 if _phase == Phase.TWO else HOMING_COUNT_P1
|
||||||
|
for i in count:
|
||||||
|
var angle := TAU * float(i) / float(count)
|
||||||
|
_spawn_homing_proj(angle)
|
||||||
|
|
||||||
|
func _spawn_homing_proj(start_angle: float) -> void:
|
||||||
|
var proj := Node2D.new()
|
||||||
|
proj.set_meta("angle", start_angle)
|
||||||
|
|
||||||
|
# Diamond shape visual
|
||||||
|
var poly := Polygon2D.new()
|
||||||
|
poly.polygon = PackedVector2Array([
|
||||||
|
Vector2(0, -8), Vector2(5, 0),
|
||||||
|
Vector2(0, 8), Vector2(-5, 0)
|
||||||
|
])
|
||||||
|
poly.color = Color(0.9, 0.3, 0.9) # purple/magenta
|
||||||
|
proj.add_child(poly)
|
||||||
|
|
||||||
|
var area := Area2D.new()
|
||||||
|
area.collision_layer = 0
|
||||||
|
area.collision_mask = 2
|
||||||
|
var shape := CollisionShape2D.new()
|
||||||
|
var circle := CircleShape2D.new()
|
||||||
|
circle.radius = 6.0
|
||||||
|
shape.shape = circle
|
||||||
|
area.add_child(shape)
|
||||||
|
proj.add_child(area)
|
||||||
|
area.body_entered.connect(_on_homing_hit_player.bind(proj))
|
||||||
|
|
||||||
|
get_parent().add_child(proj)
|
||||||
|
proj.global_position = global_position # Set AFTER add_child
|
||||||
|
_homing_projs.append(proj)
|
||||||
|
|
||||||
|
func _on_homing_hit_player(body: Node2D, proj: Node2D) -> void:
|
||||||
|
if body.is_in_group("player") and body.has_method("take_damage"):
|
||||||
|
body.take_damage(1)
|
||||||
|
if is_instance_valid(proj):
|
||||||
|
proj.queue_free()
|
||||||
|
|
||||||
|
func _tick_homing_projs(delta: float) -> void:
|
||||||
|
if _player == null or not is_instance_valid(_player):
|
||||||
|
return
|
||||||
|
|
||||||
|
var still_valid: Array[Node2D] = []
|
||||||
|
|
||||||
|
for proj in _homing_projs:
|
||||||
|
if not is_instance_valid(proj):
|
||||||
|
continue
|
||||||
|
|
||||||
|
var angle: float = proj.get_meta("angle")
|
||||||
|
var to_player := (_player.global_position - proj.global_position).normalized()
|
||||||
|
var desired_angle := to_player.angle()
|
||||||
|
|
||||||
|
# Smoothly turn towards player
|
||||||
|
var diff := angle_difference(angle, desired_angle)
|
||||||
|
angle += clampf(diff, -HOMING_TURN_SPEED * delta, HOMING_TURN_SPEED * delta)
|
||||||
|
proj.set_meta("angle", angle)
|
||||||
|
|
||||||
|
# Move forward
|
||||||
|
var dir := Vector2.from_angle(angle)
|
||||||
|
proj.global_position += dir * HOMING_SPEED * delta
|
||||||
|
proj.rotation = angle + PI / 2 # Point diamond forward
|
||||||
|
|
||||||
|
# Check bounds
|
||||||
|
var rel := proj.global_position - _room_center
|
||||||
|
if absf(rel.x) > ROOM_HW - WALL_T or absf(rel.y) > ROOM_HH - WALL_T:
|
||||||
|
proj.queue_free()
|
||||||
|
else:
|
||||||
|
still_valid.append(proj)
|
||||||
|
|
||||||
|
_homing_projs = still_valid
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Signal Drifters
|
# Signal Drifters
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -311,4 +406,10 @@ func _die() -> void:
|
|||||||
proj.queue_free()
|
proj.queue_free()
|
||||||
_burst_projs.clear()
|
_burst_projs.clear()
|
||||||
|
|
||||||
|
# Cleanup homing projectiles
|
||||||
|
for proj in _homing_projs:
|
||||||
|
if is_instance_valid(proj):
|
||||||
|
proj.queue_free()
|
||||||
|
_homing_projs.clear()
|
||||||
|
|
||||||
queue_free()
|
queue_free()
|
||||||
|
|||||||
@@ -368,15 +368,15 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Practice room — boss training arena
|
# Practice room — boss training arena
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
const WARDEN_SCENE = preload("res://scenes/enemies/warden.tscn")
|
const WARDEN_SCENE = preload("res://scenes/enemies/warden.tscn")
|
||||||
const RELAY_SCENE = preload("res://scenes/enemies/relay.tscn")
|
const RELAY_SCENE = preload("res://scenes/enemies/relay.tscn")
|
||||||
const SUPERVISOR_SCENE = preload("res://scenes/enemies/supervisor.tscn")
|
const HIVEMIND_SCENE = preload("res://scenes/enemies/hivemind.tscn")
|
||||||
|
|
||||||
const BOSS_NAMES := ["WARDEN", "RELAY", "SUPERVISOR"]
|
const BOSS_NAMES := ["WARDEN", "RELAY", "HIVEMIND"]
|
||||||
const BOSS_COLORS := [
|
const BOSS_COLORS := [
|
||||||
Color(0.6, 0.35, 0.2), # Warden — rust
|
Color(0.6, 0.35, 0.2), # Warden — rust
|
||||||
Color(0.15, 0.55, 0.65), # Relay — teal
|
Color(0.15, 0.55, 0.65), # Relay — teal
|
||||||
Color(0.7, 0.2, 0.2), # Supervisor — red
|
Color(0.5, 0.2, 0.6), # Hivemind — purple
|
||||||
]
|
]
|
||||||
|
|
||||||
func _build_practice_room() -> void:
|
func _build_practice_room() -> void:
|
||||||
@@ -469,7 +469,7 @@ func _spawn_practice_boss(boss_idx: int) -> void:
|
|||||||
match boss_idx:
|
match boss_idx:
|
||||||
0: scene = WARDEN_SCENE
|
0: scene = WARDEN_SCENE
|
||||||
1: scene = RELAY_SCENE
|
1: scene = RELAY_SCENE
|
||||||
_: scene = SUPERVISOR_SCENE
|
_: scene = HIVEMIND_SCENE
|
||||||
|
|
||||||
_practice_boss = scene.instantiate()
|
_practice_boss = scene.instantiate()
|
||||||
contents.add_child(_practice_boss)
|
contents.add_child(_practice_boss)
|
||||||
|
|||||||
Reference in New Issue
Block a user