update: character

This commit is contained in:
Vick Scarlet
2026-08-16 20:06:16 +08:00
committed by 神戸小鳥
parent ad4e83fc47
commit f4204601ee
22 changed files with 495 additions and 101 deletions
+2 -2
View File
@@ -47,7 +47,7 @@ export const useAllocator = () => {
return { ...prev, [key]: final }
})
},
[allocate, total],
[allocate, total, setAlloc],
)
return [alloc, allocator] as const
}
@@ -71,7 +71,7 @@ export const usePointRandomizer = () => {
}
setAlloc(alloc)
},
[allocate, total],
[allocate, total, setAlloc],
)
}
+104
View File
@@ -0,0 +1,104 @@
import { useCallback, useState } from 'react'
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
import { useConfig, useProfile, useSetStep, useSetGameState, Step } from '.'
import { uniqueGenerate, startUnique, startChara } from '@remake/core'
import { pullChara, start, pick } from '@remake/core'
import type { BaseChara, PullCharaTms } from '@remake/core'
import type { Character } from '@remake/data'
import type { RNG } from '@remake/vitex'
export const uniqueAtom = atom<BaseChara | null>(null)
export const timesAtom = atom<PullCharaTms | undefined>(undefined)
export const useUnique = () => useAtomValue(uniqueAtom)
export const useUniqueInject = () => {
const setUnique = useSetAtom(uniqueAtom)
return useCallback((unique: BaseChara) => setUnique(unique), [setUnique])
}
export const useUniqueGenerator = () => {
const { config } = useConfig().unique
const [unique, setUnique] = useAtom(uniqueAtom)
const generator = useCallback(
(rng?: RNG) => {
if (unique) return unique
const generated = uniqueGenerate(config, rng)
setUnique(generated)
return generated
},
[config, unique, setUnique],
)
return [unique, generator] as const
}
export interface PullCharaResult {
characters: Character['id'][]
unique: boolean
}
export const useCharaPuller = (rng?: RNG) => {
const { unique: u, chara } = useConfig()
const [unique] = useAtom(uniqueAtom)
const [times, setTimes] = useAtom(timesAtom)
const [pulled, setPulled] = useState<PullCharaResult>({
characters: pullChara(chara, times, rng).characters,
unique: !!unique,
})
const puller = useCallback(() => {
const result = pullChara(chara, times, rng)
setTimes(result.times)
setPulled({
characters: result.characters,
unique: !!unique || result.times?.times >= u.limit * chara.count,
})
}, [u, chara, unique, times, setTimes, rng])
return [pulled, puller] as const
}
export type PickUnique = { type: 'unique'; id?: never }
export type PickCharacter = {
type: 'character'
id: Character['id']
}
export type CharaPick = PickUnique | PickCharacter
export const useCharaPicker = () => {
const [picked, setPicked] = useState<CharaPick | null>(null)
const chara = useCallback(
(id: Character['id']) => setPicked({ type: 'character', id }),
[setPicked],
)
const uni = useCallback(() => setPicked({ type: 'unique' }), [setPicked])
return [picked, { chara, unique: uni }] as const
}
export const useCharaStart = () => {
const { spirit } = useConfig()
const [profile] = useProfile()
const unique = useAtomValue(uniqueAtom)
const setStep = useSetStep()
const setState = useSetGameState()
return useCallback(
(picked: CharaPick, rng?: RNG) => {
let result
if (picked.type === 'unique') {
if (!unique)
throw new Error('Unique character not generated yet')
result = startUnique(unique, spirit)
} else {
result = startChara(picked.id, spirit)
}
const { talents, additionalPoints } = pick(result.talents, rng)
// TODO: additionalPoints
const { state, achievements } = start(
profile,
result.allocation,
talents.talents,
)
setState(state)
setStep(Step.Play)
return achievements
},
[spirit, profile, unique, setStep, setState],
)
}
export { charaPropToBaseAlloc as convertProps } from '@remake/core'
+6 -1
View File
@@ -1,6 +1,6 @@
import { useCallback } from 'react'
import { atom, useSetAtom, useAtomValue } from 'jotai'
import type { PullOptions, PullCharaOpt } from '@remake/core'
import type { PullOptions, PullCharaOpt, UniqueGenCfg } from '@remake/core'
export interface Config {
/** 游戏锁定天赋数量 */
lock: number
@@ -20,6 +20,11 @@ export interface Config {
pull: PullOptions
/** 名人模式配置 */
chara: PullCharaOpt
/** 唯一角色限制 */
unique: {
limit: number
config: UniqueGenCfg
}
}
export const configAtom = atom<Config | null>(null)
+1
View File
@@ -3,4 +3,5 @@ export * from './profile'
export * from './talent'
export * from './play'
export * from './alloc'
export * from './character'
export type * from '@remake/core'
+13 -12
View File
@@ -25,6 +25,7 @@ export type Log = Omit<NextResult, 'state'> & {
props: Omit<Properties, 'age'>
}
export const modeAtom = atom<Mode>(Mode.Classic)
export const stepAtom = atom<Step>(Step.Idle)
export const gameStateAtom = atom<GameState | null>(null)
export const logsAtom = atom<Log[]>([])
@@ -51,9 +52,10 @@ export const useGameReset = () => {
}, [resetTalent, resetAlloc, resetState, resetLogs])
}
export const useStep = () => [useAtomValue(stepAtom), Step] as const
export const useSetStep = () => [useSetAtom(stepAtom), Step] as const
export const useStep = () => useAtomValue(stepAtom)
export const useSetStep = () => useSetAtom(stepAtom)
export const useGameState = () => useAtomValue(gameStateAtom)
export const useSetGameState = () => useSetAtom(gameStateAtom)
export const useSummary = () => useAtomValue(summaryAtom)
export const useLogs = () => useAtomValue(logsAtom)
@@ -69,16 +71,15 @@ export const useRemake = () => {
}
export const useModeChoose = () => {
const setMode = useSetAtom(modeAtom)
const setStep = useSetAtom(stepAtom)
const choose = useCallback(
(mode: Mode) => {
setMode(mode)
/* prettier-ignore */
switch (mode) {
case Mode.Classic:
setStep(Step.Pick)
break
case Mode.Celebrity:
setStep(Step.Chara)
break
case Mode.Classic: return setStep(Step.Pick)
case Mode.Celebrity: return setStep(Step.Chara)
}
},
[setStep],
@@ -122,7 +123,7 @@ export const useNext = () => {
setEnded(true)
}
return result.achievements
}, [state, profile, setState])
}, [state, profile, setState, setLogs])
return [{ logs, ended }, nexter] as const
}
@@ -144,7 +145,7 @@ export const useGotoSummary = () => {
export const useEnd = () => {
const { lock } = useConfig()
const [profile, setProfile] = useProfile()
const [step, setStep] = useAtom(stepAtom)
const setStep = useSetAtom(stepAtom)
const state = useAtomValue(gameStateAtom)
const reset = useGameReset()
const [locked, setLocked] = useState<Set<Talent['id']>>(new Set())
@@ -163,7 +164,7 @@ export const useEnd = () => {
return next
})
},
[setLocked],
[lock, setLocked],
)
const ender = useCallback(() => {
if (!state)
@@ -174,6 +175,6 @@ export const useEnd = () => {
setProfile(result.profile)
reset()
return result.achievements
}, [state, step, profile, locked, setStep, setProfile, reset])
}, [state, profile, locked, setStep, setProfile, reset])
return [locked, picker, ender] as const
}
+2 -2
View File
@@ -1,6 +1,6 @@
import { useState, useCallback } from 'react'
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
import { useConfig, useProfile, useSetStep } from '.'
import { useConfig, useProfile, useSetStep, Step } from '.'
import { pull, exclude, pick, type PickResult } from '@remake/core'
import type { Talent } from '@remake/data'
import type { RNG } from '@remake/vitex'
@@ -82,7 +82,7 @@ export const useTalentSubmit = () => {
const picked = useAtomValue(pickedAtom)
const setReplaced = useSetAtom(replacedAtom)
const { enabled } = useSubmitIsEnable()
const [setStep, Step] = useSetStep()
const setStep = useSetStep()
return useCallback(
(rng?: RNG) => {
if (!enabled) throw new Error('Not enough talents picked')