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:
@@ -29,6 +29,9 @@ var _shield_flash_timer: float = 0.0 # brief white flash on absorb
|
||||
# Oil slick — count of slick zones currently overlapping player
|
||||
var _slick_count: int = 0
|
||||
|
||||
# Knockback — applied by enemies (e.g., Hivemind pulse)
|
||||
var _knockback_velocity: Vector2 = Vector2.ZERO
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("player")
|
||||
EventBus.room_entered.connect(_on_room_entered)
|
||||
@@ -42,6 +45,8 @@ func _physics_process(delta: float) -> void:
|
||||
_handle_shooting(delta)
|
||||
_handle_iframes(delta)
|
||||
_handle_shield(delta)
|
||||
# Decay knockback
|
||||
_knockback_velocity = _knockback_velocity.move_toward(Vector2.ZERO, 800.0 * delta)
|
||||
move_and_slide()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -62,6 +67,9 @@ func _handle_movement() -> void:
|
||||
else:
|
||||
velocity = input_dir * stats.speed
|
||||
|
||||
# Apply knockback on top of movement
|
||||
velocity += _knockback_velocity
|
||||
|
||||
if input_dir.x != 0.0:
|
||||
body.scale.x = sign(input_dir.x)
|
||||
|
||||
@@ -141,6 +149,7 @@ func _handle_shield(delta: float) -> void:
|
||||
|
||||
if _shield_ready and Input.is_physical_key_pressed(KEY_SPACE):
|
||||
_shield_active = true
|
||||
_shield_ready = false
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Iframes — flash body during invincibility
|
||||
@@ -239,12 +248,18 @@ func _spawn_coolant_zone() -> void:
|
||||
|
||||
zone.body_entered.connect(func(b: Node) -> void:
|
||||
if b.has_method("apply_slow"):
|
||||
b.apply_slow(2.0, 0.4))
|
||||
b.apply_slow(2.0, 0.4)
|
||||
)
|
||||
|
||||
# Use a Timer child for self-cleanup (avoids SceneTreeTimer leak)
|
||||
var timer := Timer.new()
|
||||
timer.wait_time = 1.0
|
||||
timer.one_shot = true
|
||||
timer.timeout.connect(zone.queue_free)
|
||||
zone.add_child(timer)
|
||||
|
||||
get_parent().call_deferred("add_child", zone)
|
||||
get_tree().create_timer(1.0).timeout.connect(func() -> void:
|
||||
if is_instance_valid(zone):
|
||||
zone.queue_free())
|
||||
timer.call_deferred("start")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Oil slick — called by hazard Area2D nodes
|
||||
@@ -255,6 +270,12 @@ func enter_slick() -> void:
|
||||
func exit_slick() -> void:
|
||||
_slick_count = maxi(_slick_count - 1, 0)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Knockback — called by enemies (e.g., Hivemind pulse attack)
|
||||
# ---------------------------------------------------------------------------
|
||||
func apply_knockback(force: Vector2) -> void:
|
||||
_knockback_velocity = force
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Room event handlers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -10,6 +10,12 @@ const BASE_FIRE_RATE := 3.5 # shots per second
|
||||
const BASE_RANGE := 400.0
|
||||
const BASE_PROJ_SPEED := 520.0
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stat caching — only recalculate when items/parts change
|
||||
# ---------------------------------------------------------------------------
|
||||
var _stats_dirty: bool = true
|
||||
var _cached_item_count: int = -1 # Track item count to detect external changes
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Live stats (recalculated from base + equipped parts + run items)
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -52,6 +58,7 @@ var shield_projector: bool = false
|
||||
func _ready() -> void:
|
||||
EventBus.body_part_equipped.connect(_on_part_equipped)
|
||||
EventBus.save_loaded.connect(_on_save_loaded)
|
||||
EventBus.item_collected.connect(_on_item_collected)
|
||||
recalculate()
|
||||
if RunManager.player_health_carry > 0.0:
|
||||
current_health = minf(RunManager.player_health_carry, max_health)
|
||||
@@ -61,6 +68,7 @@ func _ready() -> void:
|
||||
|
||||
func _on_save_loaded() -> void:
|
||||
var old_max := max_health
|
||||
_stats_dirty = true
|
||||
recalculate()
|
||||
# Grant bonus HP from body parts on fresh start
|
||||
if RunManager.player_health_carry < 0.0 and max_health > old_max:
|
||||
@@ -68,7 +76,23 @@ func _on_save_loaded() -> void:
|
||||
current_health = minf(current_health, max_health)
|
||||
EventBus.player_health_changed.emit(current_health, max_health)
|
||||
|
||||
func _on_item_collected(_item: Resource) -> void:
|
||||
_stats_dirty = true
|
||||
recalculate()
|
||||
EventBus.player_health_changed.emit(current_health, max_health)
|
||||
|
||||
func mark_dirty() -> void:
|
||||
_stats_dirty = true
|
||||
|
||||
func recalculate() -> void:
|
||||
# Skip if stats haven't changed (cached)
|
||||
var current_item_count := RunManager.run_items.size()
|
||||
if not _stats_dirty and _cached_item_count == current_item_count:
|
||||
return
|
||||
|
||||
_cached_item_count = current_item_count
|
||||
_stats_dirty = false
|
||||
|
||||
speed = BASE_SPEED + UpgradeManager.get_stat_modifier("speed")
|
||||
max_health = BASE_MAX_HEALTH + UpgradeManager.get_stat_modifier("max_health")
|
||||
damage = BASE_DAMAGE + UpgradeManager.get_stat_modifier("damage")
|
||||
@@ -154,6 +178,7 @@ func heal(amount: float) -> void:
|
||||
|
||||
func _on_part_equipped(_part: Resource, _slot: String) -> void:
|
||||
var old_max := max_health
|
||||
_stats_dirty = true
|
||||
recalculate()
|
||||
# If max_health increased, grant the extra HP immediately
|
||||
if max_health > old_max:
|
||||
|
||||
Reference in New Issue
Block a user