Small update fixes
This commit is contained in:
@@ -50,6 +50,7 @@ const BEAM_LENGTH := 500.0
|
|||||||
const BEAM_WIDTH := 28.0
|
const BEAM_WIDTH := 28.0
|
||||||
const PULSE_FORCE := 350.0
|
const PULSE_FORCE := 350.0
|
||||||
const PULSE_CD := 5.0
|
const PULSE_CD := 5.0
|
||||||
|
const PULSE_RADIUS := 200.0
|
||||||
const HOMING_COUNT := 4
|
const HOMING_COUNT := 4
|
||||||
const HOMING_SPEED := 120.0
|
const HOMING_SPEED := 120.0
|
||||||
const HOMING_TURN := 1.8 # rad/sec
|
const HOMING_TURN := 1.8 # rad/sec
|
||||||
@@ -747,6 +748,9 @@ func _attack_pulse() -> void:
|
|||||||
if _player == null or not is_instance_valid(_player):
|
if _player == null or not is_instance_valid(_player):
|
||||||
return
|
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()
|
var dir := (_player.global_position - global_position).normalized()
|
||||||
if _player.has_method("apply_knockback"):
|
if _player.has_method("apply_knockback"):
|
||||||
_player.apply_knockback(dir * PULSE_FORCE)
|
_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:
|
func _tick_homing_projs(delta: float) -> void:
|
||||||
if _player == null or not is_instance_valid(_player):
|
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
|
return
|
||||||
|
|
||||||
var still_valid: Array[Dictionary] = []
|
var still_valid: Array[Dictionary] = []
|
||||||
|
|
||||||
for p in _homing_projs:
|
for p in _homing_projs:
|
||||||
var node: Node2D = p["node"]
|
# Check validity on raw value before typed assignment to avoid error
|
||||||
if not is_instance_valid(node):
|
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
|
continue
|
||||||
|
|
||||||
# Age check
|
# Age check
|
||||||
|
|||||||
+10
-15
@@ -24,6 +24,7 @@ const DOOR_W := 80.0
|
|||||||
const DOOR_H := 80.0
|
const DOOR_H := 80.0
|
||||||
|
|
||||||
const WALL_COLOR := Color(0.18, 0.18, 0.25)
|
const WALL_COLOR := Color(0.18, 0.18, 0.25)
|
||||||
|
const STATION_DOOR_SCRIPT = preload("res://scripts/rooms/station_door.gd")
|
||||||
|
|
||||||
const ROOM_NAMES := [
|
const ROOM_NAMES := [
|
||||||
"STAGING AREA", # 0
|
"STAGING AREA", # 0
|
||||||
@@ -272,33 +273,27 @@ func _build_armory_workbench() -> void:
|
|||||||
# Staging Area run portal
|
# Staging Area run portal
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
func _build_run_portal() -> void:
|
func _build_run_portal() -> void:
|
||||||
# Visual — bright green rectangle
|
# Station door with proper frame graphics
|
||||||
var poly := Polygon2D.new()
|
var portal_door := Node2D.new()
|
||||||
poly.polygon = PackedVector2Array([-40, -30, 40, -30, 40, 30, -40, 30])
|
portal_door.set_script(STATION_DOOR_SCRIPT)
|
||||||
poly.color = Color(0.2, 0.9, 0.4)
|
portal_door.position = Vector2(0, -150)
|
||||||
poly.position = Vector2(0, -150)
|
contents.add_child(portal_door)
|
||||||
contents.add_child(poly)
|
portal_door.setup_hub_portal()
|
||||||
|
|
||||||
# Label above portal
|
|
||||||
var lbl := Label.new()
|
|
||||||
lbl.text = "START RUN"
|
|
||||||
lbl.position = Vector2(-36, -195)
|
|
||||||
contents.add_child(lbl)
|
|
||||||
|
|
||||||
_portal_prompt = Label.new()
|
_portal_prompt = Label.new()
|
||||||
_portal_prompt.text = "[E] Start Run"
|
_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
|
_portal_prompt.visible = false
|
||||||
contents.add_child(_portal_prompt)
|
contents.add_child(_portal_prompt)
|
||||||
|
|
||||||
# Trigger zone
|
# Trigger zone for interaction
|
||||||
var area := Area2D.new()
|
var area := Area2D.new()
|
||||||
area.collision_layer = 0
|
area.collision_layer = 0
|
||||||
area.collision_mask = 2
|
area.collision_mask = 2
|
||||||
|
|
||||||
var cs := CollisionShape2D.new()
|
var cs := CollisionShape2D.new()
|
||||||
var rs := RectangleShape2D.new()
|
var rs := RectangleShape2D.new()
|
||||||
rs.size = Vector2(80, 60)
|
rs.size = Vector2(80, 100)
|
||||||
cs.shape = rs
|
cs.shape = rs
|
||||||
area.add_child(cs)
|
area.add_child(cs)
|
||||||
area.position = Vector2(0, -150)
|
area.position = Vector2(0, -150)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ var _slick_count: int = 0
|
|||||||
|
|
||||||
# Knockback — applied by enemies (e.g., Hivemind pulse)
|
# Knockback — applied by enemies (e.g., Hivemind pulse)
|
||||||
var _knockback_velocity: Vector2 = Vector2.ZERO
|
var _knockback_velocity: Vector2 = Vector2.ZERO
|
||||||
|
var _knockback_flash_timer: float = 0.0
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
add_to_group("player")
|
add_to_group("player")
|
||||||
@@ -62,8 +63,9 @@ func _handle_movement() -> void:
|
|||||||
input_dir = input_dir.normalized()
|
input_dir = input_dir.normalized()
|
||||||
|
|
||||||
if _slick_count > 0:
|
if _slick_count > 0:
|
||||||
# Oil slick — momentum-based: velocity slowly drifts toward desired direction
|
# Oil slick — 40% slower + momentum-based movement
|
||||||
velocity = velocity.lerp(input_dir * stats.speed, 0.08)
|
var slick_speed := stats.speed * 0.6
|
||||||
|
velocity = velocity.lerp(input_dir * slick_speed, 0.12)
|
||||||
else:
|
else:
|
||||||
velocity = input_dir * stats.speed
|
velocity = input_dir * stats.speed
|
||||||
|
|
||||||
@@ -168,6 +170,12 @@ func _handle_iframes(delta: float) -> void:
|
|||||||
body.modulate = Color(1, 1, 1, 1)
|
body.modulate = Color(1, 1, 1, 1)
|
||||||
return
|
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
|
# Shield active — cyan tint
|
||||||
if _shield_active:
|
if _shield_active:
|
||||||
body.modulate = Color(0.4, 0.9, 1.0)
|
body.modulate = Color(0.4, 0.9, 1.0)
|
||||||
@@ -216,6 +224,10 @@ func take_damage(amount: float) -> void:
|
|||||||
|
|
||||||
func _die() -> void:
|
func _die() -> void:
|
||||||
if RunManager.run_active:
|
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)
|
RunManager.end_run(false)
|
||||||
else:
|
else:
|
||||||
# In hub/practice — signal for practice room handling, then heal
|
# In hub/practice — signal for practice room handling, then heal
|
||||||
@@ -277,6 +289,7 @@ func exit_slick() -> void:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
func apply_knockback(force: Vector2) -> void:
|
func apply_knockback(force: Vector2) -> void:
|
||||||
_knockback_velocity = force
|
_knockback_velocity = force
|
||||||
|
_knockback_flash_timer = 0.25 # brief magenta flash to show player was pushed
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Room event handlers
|
# 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_death_connections.append({"node": boss, "signal": "tree_exited", "callable": callable})
|
||||||
_enemy_count = 1
|
_enemy_count = 1
|
||||||
|
|
||||||
|
# Spawn exit door on top wall (visible but closed until boss defeated)
|
||||||
|
_spawn_floor_exit_door()
|
||||||
|
|
||||||
|
|
||||||
func _on_enemy_died() -> void:
|
func _on_enemy_died() -> void:
|
||||||
# Guard against deferred callback running after room is freed
|
# Guard against deferred callback running after room is freed
|
||||||
@@ -335,29 +338,33 @@ func _on_enemy_died() -> void:
|
|||||||
AudioManager.play("room_clear")
|
AudioManager.play("room_clear")
|
||||||
if data.type == RoomData.RoomType.BOSS:
|
if data.type == RoomData.RoomType.BOSS:
|
||||||
_spawn_body_part_drop()
|
_spawn_body_part_drop()
|
||||||
_spawn_floor_exit()
|
_open_floor_exit()
|
||||||
room_cleared.emit()
|
room_cleared.emit()
|
||||||
EventBus.room_cleared.emit(data.id)
|
EventBus.room_cleared.emit(data.id)
|
||||||
_update_door_locks()
|
_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
|
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 = 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:
|
if is_final_floor:
|
||||||
_floor_exit_door.setup_evacuation_exit()
|
_floor_exit_door.setup_evacuation_exit()
|
||||||
else:
|
else:
|
||||||
# Next sector: floor 1 -> SECTOR 2, floor 2 -> SECTOR 3
|
|
||||||
_floor_exit_door.setup_sector_exit(RunManager.current_floor + 1)
|
_floor_exit_door.setup_sector_exit(RunManager.current_floor + 1)
|
||||||
|
|
||||||
_floor_exit_door.door_entered.connect(_on_floor_exit_entered)
|
_floor_exit_door.door_entered.connect(_on_floor_exit_entered)
|
||||||
contents.add_child(_floor_exit_door)
|
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
|
# Open the door now that boss is dead
|
||||||
|
if _floor_exit_door != null:
|
||||||
_floor_exit_door.open()
|
_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.collision_mask = 2 | 8 # player (2) + enemies (8)
|
||||||
slick.position = pos
|
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 cs := CollisionShape2D.new()
|
||||||
var sh := CircleShape2D.new()
|
var sh := RectangleShape2D.new()
|
||||||
sh.radius = radius
|
sh.size = Vector2(radius_x * 1.6, radius_y * 1.6) # slightly smaller than visual
|
||||||
cs.shape = sh
|
cs.shape = sh
|
||||||
slick.add_child(cs)
|
slick.add_child(cs)
|
||||||
|
|
||||||
# Visual — circle matching collision shape
|
# Visual — oval shape
|
||||||
var poly := Polygon2D.new()
|
var poly := Polygon2D.new()
|
||||||
var pts := PackedVector2Array()
|
var pts := PackedVector2Array()
|
||||||
var segs := 14
|
var segs := 16
|
||||||
for i in segs:
|
for i in segs:
|
||||||
var a := i * TAU / 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.polygon = pts
|
||||||
poly.color = Color(0.04, 0.07, 0.14, 0.78)
|
poly.color = Color(0.04, 0.07, 0.14, 0.78)
|
||||||
slick.add_child(poly)
|
slick.add_child(poly)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const EXIT_LIGHT := Color(0.2, 0.95, 0.4) # bright green exit
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Configuration
|
# Configuration
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
enum DoorType { ENTRY, SECTOR_EXIT, EVACUATION_EXIT }
|
enum DoorType { ENTRY, SECTOR_EXIT, EVACUATION_EXIT, HUB_PORTAL }
|
||||||
|
|
||||||
var door_type: DoorType = DoorType.ENTRY
|
var door_type: DoorType = DoorType.ENTRY
|
||||||
var sector_label: String = "" # "SECTOR 2", "SECTOR 3", or "EXIT"
|
var sector_label: String = "" # "SECTOR 2", "SECTOR 3", or "EXIT"
|
||||||
@@ -78,6 +78,17 @@ func setup_evacuation_exit() -> void:
|
|||||||
_build_trigger()
|
_build_trigger()
|
||||||
close() # starts closed until boss dies
|
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
|
# Build components
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user