godot-combat-system
Installation
SKILL.md
NEVER Do
- NEVER use direct damage references (
target.health -= 10) — Bypass armor, resistances, and i-frames. AlwaysDamageData+HealthComponent.take_damage. - NEVER forget invincibility frames (i-frames) — Multi-hit shapes otherwise tick every physics frame. Apply a short invuln window after a successful hit.
- NEVER keep hitboxes active permanently — Enable/disable with AnimationPlayer tracks or timed code; permanent monitoring causes ghost hits.
- NEVER use groups for physics-based hit filtering — Prefer collision layers/masks (C++ filter). Groups are secondary logic, not the physics gate.
- NEVER emit damage signals without a DamageData object — Raw numbers lose type, source, knockback, and crit context.
- NEVER use raw strings for elemental damage types — Use
enum/@export_flagsbitfields. String"physical"violates this skill’s own contract. - NEVER use try/catch to validate targets — GDScript has no exceptions. Use
has_method(&"take_damage")/ischecks. - NEVER hardcode hitstun with
OS.delay_msec()— Blocks the OS thread. Use tweens /Engine.time_scale+ignore_time_scaletimers. - NEVER apply RigidBody impulses in
_process()— Use_physics_process/_integrate_forces. - NEVER couple UI lifebars inside the Player script — Emit
health_changed; HUD listens. - NEVER leave CollisionShapes active on dead entities —
set_deferred("disabled", true)on death. - NEVER scale CollisionShapes non-uniformly — Scale the shape resource (
radius,size), not the node transform unevenly. - NEVER use instanced Nodes for base combat stats — Prefer
Resource/RefCountedcontainers;duplicate()per instance. - NEVER use standard strings for high-frequency state names — Prefer
StringName(&"attacking"). - NEVER forget
duplicate()on shared Resource stats — Shared templates = shared health pools.