1b3d61793d
walker.gd lost its firing code entirely: monsters now only patrol and hurt on contact, so the only way past is footwork or the key throw + W warp. New bullet_ring prefab fires a set number of bullets evenly spread over a fixed arc on a timer, with directions derived only from its own exports, so every volley is identical and the pattern can be memorised; the level places one at cell (12,12) aimed upward with the 12 bullets spread over 180 degrees. laser.gd gained always_on, defaulting to true, so the five copied lasers are now static hazards that are always lit and damage anything overlapping them rather than blinking on and off. Bullets now stop when they spawn inside terrain.
91 lines
2.7 KiB
GDScript
91 lines
2.7 KiB
GDScript
extends Area2D
|
||
|
||
## 激光。两种模式:
|
||
## - always_on = true(默认):**固定障碍** —— 一直亮着,碰到就扣血,只能绕开或用钥匙传送穿过去
|
||
## - always_on = false:循环「蓄力闪烁 -> 发射 -> 熄灭」,需要卡时间通过
|
||
## 光束从自己的原点沿 +X 方向伸出,想改朝向就转节点本身。
|
||
|
||
enum State { WAIT, WARMUP, FIRING }
|
||
|
||
## 固定障碍模式(一直亮着)
|
||
@export var always_on: bool = true
|
||
@export var length: float = 90.0
|
||
@export var thickness: float = 6.0
|
||
@export var damage: int = 1
|
||
## 蓄力(闪烁警告)时长,只有循环模式用
|
||
@export var warmup: float = 0.8
|
||
## 发射持续时长,只有循环模式用
|
||
@export var fire_time: float = 0.35
|
||
## 两次发射之间的间隔,只有循环模式用
|
||
@export var cooldown: float = 1.8
|
||
## 开场后第一次发射前的延迟,只有循环模式用
|
||
@export var start_delay: float = 1.0
|
||
@export var laser_color: Color = Color(1.0, 0.25, 0.25)
|
||
@export var warn_color: Color = Color(1.0, 0.8, 0.4)
|
||
|
||
var _state: int = State.WAIT
|
||
var _timer: float = 0.0
|
||
var _first: bool = true
|
||
var _beam: Polygon2D = null
|
||
|
||
|
||
func _ready() -> void:
|
||
add_to_group("hazard")
|
||
_beam = get_node_or_null("Beam") as Polygon2D
|
||
var half := thickness * 0.5
|
||
if _beam != null:
|
||
_beam.polygon = PackedVector2Array([
|
||
Vector2(0.0, -half), Vector2(length, -half),
|
||
Vector2(length, half), Vector2(0.0, half)])
|
||
_beam.color = laser_color
|
||
_beam.visible = always_on
|
||
var hit := get_node_or_null("HitBox") as CollisionShape2D
|
||
if hit != null:
|
||
var rect := RectangleShape2D.new()
|
||
rect.size = Vector2(length, thickness)
|
||
hit.shape = rect
|
||
hit.position = Vector2(length * 0.5, 0.0)
|
||
|
||
|
||
func _physics_process(delta: float) -> void:
|
||
if always_on:
|
||
if _beam != null:
|
||
_beam.visible = true
|
||
_beam.color = laser_color
|
||
_hurt_overlaps()
|
||
return
|
||
|
||
_timer += delta
|
||
match _state:
|
||
State.WAIT:
|
||
var wait: float = start_delay if _first else cooldown
|
||
if _timer >= wait:
|
||
_first = false
|
||
_state = State.WARMUP
|
||
_timer = 0.0
|
||
State.WARMUP:
|
||
if _beam != null:
|
||
_beam.color = warn_color
|
||
_beam.visible = fmod(_timer, 0.16) < 0.08
|
||
if _timer >= warmup:
|
||
_state = State.FIRING
|
||
_timer = 0.0
|
||
if _beam != null:
|
||
_beam.color = laser_color
|
||
_beam.visible = true
|
||
State.FIRING:
|
||
_hurt_overlaps()
|
||
if _timer >= fire_time:
|
||
_state = State.WAIT
|
||
_timer = 0.0
|
||
if _beam != null:
|
||
_beam.visible = false
|
||
|
||
|
||
## 每帧检查压在光柱里的东西(不能只靠 body_entered,
|
||
## 因为玩家可能本来就在光柱里,那样不会触发 entered)
|
||
func _hurt_overlaps() -> void:
|
||
for body in get_overlapping_bodies():
|
||
if body.has_method("hurt"):
|
||
body.hurt(damage)
|