mirror of
https://github.com/VickScarlet/lifeRestart.git
synced 2026-08-28 17:26:47 +08:00
feature: multiple lock, pick min max
This commit is contained in:
@@ -102,10 +102,10 @@ export interface EndResult {
|
||||
export function end(
|
||||
state: GameState,
|
||||
profile: ProfileState,
|
||||
lockedTalent?: Talent['id'],
|
||||
locked?: Talent['id'][],
|
||||
) {
|
||||
const ar = atr(Ao.End, state, profile)
|
||||
const p = nextProfile(profile, ar.state, lockedTalent)
|
||||
const p = nextProfile(profile, ar.state, locked)
|
||||
return { profile: p, achievements: ar.triggers }
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ export type UniqueCharacter = Omit<Character, 'id' | 'name'>
|
||||
/** 持久化存储的数据 */
|
||||
export interface ProfileState {
|
||||
times: number // 游戏次数
|
||||
lockedTalent?: Talent['id'] // 继承的天赋
|
||||
locked?: Talent['id'][] // 锁定的天赋
|
||||
talents: Set<Talent['id']> // 拥有过的天赋
|
||||
events: Set<Event['id']> // 触发过的事件
|
||||
achievements: Set<Achievement['id']> // 达成的成就
|
||||
@@ -195,7 +195,7 @@ export function lowestProperties(a: Properties, b?: Properties): Properties {
|
||||
export function nextProfile(
|
||||
profile: ProfileState,
|
||||
state: GameState,
|
||||
lockedTalent?: Talent['id'],
|
||||
locked?: Talent['id'][],
|
||||
) {
|
||||
return {
|
||||
times: profile.times + 1,
|
||||
@@ -204,6 +204,6 @@ export function nextProfile(
|
||||
achievements: profile.achievements.union(state.achievements),
|
||||
highest: highestProperties(state.props.highest, profile.highest),
|
||||
lowest: lowestProperties(state.props.lowest, profile.lowest),
|
||||
lockedTalent: lockedTalent ?? profile.lockedTalent,
|
||||
locked,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +70,13 @@ export function pull(
|
||||
)
|
||||
const result = []
|
||||
const map = new Map(Grades.map(g => [g, new Set(GradeMap.get(g))]))
|
||||
if (profile.lockedTalent) {
|
||||
result.push(profile.lockedTalent)
|
||||
const { grade } = talents.get(profile.lockedTalent)!
|
||||
map.get(grade)!.delete(profile.lockedTalent)
|
||||
if (profile.locked) {
|
||||
result.push(...profile.locked)
|
||||
if (result.length >= options.count) return result
|
||||
for (const talent of result) {
|
||||
const { grade } = talents.get(talent)!
|
||||
map.get(grade)!.delete(talent)
|
||||
}
|
||||
}
|
||||
for (let i = options.count - result.length; i > 0; i--) {
|
||||
const grade = pickWeight(rate, rng)! ?? 0
|
||||
|
||||
@@ -3,11 +3,21 @@ import { atom, useSetAtom, useAtomValue } from 'jotai'
|
||||
import type { PullOptions } from '@remake/core'
|
||||
|
||||
export interface Config {
|
||||
pick: number
|
||||
/** 游戏锁定天赋数量 */
|
||||
lock: number
|
||||
/** 游戏可选天赋数量 */
|
||||
max: number
|
||||
/** 游戏最少选择天赋数量 */
|
||||
min: number
|
||||
/** 天赋抽取个数 */
|
||||
pull: PullOptions
|
||||
/** 初始属性点 */
|
||||
points: number
|
||||
/** 单项属性点最大限制 */
|
||||
allocate: number
|
||||
/** 初始快乐 */
|
||||
spirit: number
|
||||
/** 模式选择限制 */
|
||||
mode: number
|
||||
}
|
||||
|
||||
|
||||
+27
-11
@@ -101,7 +101,7 @@ export const useNext = () => {
|
||||
}
|
||||
return result.achievements
|
||||
}, [state, profile, setState])
|
||||
return [{ state, logs, ended }, nexter] as const
|
||||
return [{ logs, ended }, nexter] as const
|
||||
}
|
||||
|
||||
export const useGotoSummary = () => {
|
||||
@@ -120,20 +120,36 @@ export const useGotoSummary = () => {
|
||||
}
|
||||
|
||||
export const useEnd = () => {
|
||||
const { lock } = useConfig()
|
||||
const [profile, setProfile] = useProfile()
|
||||
const [step, setStep] = useAtom(stepAtom)
|
||||
const state = useAtomValue(gameStateAtom)
|
||||
const reset = useGameReset()
|
||||
return useCallback(
|
||||
(talent?: Talent['id']) => {
|
||||
if (!state)
|
||||
throw new Error('Game state is not available or already ended.')
|
||||
const result = end(state, profile, talent)
|
||||
setStep(Step.Idle)
|
||||
setProfile(result.profile)
|
||||
reset()
|
||||
return result.achievements
|
||||
const [locked, setLocked] = useState<Set<Talent['id']>>(new Set())
|
||||
const picker = useCallback(
|
||||
(talent: Talent['id']) => {
|
||||
setLocked(prev => {
|
||||
const next = new Set(prev)
|
||||
if (next.has(talent)) {
|
||||
next.delete(talent)
|
||||
return next
|
||||
}
|
||||
if (next.size >= lock) return prev
|
||||
next.add(talent)
|
||||
return next
|
||||
})
|
||||
},
|
||||
[state, step, profile, setStep, setProfile, reset],
|
||||
[setLocked],
|
||||
)
|
||||
const ender = useCallback(() => {
|
||||
if (!state)
|
||||
throw new Error('Game state is not available or already ended.')
|
||||
const l = locked.size > 0 ? Array.from(locked) : undefined
|
||||
const result = end(state, profile, l)
|
||||
setStep(Step.Idle)
|
||||
setProfile(result.profile)
|
||||
reset()
|
||||
return result.achievements
|
||||
}, [state, step, profile, locked, setStep, setProfile, reset])
|
||||
return [locked, picker, ender] as const
|
||||
}
|
||||
|
||||
@@ -35,12 +35,7 @@ export const useTalentPuller = () => {
|
||||
const [profile] = useProfile()
|
||||
const [pulled, setPulled] = useState<Talent['id'][] | null>(null)
|
||||
const puller = useCallback(
|
||||
// (rng?: RNG) => setPulled(pull(p, profile, rng)),
|
||||
(rng?: RNG) =>
|
||||
setPulled([
|
||||
1142, 1143, 1144, 1145, 1146, 1086, 1122, 1111, 1130, 1048,
|
||||
1033,
|
||||
]),
|
||||
(rng?: RNG) => setPulled(pull(p, profile, rng)),
|
||||
[p, profile, setPulled],
|
||||
)
|
||||
return [pulled, puller] as const
|
||||
@@ -61,7 +56,7 @@ export type TalentPickerResult =
|
||||
}
|
||||
|
||||
export const useTalentPicker = () => {
|
||||
const { pick } = useConfig()
|
||||
const { max } = useConfig()
|
||||
const [picked, setPicked] = useAtom(pickedAtom)
|
||||
const picker = useCallback(
|
||||
(talent: Talent['id']): TalentPickerResult => {
|
||||
@@ -75,29 +70,29 @@ export const useTalentPicker = () => {
|
||||
setPicked(next)
|
||||
return { type: 'ok' }
|
||||
}
|
||||
if (picked.size >= pick) return { type: 'not-enough' }
|
||||
if (picked.size >= max) return { type: 'not-enough' }
|
||||
const e = exclude(talent, picked)
|
||||
if (e) return { type: 'exclude', talent: e }
|
||||
const next = new Set([...picked, talent])
|
||||
setPicked(next)
|
||||
return { type: 'ok' }
|
||||
},
|
||||
[pick, picked, setPicked],
|
||||
[max, picked, setPicked],
|
||||
)
|
||||
return [picked, picker] as const
|
||||
}
|
||||
|
||||
export const useSubmitIsEnable = () => {
|
||||
const { pick } = useConfig()
|
||||
const { max, min } = useConfig()
|
||||
const picked = useAtomValue(pickedAtom)
|
||||
const enabled = picked.size >= pick
|
||||
return [enabled, pick] as const
|
||||
const enabled = picked.size >= min && picked.size <= max
|
||||
return { min, max, enabled } as const
|
||||
}
|
||||
|
||||
export const useTalentSubmit = () => {
|
||||
const picked = useAtomValue(pickedAtom)
|
||||
const setReplaced = useSetAtom(replacedAtom)
|
||||
const [enabled] = useSubmitIsEnable()
|
||||
const { enabled } = useSubmitIsEnable()
|
||||
const next = useSetStep()
|
||||
return useCallback(
|
||||
(rng?: RNG) => {
|
||||
|
||||
Reference in New Issue
Block a user