Files
urm03/scripts/grunt.gd
T
vh 380088c694 feat: tracer bullet — greybox arena, positioning piloting, auto-fire KINETIC weapon
One-screen 640x360 arena (walls, grid floor, fixed view), mech with
positioning-only movement (WASD/arrows, accel/decel, no aim), auto-fire
KINETIC hardpoint (nearest-target, visible range ring) firing slugs
that damage + knock back, untagged grunt trickle spawner with contact
damage, hull bar HUD, scene-reload on hull zero.

Headless smoke gate: tests/smoke.tscn asserts boot, piloting movement,
spawning, input-free auto-fire, grunt lethality, knockback displacement.
3/3 passes on nh3-dev headless 4.7.1.
2026-08-06 22:21:10 -07:00

59 lines
1.5 KiB
GDScript

extends CharacterBody2D
## Untagged grunt — the Act-1 learning-language enemy. Walks at the
## mech, deals contact damage on a tick. Untagged = neutral hue, no
## tag behavior. Knockback state lives here so KINETIC (and later
## chemistry) has something to shove.
signal died
@export var speed := 70.0
@export var hp := 3.0
@export var contact_damage := 5.0
@export var contact_interval := 0.5
@export var knockback_decay := 5.0
var _knockback := Vector2.ZERO
var _contact_cd := 0.0
var _flash := 0.0
var _mech: Node2D
func _ready() -> void:
add_to_group("enemies")
_mech = get_tree().get_first_node_in_group("mech")
func _physics_process(delta: float) -> void:
_contact_cd -= delta
var seek := Vector2.ZERO
if is_instance_valid(_mech):
seek = (_mech.global_position - global_position).normalized() * speed
_knockback = _knockback.lerp(Vector2.ZERO, 1.0 - exp(-knockback_decay * delta))
velocity = seek + _knockback
move_and_slide()
for i in get_slide_collision_count():
var collider := get_slide_collision(i).get_collider()
if collider == _mech and _contact_cd <= 0.0:
_mech.take_damage(contact_damage)
_contact_cd = contact_interval
if _flash > 0.0:
_flash -= delta
queue_redraw()
func hit(damage: float, kb: Vector2) -> void:
hp -= damage
_knockback += kb
_flash = 0.12
queue_redraw()
if hp <= 0.0:
died.emit()
queue_free()
func _draw() -> void:
var body := Color(0.95, 0.95, 0.95) if _flash > 0.0 else Color(0.45, 0.47, 0.4)
draw_circle(Vector2.ZERO, 8.0, body)