Player Health and Damage - UQcsse3200/2026-studio-2 GitHub Wiki

Player Health and Damage

Introduction

The Player Health and Damage System controls health changes, the invulnerability window after a hit, and the transition from player death to the loss screen. It separates combat rules from hitbox detection, animation and HUD presentation.

Reviewed 15 September 2026 against main 4941dba, which merged PR #145. The player mechanics described below are merged unless explicitly labelled as branch-specific.

Architecture

flowchart LR
    Damage["Damage source"] -->|takeDamage| Stats["CombatStatsComponent"]
    Stats -->|updateHealth| HUD["PlayerStatsDisplay"]
    Stats -->|hurt / death| Animation["PlayerAnimationController"]
    Stats -->|death| Actions["Input and PlayerActions"]
    Animation -->|deathAnimationFinished| Screen["Game screen"]
    Screen -->|gameEnd LOSE| End["Game-end UI"]

CombatStatsComponent

Stores current/max health, attack damage and the invulnerability deadline. It receives damage events and publishes health, hurt and death events.

Collider, Hitbox and HUD

The physical ColliderComponent handles solid contact. The separate HitboxComponent creates a sensor fixture on PhysicsLayer.PLAYER for combat interactions. Changing the ordinary collider’s scale does not also resize that separate sensor via the collider helper.

The player’s rendered/entity width is scaled to 0.6 world units. The merged factory uses collider scale 1 × 1 with density 1.5.

The Player HUD listens to health updates and displays two HP per heart using rounding up: 10 HP shows five hearts and 9 HP still shows five. HUD artwork/display belongs to the UI collaboration; Team 5’s health system supplies the integration events. See inventory documentation for potion items; item use refuses a full-health potion and otherwise heals within the maximum.

Death Presentation

PlayerAnimationController handles the death clip; screen listeners handle removal and the loss screen. The combat component does not construct UI.

Damage and Death Lifecycle

Damage and Invulnerability

The combat component listens for takeDamage with the attacker’s combat stats. An accepted hit(...) subtracts base attack, clamps health to the entity’s own maximum/minimum, and advances the invulnerability deadline.

  • updateHealth notifies dependent systems when health is set.
  • hurt is emitted when an accepted hit actually reduces health.
  • death is emitted when health changes from positive to zero; setting zero again does not repeat that transition.
  • Hits before the invulnerability deadline are ignored; a hit exactly at expiry is accepted.

On a lethal hit, death occurs during the health update before hurt; the animation controller’s dead guard prevents the later hurt event replacing the death clip.

Invulnerability is damage filtering, not collision immunity or an implemented flashing effect. Contact knockback is handled separately and is not automatically suppressed by i-frames. Direct setHealth/addHealth operations bypass the hit timer; an accepted zero-damage hit still advances it.

Player Death

  1. Health crosses from positive to zero and emits death.
  2. Keyboard input rejects further player actions; PlayerActions stops walking and clears queued jumping.
  3. The animation controller plays the one-shot death animation.
  4. On completion, it emits deathAnimationFinished once.
  5. Main/tutorial screen listeners schedule player removal and post gameEnd with the loss state.

This describes input inhibition and the normal animation-to-loss transition, not a guarantee that all physics instantly freezes: vertical velocity is preserved, and the action component has active dash/recovery state. The bow also cancels a charge on death without refunding already-reserved ammunition.

Checkpoint Respawn Is Separate from Death

PlayerFactory also attaches the level team's RespawnComponent. Falling below y = -10 emits respawnAtCheckpoint; the active GameArea repositions the existing player at the last active checkpoint in configuration order, or the default spawn. This path is separate from health reaching zero and completing the loss animation. It does not create a new player or explicitly reset every movement/grapple state.

PR #145 playtesting reported the grapple remaining attached after this repositioning. This is a reviewer-reported issue, not a fix delivered by the regression tests.

Enemy Lifecycle Boundary

The same combat-stat events support damageable enemies, but the bow does not own enemy removal. Team 1’s EnemyDeathComponent listens to updateHealth and schedules removal at zero health. Skeleton factories using it support that lifecycle; legacy NPC factories must be checked separately. Player death waits for the player animation and screen transition rather than using this immediate enemy-removal path.

Tuning

PlayerFactory constructs CombatStatsComponent from configs/player.json:

Setting Current value
Initial / maximum player health 10 HP
Player base attack (used by melee) 2
Post-hit invulnerability 1000 ms

Projectile damage is configured separately in ProjectileFactory; the player’s base attack of 2 does not mean arrows deal 2 damage. The legacy CombatStatsComponent.MAX_HEALTH = 100 constant is not the player’s configured per-instance maximum.

Testing

Relevant test classes:

  • CombatStatsComponentTest: clamping, per-instance health limits, direct/event damage, invulnerability and death transitions.
  • PlayerAnimationControllerTest: death playback, selected blocked events and one-time completion.
  • PlayerCombatIntegrationTest: charge cancellation on death alongside the inventory/weapon flow.

RespawnComponentTest covers threshold/event dispatch, not full player-state reset after respawn. Health/animation/combat tests were included in the 503-test run at c900c8d; see Player Attack System for the revision and verification limits.

The original regression-branch playtest reported damage, player loss and skeleton death working. Exact hurt timing, i-frame duration and integrated player state combinations were not independently measured.

Known Limitations

  • Movement can interrupt hurt animation; see Animations.
  • I-frames filter damage, not knockback, collisions or direct health setters.
  • The death transition does not instantly freeze every physical state.
  • Exact timing and integrated PR #145 state combinations remain unverified by this review.

Relevant Development Links

Contribution Attribution

Rohan Shirkhedkar implemented/integrated player damage, configurable i-frames and health/death hooks. Tristan Sheedy contributed player death animation/input handling and player sizing; Josiah Enns contributed hurt animation. Dhiren Malik contributed projectile-damage integration and combat regressions. UI/HUD work includes Ayooluwa Fadeyibi and the UI team; enemy lifecycle work includes Jordan Conis, Ernie Cheng and Team 1. Checkpoint/respawn and ledge integration belong to the level team. These collaborations are not reassigned wholesale to Team 5.