# OpenSpec Change: enterprise-gaming-hitbox - **Change ID**: enterprise-gaming-hitbox - **Package**: `@suaveplan/hitbox` - **Category**: gaming/combat - **Target path**: `gaming/combat/hitbox` - **Status**: PROPOSED - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/hitbox.md` --- ## Summary Deliver `@suaveplan/hitbox` on the genesis branch from scratch at enterprise quality. The package provides hierarchical hit detection with per-bone AABB/sphere hitboxes, raycasting, and swept-shape tests. A `HitboxRig` attaches typed hitbox volumes to named bone slots; `HitboxQuery` tests ray-vs-AABB and ray-vs-sphere intersections using separating-axis test and returns hit results sorted by distance with zone metadata (head/torso/limb). Genesis adds Zod hitbox descriptor schemas, structured errors, OTel intersection-test counter, and 100% coverage with property tests for ray-sphere/AABB correctness. --- ## Current-state audit (genesis) Package does not exist on genesis yet — fresh build. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/error` (`workspace:^`) | `HitboxError`; `HITBOX.*` codes | | `@suaveplan/types` (`workspace:^`) | Shared primitives | | `@suaveplan/spatial-hash` (`workspace:^`) | Broad-phase culling before per-hitbox tests | | `zod` (`catalog:forms`) | Hitbox descriptor schemas | | `@opentelemetry/api` (`catalog:observability`) | Intersection test counter | --- ## Public API surface ```ts type HitboxShape = 'aabb' | 'sphere' | 'capsule' type HitboxZone = 'head' | 'neck' | 'torso' | 'upper_arm' | 'forearm' | 'hand' | 'thigh' | 'shin' | 'foot' interface HitboxDescriptor { boneName: string shape: HitboxShape zone: HitboxZone damageMultiplier?: number // 1.0 = normal, 2.0 = headshot // AABB halfExtents?: Vec3 // Sphere radius?: number // Capsule halfHeight?: number capsuleRadius?: number offset?: Vec3 } function createHitboxRig(descriptors: HitboxDescriptor[]): HitboxRig interface HitboxRig { updateBone(boneName: string, worldPosition: Vec3, worldRotation: Quat): void getHitboxes(): HitboxVolume[] } interface HitboxVolume { readonly descriptor: HitboxDescriptor readonly worldPosition: Vec3 readonly worldRotation: Quat } // Ray query interface Ray { origin: Vec3 direction: Vec3 maxDistance?: number } function queryRay(ray: Ray, rigs: HitboxRig[]): HitResult[] interface HitResult { entityId?: string distance: number hitPoint: Vec3 zone: HitboxZone damageMultiplier: number normal: Vec3 } // Sweep query function querySphere(center: Vec3, radius: number, rigs: HitboxRig[]): HitResult[] function queryAABB(min: Vec3, max: Vec3, rigs: HitboxRig[]): HitResult[] ``` ### Error codes ```ts type HitboxErrorCode = | 'HITBOX_BONE_NOT_FOUND' | 'HITBOX_INVALID_SHAPE' | 'HITBOX_RIG_EMPTY' | 'HITBOX_QUERY_FAILED' ``` --- ## New additions for genesis 1. **Separating-axis AABB test** — correct oriented AABB test using world rotation. 2. **Damage multiplier per zone** — `head` defaults to 2.0; others configurable. 3. **Spatial hash broad-phase** — `@suaveplan/spatial-hash` culls rigs before per-hitbox tests. 4. **OTel** — `hitbox.ray_tests_total` counter; `hitbox.query_duration_ms` histogram. --- ## File layout ```text gaming/combat/hitbox/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts constants/index.ts types/common.ts types/common.md core/ hitbox-rig/ hitbox-rig.ts hitbox-rig.md hitbox-rig.types.ts hitbox-rig.test.ts index.ts query/ query.ts query.md query.types.ts query.test.ts index.ts utils/ intersection.ts intersection.md intersection.test.ts tests/ property.test.ts ``` --- ## Impact **Downstream**: `@suaveplan/weapons` uses `queryRay` for hitscan; `@suaveplan/r3f-combat` renders hitbox debug overlays. Prerequisites: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/spatial-hash`.