1
0
Fork 0
This commit is contained in:
Pabloader 2026-04-29 12:24:29 +00:00
parent cb2d1a099f
commit 30531bcd52
1 changed files with 69 additions and 0 deletions

View File

@ -0,0 +1,69 @@
import { component } from "../core/registry";
import { Component } from "../core/world";
import { action, variable } from "../utils/decorators";
/**
* How much XP is required to advance from level N to N+1.
*
* - `number[]` explicit table; `values[0]` = XP for level 12.
* Max level = `values.length + 1`. Past the end the entity is at max level.
* - `{ base, factor }` geometric: XP[N] = floor(base * factor^(N-1)).
* No max level.
*/
export type ThresholdSpec = number[] | { base: number; factor: number };
function xpForStep(spec: ThresholdSpec, level: number): number | null {
if (Array.isArray(spec)) {
const idx = level - 1;
return idx < spec.length ? spec[idx] : null;
}
return Math.floor(spec.base * Math.pow(spec.factor, level - 1));
}
@component
export class Experience extends Component<{
xp: number;
level: number;
xpAtLevel: number; // total XP accumulated when current level was reached
spec: ThresholdSpec;
}> {
constructor(spec: ThresholdSpec) {
super({ xp: 0, level: 1, xpAtLevel: 0, spec });
}
/** Current level, starting at 1. Primary variable for conditions: `experience >= 5`. */
@variable get level(): number { return this.state.level; }
/** Total accumulated XP (never resets). */
@variable('.') get xp(): number { return this.state.xp; }
/** XP accumulated within the current level. */
@variable get xpInLevel(): number { return this.state.xp - this.state.xpAtLevel; }
/** XP remaining until the next level, or `null` at max level. */
get xpToNext(): number | null {
const needed = xpForStep(this.state.spec, this.state.level);
return needed === null ? null : needed - this.xpInLevel;
}
/** Progress toward the next level as a 01 fraction. `1` at max level. */
@variable get progress(): number {
const needed = xpForStep(this.state.spec, this.state.level);
if (needed === null) return 1;
return Math.min(this.xpInLevel / needed, 1);
}
/** Add XP and emit `'levelup'` with `{ level, prev }` for each level gained. */
@action
award(xp: number): void {
this.state.xp += xp;
while (true) {
const needed = xpForStep(this.state.spec, this.state.level);
if (needed === null) break;
if (this.xpInLevel < needed) break;
this.state.xpAtLevel += needed;
const prev = this.state.level++;
this.emit('levelup', { level: this.state.level, prev });
}
}
}