import type { World } from './world' import { WorldEventBus } from './world' import { YearlyReport, BattleLog, ChronicleEntry, SaveMeta } from '../types/domain' import { marketPrice, buyItem, sellItem, buyTechnique } from './market' import { sendMission, recallAll } from './systems/missions' import { giftNpc, makePeace, marryNpcFamily } from './systems/diplomacy' import { combatPowerOf } from './systems/combat' import type { LogKind } from './world' import { computeLegacy, resolveLegacy, LegacyArch } from '../core/legacy' import { yearAxis, AxisCell } from '../core/yearaxis' import { PACK } from '../data/registry' export type ActName = | 'head.set' | 'member.meditate' | 'member.technique' | 'member.equip' | 'member.pill' | 'member.advance' | 'member.marry' | 'member.post' | 'estate.build' | 'estate.upgrade' | 'estate.rite' | 'estate.sutra' | 'market.buy' | 'market.sell' | 'market.tech' | 'craft.pill' | 'diplomacy.gift' | 'diplomacy.peace' | 'diplomacy.taunt' | 'diplomacy.marry' | 'expedition.send' | 'expedition.recall' | 'legacy.resolve' export interface ActPayload { memberId?: string targetId?: string post?: string item?: string pill?: string tech?: string building?: string npcId?: string stones?: number mission?: string squad?: string[] count?: number } export const ACT_CATALOG: Record boolean }> = { 'head.set': { desc: '册立家主', fn: (w, p) => (p.memberId ? (w.assignHead(p.memberId), true) : false) }, 'member.meditate': { desc: '闭关/出关', fn: (w, p) => (p.memberId ? (w.setMeditation(p.memberId, !!p.count), true) : false) }, 'member.technique': { desc: '传功', fn: (w, p) => (p.memberId && p.tech ? (w.giveTechnique(p.memberId, p.tech), true) : false) }, 'member.equip': { desc: '装备法宝', fn: (w, p) => (p.memberId && p.item ? (w.equip(p.memberId, p.item), true) : false) }, 'member.pill': { desc: '服丹', fn: (w, p) => (p.memberId && p.pill ? (w.takePill(p.memberId, p.pill), true) : false) }, 'member.advance': { desc: '冲关', fn: (w, p) => (p.memberId ? (w.assistedBreakthrough(p.memberId), true) : false) }, 'member.marry': { desc: '指婚', fn: (w, p) => (p.memberId && p.targetId ? w.marryTo(p.memberId, p.targetId) : false) }, 'member.post': { desc: '任职/卸任', fn: (w, p) => (p.memberId ? w.assignPost(p.memberId, p.post) : false) }, 'estate.build': { desc: '营建', fn: (w, p) => (p.building ? w.build(p.building) : false) }, 'estate.upgrade': { desc: '升级', fn: (w, p) => (p.building ? w.upgrade(p.building) : false) }, 'estate.rite': { desc: '祭祖', fn: (w) => w.ancestralRite() }, 'estate.sutra': { desc: '求经', fn: (w) => w.seekSutra() }, 'market.buy': { desc: '购货', fn: (w, p) => (p.item ? buyItem(w, p.item, p.count ?? 1) : false) }, 'market.sell': { desc: '售货', fn: (w, p) => (p.item ? sellItem(w, p.item, p.count ?? 1) : false) }, 'market.tech': { desc: '购法帖', fn: (w, p) => (p.tech ? buyTechnique(w, p.tech, p.stones ?? 0) : false) }, 'craft.pill': { desc: '炼丹', fn: (w, p) => (p.item === 'ningyuan' ? w.craftPill('ningyuan') : Boolean(p.item === 'qiyuan') && w.craftPill('qiyuan')) }, 'diplomacy.gift': { desc: '赠礼', fn: (w, p) => (p.npcId ? giftNpc(w, p.npcId, p.stones ?? 0) : false) }, 'diplomacy.peace': { desc: '议和', fn: (w, p) => (p.npcId ? makePeace(w, p.npcId) : false) }, 'diplomacy.taunt': { desc: '寻衅', fn: (w, p) => (p.npcId ? w.tauntNpc(p.npcId) : false) }, 'diplomacy.marry': { desc: '联姻', fn: (w, p) => (p.npcId ? marryNpcFamily(w, p.npcId) : false) }, 'expedition.send': { desc: '遣队出征', fn: (w, p) => (p.mission ? sendMission(w, p.mission, p.squad ?? []) : false) }, 'expedition.recall': { desc: '召回', fn: (w, p) => (p.memberId ? (recallAll(w, p.memberId), true) : false) }, 'legacy.resolve': { desc: '落印定鼎', fn: (w) => (w.resolveLegacyNow(), true) } } export interface QueryResult { [k: string]: unknown } export class GameFacade { constructor( public readonly world: World, public readonly slot: number, private bus?: WorldEventBus ) {} act(name: ActName, payload: ActPayload = {}): boolean { const c = ACT_CATALOG[name] if (!c) return false return c.fn(this.world, payload) } query(ref: string): QueryResult { const w = this.world switch (ref) { case 'family': return { name: w.state.family.name, estate: w.state.family.estate, year: w.state.year, month: w.state.month, reputation: w.state.family.reputation, generation: w.state.family.generation } case 'members': return { list: w.aliveMembers().map((c) => ({ id: c.id, name: c.name, realm: `${c.realm.major}/${c.realm.minor}`, post: c.post ?? '', state: c.state, power: Math.round(combatPowerOf(w, c)) })) } case 'legacy': return resolveLegacy(w.state) as unknown as QueryResult case 'yearAxis': return { cells: yearAxis(w.state) as unknown as AxisCell[] } case 'systems': return { list: w.systemList() } case 'finance': return { stones: w.state.family.stones, accum: w.state.finance.accum, yearStats: w.state.yearStats } case 'plugins': return { list: w.pluginList() } default: return {} } } subscribe(on: (e: FacadeEvent) => void): () => void { const bus: WorldEventBus = { onLog: (kind: LogKind, text: string) => on({ type: 'log', kind, text, year: this.world.state.year, month: this.world.state.month }), onChronicle: (entry: ChronicleEntry, important: boolean) => on({ type: 'chronicle', entry, important }), onBattle: (log: BattleLog) => on({ type: 'battle', log }), onPendingEvent: (id: string) => on({ type: 'pending', id }), onGameOver: (reason: string) => on({ type: 'gameover', reason }), onYearPaper: (report: YearlyReport) => on({ type: 'paper', report }), onSystemChange: (id: string, enabled: boolean) => on({ type: 'sysChanged', id, enabled }), onPluginChange: (id: string, action: string) => on({ type: 'plugin', id, action }) } this.world.out.push(bus) return () => { const i = this.world.out.indexOf(bus) if (i >= 0) this.world.out.splice(i, 1) } } about(): { title: string; version: string; modules: number; systems: number; plugins: number; packFingerprint: string } { return { title: '仙途家族志', version: '0.1.8', modules: this.world.systemList().length, systems: this.world.systemList().filter((s) => s.enabled).length, plugins: this.world.pluginList().length, packFingerprint: PACK.fingerprint() } } } export type FacadeEvent = | { type: 'log'; kind: LogKind; text: string; year: number; month: number } | { type: 'chronicle'; entry: ChronicleEntry; important: boolean } | { type: 'battle'; log: BattleLog } | { type: 'pending'; id: string } | { type: 'gameover'; reason: string } | { type: 'paper'; report: YearlyReport } | { type: 'sysChanged'; id: string; enabled: boolean } | { type: 'plugin'; id: string; action: string } export { marketPrice, computeLegacy } export type { SaveMeta, LegacyArch }