This commit is contained in:
parent
cb2d1a099f
commit
30531bcd52
|
|
@ -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 1→2.
|
||||
* 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 0–1 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue