Small update fixes
This commit is contained in:
@@ -50,6 +50,7 @@ const BEAM_LENGTH := 500.0
|
||||
const BEAM_WIDTH := 28.0
|
||||
const PULSE_FORCE := 350.0
|
||||
const PULSE_CD := 5.0
|
||||
const PULSE_RADIUS := 200.0
|
||||
const HOMING_COUNT := 4
|
||||
const HOMING_SPEED := 120.0
|
||||
const HOMING_TURN := 1.8 # rad/sec
|
||||
@@ -747,6 +748,9 @@ func _attack_pulse() -> void:
|
||||
if _player == null or not is_instance_valid(_player):
|
||||
return
|
||||
|
||||
# Only apply knockback if player is within pulse radius
|
||||
var dist := global_position.distance_to(_player.global_position)
|
||||
if dist <= PULSE_RADIUS:
|
||||
var dir := (_player.global_position - global_position).normalized()
|
||||
if _player.has_method("apply_knockback"):
|
||||
_player.apply_knockback(dir * PULSE_FORCE)
|
||||
@@ -812,13 +816,24 @@ func _on_homing_hit(body: Node, proj: Node2D) -> void:
|
||||
|
||||
func _tick_homing_projs(delta: float) -> void:
|
||||
if _player == null or not is_instance_valid(_player):
|
||||
# Player died or invalid — clear all homing projectiles
|
||||
for p in _homing_projs:
|
||||
var raw_node = p.get("node")
|
||||
if raw_node != null and is_instance_valid(raw_node):
|
||||
raw_node.queue_free()
|
||||
_homing_projs.clear()
|
||||
return
|
||||
|
||||
var still_valid: Array[Dictionary] = []
|
||||
|
||||
for p in _homing_projs:
|
||||
var node: Node2D = p["node"]
|
||||
if not is_instance_valid(node):
|
||||
# Check validity on raw value before typed assignment to avoid error
|
||||
var raw_node = p.get("node")
|
||||
if raw_node == null or not is_instance_valid(raw_node):
|
||||
continue
|
||||
|
||||
var node := raw_node as Node2D
|
||||
if node == null:
|
||||
continue
|
||||
|
||||
# Age check
|
||||
|
||||
+10
-15
@@ -24,6 +24,7 @@ const DOOR_W := 80.0
|
||||
const DOOR_H := 80.0
|
||||
|
||||
const WALL_COLOR := Color(0.18, 0.18, 0.25)
|
||||
const STATION_DOOR_SCRIPT = preload("res://scripts/rooms/station_door.gd")
|
||||
|
||||
const ROOM_NAMES := [
|
||||
"STAGING AREA", # 0
|
||||
@@ -272,33 +273,27 @@ func _build_armory_workbench() -> void:
|
||||
# Staging Area run portal
|
||||
# ---------------------------------------------------------------------------
|
||||
func _build_run_portal() -> void:
|
||||
# Visual — bright green rectangle
|
||||
var poly := Polygon2D.new()
|
||||
poly.polygon = PackedVector2Array([-40, -30, 40, -30, 40, 30, -40, 30])
|
||||
poly.color = Color(0.2, 0.9, 0.4)
|
||||
poly.position = Vector2(0, -150)
|
||||
contents.add_child(poly)
|
||||
|
||||
# Label above portal
|
||||
var lbl := Label.new()
|
||||
lbl.text = "START RUN"
|
||||
lbl.position = Vector2(-36, -195)
|
||||
contents.add_child(lbl)
|
||||
# Station door with proper frame graphics
|
||||
var portal_door := Node2D.new()
|
||||
portal_door.set_script(STATION_DOOR_SCRIPT)
|
||||
portal_door.position = Vector2(0, -150)
|
||||
contents.add_child(portal_door)
|
||||
portal_door.setup_hub_portal()
|
||||
|
||||
_portal_prompt = Label.new()
|
||||
_portal_prompt.text = "[E] Start Run"
|
||||
_portal_prompt.position = Vector2(-50, -210)
|
||||
_portal_prompt.position = Vector2(-50, -85) # below the door label
|
||||
_portal_prompt.visible = false
|
||||
contents.add_child(_portal_prompt)
|
||||
|
||||
# Trigger zone
|
||||
# Trigger zone for interaction
|
||||
var area := Area2D.new()
|
||||
area.collision_layer = 0
|
||||
area.collision_mask = 2
|
||||
|
||||
var cs := CollisionShape2D.new()
|
||||
var rs := RectangleShape2D.new()
|
||||
rs.size = Vector2(80, 60)
|
||||
rs.size = Vector2(80, 100)
|
||||
cs.shape = rs
|
||||
area.add_child(cs)
|
||||
area.position = Vector2(0, -150)
|
||||
|
||||
@@ -31,6 +31,7 @@ var _slick_count: int = 0
|
||||
|
||||
# Knockback — applied by enemies (e.g., Hivemind pulse)
|
||||
var _knockback_velocity: Vector2 = Vector2.ZERO
|
||||
var _knockback_flash_timer: float = 0.0
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("player")
|
||||
@@ -62,8 +63,9 @@ func _handle_movement() -> void:
|
||||
input_dir = input_dir.normalized()
|
||||
|
||||
if _slick_count > 0:
|
||||
# Oil slick — momentum-based: velocity slowly drifts toward desired direction
|
||||
velocity = velocity.lerp(input_dir * stats.speed, 0.08)
|
||||
# Oil slick — 40% slower + momentum-based movement
|
||||
var slick_speed := stats.speed * 0.6
|
||||
velocity = velocity.lerp(input_dir * slick_speed, 0.12)
|
||||
else:
|
||||
velocity = input_dir * stats.speed
|
||||
|
||||
@@ -168,6 +170,12 @@ func _handle_iframes(delta: float) -> void:
|
||||
body.modulate = Color(1, 1, 1, 1)
|
||||
return
|
||||
|
||||
# Knockback flash — magenta pulse when pushed by enemy ability
|
||||
if _knockback_flash_timer > 0.0:
|
||||
_knockback_flash_timer -= delta
|
||||
body.modulate = Color(1.0, 0.4, 0.7, 1.0)
|
||||
return
|
||||
|
||||
# Shield active — cyan tint
|
||||
if _shield_active:
|
||||
body.modulate = Color(0.4, 0.9, 1.0)
|
||||
@@ -216,6 +224,10 @@ func take_damage(amount: float) -> void:
|
||||
|
||||
func _die() -> void:
|
||||
if RunManager.run_active:
|
||||
# Disable player so they can't move during death screen
|
||||
set_physics_process(false)
|
||||
set_process_input(false)
|
||||
visible = false
|
||||
RunManager.end_run(false)
|
||||
else:
|
||||
# In hub/practice — signal for practice room handling, then heal
|
||||
@@ -277,6 +289,7 @@ func exit_slick() -> void:
|
||||
# ---------------------------------------------------------------------------
|
||||
func apply_knockback(force: Vector2) -> void:
|
||||
_knockback_velocity = force
|
||||
_knockback_flash_timer = 0.25 # brief magenta flash to show player was pushed
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Room event handlers
|
||||
|
||||
+22
-11
@@ -324,6 +324,9 @@ func _spawn_boss() -> void:
|
||||
_enemy_death_connections.append({"node": boss, "signal": "tree_exited", "callable": callable})
|
||||
_enemy_count = 1
|
||||
|
||||
# Spawn exit door on top wall (visible but closed until boss defeated)
|
||||
_spawn_floor_exit_door()
|
||||
|
||||
|
||||
func _on_enemy_died() -> void:
|
||||
# Guard against deferred callback running after room is freed
|
||||
@@ -335,29 +338,33 @@ func _on_enemy_died() -> void:
|
||||
AudioManager.play("room_clear")
|
||||
if data.type == RoomData.RoomType.BOSS:
|
||||
_spawn_body_part_drop()
|
||||
_spawn_floor_exit()
|
||||
_open_floor_exit()
|
||||
room_cleared.emit()
|
||||
EventBus.room_cleared.emit(data.id)
|
||||
_update_door_locks()
|
||||
|
||||
|
||||
func _spawn_floor_exit() -> void:
|
||||
func _spawn_floor_exit_door() -> void:
|
||||
# Create the exit door on the top wall (closed until boss defeated)
|
||||
var is_final_floor := RunManager.current_floor >= 3
|
||||
|
||||
# Station door for floor transition - positioned to the right of body part drop
|
||||
_floor_exit_door = StationDoor.new()
|
||||
_floor_exit_door.position = Vector2(150, 0)
|
||||
# Position on top wall, centered
|
||||
_floor_exit_door.position = Vector2(0, -ROOM_H / 2.0 + 80)
|
||||
|
||||
if is_final_floor:
|
||||
_floor_exit_door.setup_evacuation_exit()
|
||||
else:
|
||||
# Next sector: floor 1 -> SECTOR 2, floor 2 -> SECTOR 3
|
||||
_floor_exit_door.setup_sector_exit(RunManager.current_floor + 1)
|
||||
|
||||
_floor_exit_door.door_entered.connect(_on_floor_exit_entered)
|
||||
contents.add_child(_floor_exit_door)
|
||||
# Door starts closed (setup functions already call close())
|
||||
|
||||
|
||||
func _open_floor_exit() -> void:
|
||||
# Open the door now that boss is dead
|
||||
if _floor_exit_door != null:
|
||||
_floor_exit_door.open()
|
||||
|
||||
|
||||
@@ -426,20 +433,24 @@ func _make_oil_slick(pos: Vector2) -> void:
|
||||
slick.collision_mask = 2 | 8 # player (2) + enemies (8)
|
||||
slick.position = pos
|
||||
|
||||
var radius: float = randf_range(44.0, 68.0)
|
||||
# Oval dimensions — wider than tall
|
||||
var radius_x: float = randf_range(55.0, 85.0)
|
||||
var radius_y: float = randf_range(35.0, 50.0)
|
||||
|
||||
# Use rectangle shape for collision (approximates oval)
|
||||
var cs := CollisionShape2D.new()
|
||||
var sh := CircleShape2D.new()
|
||||
sh.radius = radius
|
||||
var sh := RectangleShape2D.new()
|
||||
sh.size = Vector2(radius_x * 1.6, radius_y * 1.6) # slightly smaller than visual
|
||||
cs.shape = sh
|
||||
slick.add_child(cs)
|
||||
|
||||
# Visual — circle matching collision shape
|
||||
# Visual — oval shape
|
||||
var poly := Polygon2D.new()
|
||||
var pts := PackedVector2Array()
|
||||
var segs := 14
|
||||
var segs := 16
|
||||
for i in segs:
|
||||
var a := i * TAU / segs
|
||||
pts.append(Vector2(cos(a) * radius, sin(a) * radius))
|
||||
pts.append(Vector2(cos(a) * radius_x, sin(a) * radius_y))
|
||||
poly.polygon = pts
|
||||
poly.color = Color(0.04, 0.07, 0.14, 0.78)
|
||||
slick.add_child(poly)
|
||||
|
||||
@@ -26,7 +26,7 @@ const EXIT_LIGHT := Color(0.2, 0.95, 0.4) # bright green exit
|
||||
# ---------------------------------------------------------------------------
|
||||
# Configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
enum DoorType { ENTRY, SECTOR_EXIT, EVACUATION_EXIT }
|
||||
enum DoorType { ENTRY, SECTOR_EXIT, EVACUATION_EXIT, HUB_PORTAL }
|
||||
|
||||
var door_type: DoorType = DoorType.ENTRY
|
||||
var sector_label: String = "" # "SECTOR 2", "SECTOR 3", or "EXIT"
|
||||
@@ -78,6 +78,17 @@ func setup_evacuation_exit() -> void:
|
||||
_build_trigger()
|
||||
close() # starts closed until boss dies
|
||||
|
||||
func setup_hub_portal() -> void:
|
||||
# Hub staging room portal - decorative open door with "START RUN" label
|
||||
door_type = DoorType.HUB_PORTAL
|
||||
sector_label = "START RUN"
|
||||
is_functional = false # interaction handled by hub_room.gd
|
||||
_build_frame()
|
||||
_build_panels()
|
||||
_build_indicator()
|
||||
_build_label()
|
||||
open() # always open in hub
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Build components
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user