import { Component, type Entity } from "../core/world"; import type { RPGVariables } from "../types"; import { action, component } from "../utils/decorators"; // ── FactionMember ───────────────────────────────────────────────────────────── @component export class FactionMember extends Component<{ factionId: string }> { constructor(factionId: string) { super({ factionId }); } get factionId(): string { return this.state.factionId; } override getVariables(): RPGVariables { return { [this.factionId]: true, } } } // ── Reputation ──────────────────────────────────────────────────────────────── @component export class Reputation extends Component<{ factionId: string; score: number }> { constructor(factionId: string, score = 0) { super({ factionId, score }); } get score(): number { return this.state.score; } get factionId(): string { return this.state.factionId; } adjust(delta: number): void { this.state.score += delta; } override getVariables(): RPGVariables { return { [this.factionId]: this.score, } } } //── FactionManager ──────────────────────────────────────────────────────────── @component export class FactionManager extends Component<{}> { constructor() { super({}); } @action join(factionId: string): void { Factions.join(this.entity, factionId); } @action leave(factionId: string): void { Factions.leave(this.entity, factionId); } @action setReputation({ factionId, value }: { factionId: string, value: number }): void { Factions.setReputation(this.entity, factionId, value); } @action adjustReputation({ factionId, value }: { factionId: string, value: number }): void { Factions.adjustReputation(this.entity, factionId, value); } } // ── Factions namespace ──────────────────────────────────────────────────────── export namespace Factions { function addFactionManager(entity: Entity) { if (!entity.has(FactionManager)) { entity.add(new FactionManager()); } } export function join(entity: Entity, factionId: string): void { addFactionManager(entity); const existing = entity.get(FactionMember, (c) => c.factionId === factionId); if (!existing) { entity.add(new FactionMember(factionId)); } } export function leave(entity: Entity, factionId: string): void { addFactionManager(entity); entity.removeAll(FactionMember, (c) => c.factionId === factionId); } export function isMember(entity: Entity, factionId: string): boolean { return entity.has(FactionMember, (c) => c.factionId === factionId); } export function getFactions(entity: Entity): string[] { return entity.getAll(FactionMember).map(m => m.factionId); } export function getReputation(entity: Entity, factionId: string): number { return entity.get(Reputation, (c) => c.factionId === factionId)?.score ?? 0; } export function setReputation(entity: Entity, factionId: string, value: number): void { addFactionManager(entity); const existing = entity.get(Reputation, (c) => c.factionId === factionId); if (existing) { existing.state.score = value; } else { entity.add(new Reputation(factionId, value)); } } export function adjustReputation(entity: Entity, factionId: string, delta: number): void { addFactionManager(entity); const existing = entity.get(Reputation, (c) => c.factionId === factionId); if (existing) { existing.adjust(delta); } else { entity.add(new Reputation(factionId, delta)); } } /** Returns the observer's minimum reputation across all of target's factions. * Returns null if target has no FactionMember components. */ export function getReputationBetween(observer: Entity, target: Entity): number | null { const factions = getFactions(target); if (factions.length === 0) return null; return Math.min(...factions.map(f => getReputation(observer, f))); } }