Files
sijcdusmode/scripts/walker.gd
T
sijcdu 0d2bccc3a9 Monsters are now unkillable and fire a fixed bullet pattern; add periodic lasers
Removed the stomp-kill from walker.gd: landing on an enemy now just costs a heart like any other contact, so the only way past is to weave through the pattern or use the key throw + W warp. Enemies fire a fan of bullets every fire_interval whose directions are computed only from the facing and the exported spread, never from the player, so every volley traces the identical path and can be memorised. Bullets are code-generated circles (no art needed), die on hitting terrain via a probe raycast, and are freed after their lifetime. New laser prefab cycles wait -> blinking warmup -> firing, and only damages during the firing phase.
2026-10-07 22:12:14 +08:00

111 lines
3.6 KiB
GDScript

extends CharacterBody2D
## 小怪:在平台上左右巡逻,并且按固定节奏发射弹幕。
## **打不死** —— 从上方踩上去也一样会被扣血。
## 弹道方向是写死的(不追着玩家走),所以每一波的弹道完全一样,
## 玩家可以背下空隙走位躲过去,或者用「扔钥匙 + W 传送」直接穿过去。
const BULLET := preload("res://scenes/prefabs/enemies/bullet.tscn")
@export_category("巡逻")
@export var speed: float = 28.0
## 巡逻方向:-1 向左,1 向右
@export var start_direction: int = -1
## 掉头后的冷却时间:防止贴着墙时每帧都掉头、结果原地抖动
@export var turn_cooldown: float = 0.25
## 接触玩家扣几颗心
@export var damage: int = 1
## 速度设成 0 就变成固定炮台(弹幕就完全静止可背板了)
@export_category("弹幕")
@export var fires_bullets: bool = true
## 发射间隔(秒)
@export var fire_interval: float = 2.0
## 开场后第一次发射前的延迟
@export var fire_delay: float = 1.0
## 每次发射几颗
@export var bullet_count: int = 3
## 扇面张开角度
@export var spread_deg: float = 60.0
## 扇面中心朝向(度):0=右 90=下 180=左 270=上
@export var base_angle_deg: float = 0.0
## 开火方向跟随巡逻朝向(打开时 base_angle_deg 只当上下偏移用)
@export var aim_along_facing: bool = true
@export var bullet_speed: float = 70.0
@export var bullet_color: Color = Color(1.0, 0.35, 0.7)
var direction: int = -1
var _turn_timer: float = 0.0
var _fire_timer: float = 1.0
@onready var sprite: Sprite2D = $Sprite
@onready var floor_probe: RayCast2D = $FloorProbe
@onready var touch_area: Area2D = $TouchArea
func _ready() -> void:
add_to_group("enemy")
direction = -1 if start_direction < 0 else 1
_apply_direction()
_fire_timer = fire_delay
touch_area.body_entered.connect(_on_touch)
func _physics_process(delta: float) -> void:
if _turn_timer > 0.0:
_turn_timer -= delta
if not is_on_floor():
velocity += get_gravity() * delta
velocity.x = float(direction) * speed
# 撞到墙 或 前方脚下没有地板 -> 掉头(带冷却,掉头后先走一段再说)
if _turn_timer <= 0.0:
if is_on_wall() or (is_on_floor() and not floor_probe.is_colliding()):
direction = -direction
_turn_timer = turn_cooldown
_apply_direction()
move_and_slide()
if fires_bullets:
_fire_timer -= delta
if _fire_timer <= 0.0:
_fire_timer = maxf(fire_interval, 0.05)
fire()
## 按固定扇面发射一批子弹。方向是算死的,不参考玩家位置,
## 所以每一波的弹道都一样,玩家能背下来找空隙。
func fire() -> void:
var n: int = maxi(bullet_count, 1)
var base_deg: float = base_angle_deg
if aim_along_facing:
base_deg = 0.0 if direction > 0 else 180.0
var base := deg_to_rad(base_deg)
var spread := deg_to_rad(spread_deg)
for i in n:
var t: float = 0.0 if n == 1 else (float(i) / float(n - 1) - 0.5)
var ang := base + spread * t
var bullet := BULLET.instantiate()
# 这两个属性必须在 add_child 之前设好,子弹的 _ready 会拿它们生成贴图
bullet.bullet_color = bullet_color
bullet.speed = bullet_speed
get_parent().add_child(bullet)
bullet.global_position = global_position + Vector2(0.0, -2.0)
bullet.launch(Vector2(cos(ang), sin(ang)), bullet_speed)
func _apply_direction() -> void:
if sprite != null:
sprite.flip_h = direction > 0
if floor_probe != null:
floor_probe.position.x = float(direction) * 6.0
func _on_touch(body: Node2D) -> void:
# 打不死:踩上去也一样只是扣血
if body.has_method("hurt"):
body.hurt(damage)