70274f1773
Hitbox now builds a DamageEvent (cause + hit position + shaped knock-up) and calls the bull's new receive_damage(), instead of two stringly Object.call()s. receive_damage adapts over the existing pip/knock-up logic so behaviour is identical -- the characterization test drives both the string and typed paths and asserts the same outcomes. Wire fx/damage/harness tests into run_tests.sh + CLAUDE.md. All hitbox guard tests green (stab, bear hitbox, ram, charge). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmG3mQGVXZUPDVapmdbWZ1
63 lines
2.6 KiB
GDScript
63 lines
2.6 KiB
GDScript
class_name DamageEvent
|
|
extends RefCounted
|
|
## The one typed payload for every hit in the game — a bull goring a matador, a bear slam popping
|
|
## the bull, a thrown spear landing. It replaces the scattered, stringly Object.call(&"take_sword_hit",
|
|
## "gored") paths: an emitter (a Hitbox) fills one of these and hands it to the target's
|
|
## receive_damage(event). One shape to read, log and type-check, instead of four call conventions.
|
|
##
|
|
## It serves both victim models the game already has: the bull, which dies on one clean hit
|
|
## (lethal + cause + hit_position), and an enemy, which is launched/ragdolled by an impulse
|
|
## (knockback_dir · knockback_strength + knock_up). A given hit just leaves the fields it doesn't
|
|
## use at their defaults.
|
|
|
|
enum Cause { GENERIC, GORED, MAULED, THROWN, SLAMMED, KICKED, DASHED }
|
|
|
|
## Who dealt the hit — for attribution and for ignoring self-overlap. May be null.
|
|
var source: Node = null
|
|
## What kind of hit; drives the victim's reaction (blood/vignette/FX) and any future resistances.
|
|
var cause: Cause = Cause.GENERIC
|
|
## World contact point, so impact FX spawn where the hit actually landed.
|
|
var hit_position: Vector3 = Vector3.ZERO
|
|
## Planar push direction (need not be normalised). Vector3.ZERO = no horizontal knockback.
|
|
var knockback_dir: Vector3 = Vector3.ZERO
|
|
## Impulse magnitude behind knockback_dir — the enemy launch/ragdoll strength.
|
|
var knockback_strength: float = 0.0
|
|
## Upward launch in m/s (an overhead smash pop, a leap-slam outer ring). 0 = a grounded hit.
|
|
var knock_up: float = 0.0
|
|
## One clean hit ends the run for the bull; most enemy-dealt hits are lethal to it. Enemies that
|
|
## are merely launched (not killed) by the bull's abilities set this false.
|
|
var lethal: bool = true
|
|
|
|
|
|
static func make(kind: Cause, at: Vector3, src: Node = null) -> DamageEvent:
|
|
var e := DamageEvent.new()
|
|
e.cause = kind
|
|
e.hit_position = at
|
|
e.source = src
|
|
return e
|
|
|
|
|
|
## Lower-case name of the cause, for logs and for the bull's existing cause-keyed reactions
|
|
## ("gored" / "mauled" / "thrown") during the migration off string causes.
|
|
func cause_name() -> String:
|
|
return String(Cause.keys()[cause]).to_lower()
|
|
|
|
|
|
## Maps a legacy cause string onto the enum, so a Hitbox's exported damage_cause round-trips
|
|
## exactly (gored/mauled/thrown are the only ones in use). Unknown strings fall back to GENERIC.
|
|
static func cause_from(text: String) -> Cause:
|
|
match text.to_lower():
|
|
"gored":
|
|
return Cause.GORED
|
|
"mauled":
|
|
return Cause.MAULED
|
|
"thrown":
|
|
return Cause.THROWN
|
|
"slammed":
|
|
return Cause.SLAMMED
|
|
"kicked":
|
|
return Cause.KICKED
|
|
"dashed":
|
|
return Cause.DASHED
|
|
return Cause.GENERIC
|