File size: 3,475 Bytes
b8a03fa | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | import { clamp } from './deterministic.js';
import { emitWorldEvent, findRequired } from './state-utils.js';
import type { CommandResult, EntityId, UniverseState } from './types.js';
export class FactionSystem {
constructor(private state: UniverseState) {}
replaceState(state: UniverseState): void {
this.state = state;
}
changePlayerReputation(factionId: EntityId, delta: number, reason: string): CommandResult {
const faction = findRequired(this.state.factions, factionId, 'Faction');
const previous = this.state.player.factionReputation[faction.id] ?? 0;
const next = clamp(previous + delta, -100, 100);
this.state.player.factionReputation[faction.id] = next;
const event = emitWorldEvent(this.state, 'faction-reputation-changed', faction.id, this.state.player.id, `${faction.name} reputation changed by ${delta}: ${reason}`, { data: { previous, next, delta } });
return { ok: true, message: event.summary, eventIds: [event.id] };
}
setRelation(firstFactionId: EntityId, secondFactionId: EntityId, value: number, reason: string): CommandResult {
if (firstFactionId === secondFactionId) return { ok: false, message: 'A faction cannot set a relation with itself.', eventIds: [] };
const first = findRequired(this.state.factions, firstFactionId, 'Faction');
const second = findRequired(this.state.factions, secondFactionId, 'Faction');
const normalized = clamp(value, -100, 100);
first.relations[second.id] = normalized;
second.relations[first.id] = normalized;
const event = emitWorldEvent(this.state, normalized >= 25 ? 'faction-agreement' : 'faction-relation-changed', first.id, second.id, `${first.name} and ${second.name} relation set to ${normalized}: ${reason}`, { data: { value: normalized } });
return { ok: true, message: event.summary, eventIds: [event.id] };
}
declareConflict(firstFactionId: EntityId, secondFactionId: EntityId, reason: string): CommandResult {
const first = findRequired(this.state.factions, firstFactionId, 'Faction');
const second = findRequired(this.state.factions, secondFactionId, 'Faction');
if (!first.activeConflictIds.includes(second.id)) first.activeConflictIds.push(second.id);
if (!second.activeConflictIds.includes(first.id)) second.activeConflictIds.push(first.id);
first.relations[second.id] = -80;
second.relations[first.id] = -80;
const event = emitWorldEvent(this.state, 'faction-conflict-declared', first.id, second.id, `${first.name} entered conflict with ${second.name}: ${reason}`, { severity: 0.8 });
return { ok: true, message: event.summary, eventIds: [event.id] };
}
sanction(factionId: EntityId, targetId: EntityId, active: boolean): CommandResult {
const faction = findRequired(this.state.factions, factionId, 'Faction');
faction.sanctions = active ? [...new Set([...faction.sanctions, targetId])] : faction.sanctions.filter((id) => id !== targetId);
for (const market of this.state.economy.markets.filter((candidate) => candidate.factionId === faction.id)) {
market.sanctions = active ? [...new Set([...market.sanctions, targetId])] : market.sanctions.filter((id) => id !== targetId);
}
const event = emitWorldEvent(this.state, active ? 'faction-sanction-imposed' : 'faction-sanction-lifted', faction.id, targetId, `${faction.name} ${active ? 'imposed' : 'lifted'} sanctions on ${targetId}.`);
return { ok: true, message: event.summary, eventIds: [event.id] };
}
}
|