import { useState } from 'react' import { useGameStore } from '../store' import { applyEventChoice, rankPower } from '../../game/engine/runtime/Systems/events' import { eventCategoryName } from '../../game/data/events' import { describeRealm } from '../../game/data/realms' import { Character } from '../../game/types/domain' import { combatPowerOf } from '../../game/engine/runtime/Systems/combat' import { FORMATIONS, FormationId } from '../../game/data/formations' import { buildingById } from '../../game/data/buildings' import { ModalShell } from './ModalShell' const RES_NAME: Record = { lingcao: '灵草', lingkuang: '灵矿', beastcore: '兽核', stones: '灵石', lingyu: '灵玉', lingmu: '灵木', shoupi: '兽皮', lingguo: '灵果', linglu: '灵露', dansha: '丹砂', fuzhi: '符纸', 'talisman-feng': '风符', 'talisman-shan': '山符', 'brew-niang': '灵酿' } function describeEff(eff: Record | { [k: string]: unknown }): string { const parts: string[] = [] const e = eff as { res?: Record; rep?: number; relation?: Record; addBuilding?: string; pillGain?: Record; mission?: string; addTech?: string; raid?: { npcId: string }; trib?: { mode: string }; feisheng?: { stay: boolean }; techniqueChance?: number; artifactChance?: number; flag?: Record } if (e.res) { for (const [k, v] of Object.entries(e.res)) { const name = RES_NAME[k] ?? k parts.push(v > 0 ? `${name}+${v}` : `${name}${v}`) } } if (e.rep) parts.push(`声望${e.rep > 0 ? '+' : ''}${e.rep}`) if (e.relation) { for (const v of Object.values(e.relation)) parts.push(v > 0 ? `交好+${v}` : `交恶${v}`) } if (e.addBuilding) parts.push('增筑「' + (buildingById(e.addBuilding)?.name ?? e.addBuilding) + '」') if (e.pillGain) { for (const v of Object.values(e.pillGain)) parts.push(`得丹+${v}`) } if (e.addTech) parts.push('得功法') if (e.mission) parts.push('触发任务') if (e.techniqueChance) parts.push(`功法机率+${Math.round(e.techniqueChance * 100)}%`) if (e.artifactChance) parts.push(`宝器机率+${Math.round(e.artifactChance * 100)}%`) if (e.raid) parts.push('来敌犯境') if (e.trib) { parts.push(e.trib.mode === 'rash' ? '硬渡天劫' : e.trib.mode === 'guard' ? '护法渡劫' : '渡劫延一年') } if (e.feisheng) parts.push(e.feisheng.stay ? '留世不飞升' : '飞升离世') return parts.join(' · ') } export function EventModal() { const pendingEventId = useGameStore((s) => s.pendingEventId) const pendingEventDef = useGameStore((s) => s.pendingEventDef) const world = useGameStore((s) => s.world) const bump = useGameStore((s) => s.bump) const setSpeed = useGameStore((s) => s.setSpeed) const [squadPicking, setSquadPicking] = useState(false) const [squadSel, setSquadSel] = useState([]) const [formation, setFormation] = useState('vanguard') if (!pendingEventId || !pendingEventDef || !world) return null const raidIdx = pendingEventDef.options.findIndex((o) => o.eff.raid || o.eff.tournament) const squadPool: Character[] = raidIdx >= 0 ? world.aliveMembers() .filter((c) => world.ageOf(c) >= 16 && c.state !== 'expedition') .sort((a, b) => rankPower(world, b) - rankPower(world, a)) : [] const closeModal = () => { useGameStore.setState({ pendingEventId: undefined, pendingEventDef: undefined }) setSquadPicking(false) setSquadSel([]) bump() } const choose = (idx: number) => { const isRaid = raidIdx >= 0 && raidIdx === idx if (isRaid && !squadPicking) { setSquadPicking(true) setSquadSel(squadPool.slice(0, 4).map((c) => c.id)) return } applyEventChoice(world, pendingEventId, idx, isRaid ? squadSel : undefined, undefined, isRaid ? formation : undefined) closeModal() const sp = useGameStore.getState().speed if (sp > 1) setSpeed(1) } const toggleSquad = (id: string) => { setSquadSel((old) => old.includes(id) ? old.filter((x) => x !== id) : old.length >= 4 ? [...old.slice(1), id] : [...old, id] ) } const dorm = () => { // 「稍后再议」:把事件按回队列,不消灭、不误弃 world.requeueEvent(pendingEventId) closeModal() } return ( ) : ( ) } >
{pendingEventDef.text}
{squadPicking && (
迎战·列阵(默认四人,可改动)
{(Object.keys(FORMATIONS) as FormationId[]).map((fid) => (
setFormation(fid)} title={FORMATIONS[fid].desc} > {FORMATIONS[fid].name}·{FORMATIONS[fid].desc}
))}
{squadPool.map((c) => (
toggleSquad(c.id)} > {c.name}·{describeRealm(c.realm)}·战力{Math.round(combatPowerOf(world, c))}
))} {squadPool.length === 0 && 无可出战之人}
)}
{squadPicking ? (
依选迎战(可继续点选调整阵容)
) : ( pendingEventDef.options.map((o, i) => { const isRaid = raidIdx === i return ( ) }) )}
) }