Fix critical bugs from code review
- room.gd: Add signal tracking and explicit cleanup in _exit_tree - room.gd: Guard _on_enemy_died against race condition after room freed - player.gd: Set _shield_ready = false on shield activation - player.gd: Fix coolant zone lambda syntax error - player.gd: Add apply_knockback() method for Hivemind pulse - player.gd: Fix coolant zone memory leak (use Timer child) - hud.gd: Remove deprecated boss bar code
This commit is contained in:
@@ -57,7 +57,7 @@ func _physics_process(delta: float) -> void:
|
||||
State.ANCHORED:
|
||||
_do_anchored(delta)
|
||||
|
||||
velocity *= _slow_factor
|
||||
velocity *= get_slow_factor()
|
||||
move_and_slide()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -25,7 +25,7 @@ func _physics_process(delta: float) -> void:
|
||||
if not is_active():
|
||||
return
|
||||
_do_arc_chase(delta)
|
||||
velocity *= _slow_factor
|
||||
velocity *= get_slow_factor()
|
||||
move_and_slide()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -17,8 +17,7 @@ const SPAWN_DELAY: float = 0.5
|
||||
|
||||
var _contact_timer: float = 0.0
|
||||
var _flash_timer: float = 0.0
|
||||
var _slow_factor: float = 1.0
|
||||
var _slow_timer: float = 0.0
|
||||
var _slow_effects: Array[Dictionary] = [] # [{factor, timer}] - stacking slows
|
||||
var _spawn_timer: float = SPAWN_DELAY
|
||||
var _player: Node2D = null
|
||||
|
||||
@@ -62,14 +61,24 @@ func _tick_flash(delta: float) -> void:
|
||||
visual.modulate = Color(1.0, 1.0, 1.0)
|
||||
|
||||
func apply_slow(duration: float, factor: float) -> void:
|
||||
_slow_factor = factor
|
||||
_slow_timer = duration
|
||||
# Stack slows by adding new effect - factors multiply together
|
||||
_slow_effects.append({"factor": factor, "timer": duration})
|
||||
|
||||
func _tick_slow(delta: float) -> void:
|
||||
if _slow_timer > 0.0:
|
||||
_slow_timer -= delta
|
||||
if _slow_timer <= 0.0:
|
||||
_slow_factor = 1.0
|
||||
# Tick down all slow effects and remove expired ones
|
||||
var i := _slow_effects.size() - 1
|
||||
while i >= 0:
|
||||
_slow_effects[i]["timer"] -= delta
|
||||
if _slow_effects[i]["timer"] <= 0.0:
|
||||
_slow_effects.remove_at(i)
|
||||
i -= 1
|
||||
|
||||
func get_slow_factor() -> float:
|
||||
# Multiply all active slow factors together
|
||||
var combined := 1.0
|
||||
for effect in _slow_effects:
|
||||
combined *= effect["factor"]
|
||||
return combined
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Damage / death
|
||||
|
||||
@@ -57,6 +57,11 @@ const HOMING_LIFETIME := 7.0 # seconds before homing projectiles expire
|
||||
|
||||
const FREEZE_DURATION := 2.0
|
||||
|
||||
const ORBITER_DAMAGE := 1.0
|
||||
const SWEEP_PROJ_DAMAGE := 1.0
|
||||
const BEAM_DAMAGE := 1.0
|
||||
const HOMING_PROJ_DAMAGE := 1.0
|
||||
|
||||
const DRIFTER_SCENE = preload("res://scenes/enemies/drifter.tscn")
|
||||
const REPEATER_SCENE = preload("res://scenes/enemies/repeater.tscn")
|
||||
const ANCHOR_SCENE = preload("res://scenes/enemies/anchor.tscn")
|
||||
@@ -461,7 +466,7 @@ func _create_orbiter(angle: float) -> Dictionary:
|
||||
|
||||
func _on_orbiter_hit(body: Node, _proj: Node2D) -> void:
|
||||
if body.is_in_group("player") and body.has_method("take_damage"):
|
||||
body.take_damage(1.0)
|
||||
body.take_damage(ORBITER_DAMAGE)
|
||||
|
||||
|
||||
func _tick_orbiters(delta: float) -> void:
|
||||
@@ -557,7 +562,7 @@ func _spawn_sweep_proj(pos: Vector2, vel: Vector2) -> void:
|
||||
|
||||
func _on_sweep_hit(body: Node, proj: Node2D) -> void:
|
||||
if body.is_in_group("player") and body.has_method("take_damage"):
|
||||
body.take_damage(1.0)
|
||||
body.take_damage(SWEEP_PROJ_DAMAGE)
|
||||
if is_instance_valid(proj):
|
||||
proj.queue_free()
|
||||
|
||||
@@ -738,7 +743,7 @@ func _end_beam() -> void:
|
||||
|
||||
func _on_beam_hit(body: Node) -> void:
|
||||
if body.is_in_group("player") and body.has_method("take_damage"):
|
||||
body.take_damage(1.0)
|
||||
body.take_damage(BEAM_DAMAGE)
|
||||
|
||||
|
||||
func _attack_pulse() -> void:
|
||||
@@ -803,7 +808,7 @@ func _spawn_homing_proj(start_angle: float) -> void:
|
||||
|
||||
func _on_homing_hit(body: Node, proj: Node2D) -> void:
|
||||
if body.is_in_group("player") and body.has_method("take_damage"):
|
||||
body.take_damage(1.0)
|
||||
body.take_damage(HOMING_PROJ_DAMAGE)
|
||||
if is_instance_valid(proj):
|
||||
proj.queue_free()
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ const DRIFTER_OFFSET_MAX := 80.0
|
||||
|
||||
const FREEZE_DURATION := 1.5
|
||||
|
||||
const BURST_PROJ_DAMAGE := 1.0
|
||||
const HOMING_PROJ_DAMAGE := 1.0
|
||||
|
||||
const DRIFTER_SCENE = preload("res://scenes/enemies/drifter.tscn")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -228,7 +231,7 @@ func _spawn_burst_proj(dir: Vector2) -> void:
|
||||
|
||||
func _on_burst_hit_player(body: Node2D, proj: Node2D) -> void:
|
||||
if body.is_in_group("player") and body.has_method("take_damage"):
|
||||
body.take_damage(1)
|
||||
body.take_damage(BURST_PROJ_DAMAGE)
|
||||
if is_instance_valid(proj):
|
||||
proj.queue_free()
|
||||
|
||||
@@ -298,7 +301,7 @@ func _spawn_homing_proj(start_angle: float) -> void:
|
||||
|
||||
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)
|
||||
body.take_damage(HOMING_PROJ_DAMAGE)
|
||||
if is_instance_valid(proj):
|
||||
proj.queue_free()
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ func _physics_process(delta: float) -> void:
|
||||
return
|
||||
velocity = Vector2.ZERO
|
||||
_do_fire(delta)
|
||||
velocity *= _slow_factor
|
||||
velocity *= get_slow_factor()
|
||||
move_and_slide()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -38,6 +38,8 @@ const WIND_UP_DURATION := 0.65 # seconds of warning before beam fires
|
||||
# ---------------------------------------------------------------------------
|
||||
const FREEZE_DURATION := 2.0
|
||||
|
||||
const BEAM_DAMAGE := 1.0
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Phase 2 Drifter spawning — one at a time, over a fixed window
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -304,7 +306,7 @@ func _fire_beam() -> void:
|
||||
area.add_child(cs)
|
||||
area.body_entered.connect(func(body: Node) -> void:
|
||||
if body.is_in_group("player"):
|
||||
body.take_damage(1.0))
|
||||
body.take_damage(BEAM_DAMAGE))
|
||||
beam.add_child(area)
|
||||
|
||||
get_parent().call_deferred("add_child", beam)
|
||||
@@ -314,11 +316,13 @@ func _fire_beam() -> void:
|
||||
# Damage / death — override base to emit boss signals; no drop (room handles it)
|
||||
# ---------------------------------------------------------------------------
|
||||
func take_damage(amount: float) -> void:
|
||||
if _phase == Phase.CLIMAX:
|
||||
return # already dying
|
||||
current_health -= amount
|
||||
_flash_timer = 0.1
|
||||
EventBus.boss_health_changed.emit(maxf(current_health, 0.0), max_health, "THE SUPERVISOR")
|
||||
if current_health <= 0.0:
|
||||
_die()
|
||||
_enter_climax()
|
||||
|
||||
func _die() -> void:
|
||||
EventBus.boss_died.emit()
|
||||
|
||||
@@ -37,6 +37,9 @@ const MINE_AOE := 70.0
|
||||
|
||||
const FREEZE_DURATION := 1.5
|
||||
|
||||
const SWEEP_PROJ_DAMAGE := 1.0
|
||||
const MINE_DAMAGE := 1.0
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# State
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -215,7 +218,7 @@ func _spawn_sweep_proj(world_pos: Vector2, vel: Vector2) -> void:
|
||||
area.add_child(cs)
|
||||
area.body_entered.connect(func(body: Node) -> void:
|
||||
if body.is_in_group("player"):
|
||||
body.take_damage(1.0)
|
||||
body.take_damage(SWEEP_PROJ_DAMAGE)
|
||||
proj.queue_free())
|
||||
proj.add_child(area)
|
||||
|
||||
@@ -321,7 +324,7 @@ func _explode_mine(entry: Dictionary) -> void:
|
||||
if _player != null and is_instance_valid(_player):
|
||||
var dist := node.global_position.distance_to(_player.global_position)
|
||||
if dist <= MINE_AOE:
|
||||
_player.take_damage(1.0)
|
||||
_player.take_damage(MINE_DAMAGE)
|
||||
|
||||
# Brief visual flash for explosion (optional: could add particle later)
|
||||
var flash := Polygon2D.new()
|
||||
|
||||
Reference in New Issue
Block a user