1
0
Fork 0
tsgames/src/common/rpg/components/faction.ts

133 lines
4.6 KiB
TypeScript

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)));
}
}