7.4 KiB
E2.M2 — XpAwardAndLevelEngine
Summary
| Field | Value |
|---|---|
| Module ID | E2.M2 |
| Epic | Epic 2 — Skills and Progression Framework |
| Stage target | Prototype |
| Status | In Progress (see dependency register) |
Implementation snapshot
Linear (label E2.M2): Epic 2 project — Slice 2: NEO-37 (progression read snapshot) → NEO-38 (apply grant + allowlist + level-up) → NEO-39 (data-driven LevelCurve + CI); NEO-40 (telemetry hook sites) after NEO-38, parallel with NEO-39. Slice 3 integration (multi-label where noted): NEO-41, NEO-42, NEO-43, NEO-44.
NEO-37 landed: versioned GET /game/players/{id}/skill-progression (implementation plan) — SkillProgressionSnapshotApi / SkillProgressionSnapshotDtos under server/NeonSprawl.Server/Game/Skills/; one row per ISkillDefinitionRegistry skill; 404 when the player has no position (same gate as GET …/cooldown-snapshot). Manual QA NEO-37.md; server README — Skill progression snapshot (NEO-37).
NEO-38 landed: versioned POST /game/players/{id}/skill-progression (implementation plan) — applies one grant per request with ISkillDefinitionRegistry + allowedXpSourceKinds validation, stable deny reasonCode values, dual persistence (IPlayerSkillProgressionStore: in-memory / Postgres + V003__player_skill_progression.sql), GET merges stored XP with the registry; success payload includes levelUps (see README for multi-step semantics). Bruno bruno/neon-sprawl-server/skill-progression/ (GET + POST + deny); manual QA NEO-38.md; server README — Skill progression grant (NEO-38).
NEO-39 landed: data-driven LevelCurve at content/skills/prototype_level_curve.json with schema content/schemas/level-curve.schema.json; CI validation extended in scripts/validate_content.py; server boot now fail-fast loads curve content and uses it for XP→level resolution in skill progression GET/POST paths (no code edit required for threshold tuning).
NEO-40 landed: comment-only telemetry hook sites in SkillProgressionSnapshotApi (POST …/skill-progression) for future catalog events xp_grant and level_up (implementation plan); manual QA NEO-40.md.
NEO-41 landed: prototype gather → skill XP — successful POST …/interact when interactable kind is resource_node grants salvage XP (sourceKind: activity, 10 XP per interact) via shared SkillProgressionGrantOperations (implementation plan); manual QA NEO-41.md; server README — Interaction (gather).
NEO-42 landed (prep): RefineActivitySkillXpGrant + RefineSkillXpConstants + test RefineActivityDeniedRegistryWebApplicationFactory — craft/refine success is intended to award refine XP (sourceKind: activity, 10 XP) via the same SkillProgressionGrantOperations stack as NEO-38 (implementation plan); manual QA NEO-42.md; server README — Craft / refine hook (NEO-42). E3.M2 must invoke GrantOnSuccessfulCraftOrRefine on real CraftResult success for end-to-end behavior (no craft route in repo at this slice).
Still backlog within this module: Slice 3 NEO-43 (and NEO-44 gig path); NEO-42 end-to-end wiring awaits E3.M2 success handler. Keep this paragraph and documentation tracking in sync as Slice 2 stories merge.
Purpose
Central server-authoritative engine for non-combat skill progression: applying XP grants, resolving levels against data-driven curves, and emitting level-up events. Gathering and crafting integrate as callers for skill XP. Combat encounters advance gigs (gig XP), not SkillDef lines. Mission rewards may call this API for skill XP when the payout lists a skill grant. Callers are not hard dependencies for implementing the engine core (see Epic 2 Slice 2 vs Slice 3).
Responsibilities
- Apply
XpGrantEvent(or equivalent) per skill with validatedskillIdfrom E2.M1. Reject grants whensourceKindis not in the targetSkillDef.allowedXpSourceKinds. - Resolve experience against
LevelCurve/ threshold tables. - Emit
LevelUpEventwhen thresholds are crossed. - Support data-driven curves; reload policy: Data reload and telemetry operations policy (boot load default; optional dev reload).
Key contracts
| Contract | Role |
|---|---|
XpGrantEvent |
Structured XP award: skillId (must exist in registry), amount, sourceKind (must appear in that skill’s allowedXpSourceKinds), optional modifiers. |
LevelCurve |
Thresholds or formula per skill or global policy. |
LevelUpEvent |
Notification for UI, unlocks, and downstream systems. |
Module dependencies
- E2.M1 — SkillDefinitionRegistry:
SkillDef, categories, allowedXpSourceKinds, and validation of skill IDs.
Dependents (by design)
- E5.M1 — CombatRulesEngine: gig XP from combat (not
SkillDefXP via E2.M2). - E3.M1 / E3.M2 — Gathering and crafting award XP when wired in Slice 3.
- E7.M2 — RewardAndUnlockRouter routes quest rewards including XP through E2.M2.
- E2.M3 / E2.M4 — Mastery and pacing layers build on level/XP state (pre-production).
Related implementation slices
- Epic 2 Slice 2 — XP engine (E2.M2 core): implement contracts and tests without requiring Epic 3 or 5 modules.
- Epic 2 Slice 3 — XP award integration: E3.M1 / E3.M2 call the skill XP API with
sourceKind:activity(where allowed per skill); E7.M2 / mission rewards usemission_reward(etc.) as configured; combat resolves gig progression separately from E2.M2.
Risks and telemetry
- Runaway leveling in one skill: diminishing returns per slice; monitor distribution via E9.M2 when available.
- Telemetry:
xp_grant,level_up, time-to-first-level-up once real activity fires.
Source anchors
- Master plan:
neon_sprawl_vision.plan.md— Core Epic Map, Epic 2. - Module dependency register