133 lines
4.6 KiB
TypeScript
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)));
|
|
}
|
|
}
|