From 32f703be0f6700168c633380013953b97612bd85 Mon Sep 17 00:00:00 2001 From: Vick Scarlet Date: Sun, 2 Aug 2026 17:36:04 +0800 Subject: [PATCH] update: talent, state props --- docs/remake.md | 17 +++ packages/core/src/event.spec.ts | 6 +- packages/core/src/event.ts | 48 +++---- packages/core/src/game.ts | 9 +- packages/core/src/index.ts | 1 + packages/core/src/state.spec.ts | 148 ++++++++++++-------- packages/core/src/state.ts | 153 ++++++++++----------- packages/core/src/talent.spec.ts | 98 ++++++++++++++ packages/core/src/talent.ts | 218 ++++++++++++++++++++++++++++-- packages/data/src/talent.types.ts | 26 ++-- packages/data/src/talent.xlsx | Bin 36730 -> 37221 bytes packages/vitex/index.ts | 2 +- 12 files changed, 534 insertions(+), 192 deletions(-) create mode 100644 docs/remake.md create mode 100644 packages/core/src/talent.spec.ts diff --git a/docs/remake.md b/docs/remake.md new file mode 100644 index 0000000..4d9b2a5 --- /dev/null +++ b/docs/remake.md @@ -0,0 +1,17 @@ +## 流程 + +重开 + 初始化 + 抽天赋(包含继承的天赋) + 天赋选择(检测天赋冲突) + 天赋替换(检测天赋冲突) + 点数分配(天赋额外点数) + 游戏循环 + 年龄增加一岁 + 天赋触发 + 事件触发(从对应年龄事件池随机事件) + 判断是否结束(生命值小于 1 结束) + 总评 + 天赋继承 + 存档数据 + 重回首页 \ No newline at end of file diff --git a/packages/core/src/event.spec.ts b/packages/core/src/event.spec.ts index b307cc4..e3e4008 100644 --- a/packages/core/src/event.spec.ts +++ b/packages/core/src/event.spec.ts @@ -1,6 +1,8 @@ import { expect, test, describe } from 'bun:test' import { createState } from './state' import { trigger } from './event' +import { enableMapSet } from 'immer' +enableMapSet() describe('Event', () => { const profile = { @@ -16,7 +18,7 @@ describe('Event', () => { money: 0, } - test('10003 branch 0 玉佩', () => { + test('branch 10003:0 [玉佩]', () => { const result = trigger( 10003, createState(allocation, [1001, 1002, 1003]), @@ -27,7 +29,7 @@ describe('Event', () => { expect(result.events).toEqual([10003, 10004]) }) - test('10003 branch 1 死了', () => { + test('branch 10003:1 [死了]', () => { const result = trigger(10003, createState(allocation, []), profile) expect(result.state.life).toBe(0) expect(result.state.events).toEqual(new Set([10003, 10000])) diff --git a/packages/core/src/event.ts b/packages/core/src/event.ts index 4d928ed..69bae86 100644 --- a/packages/core/src/event.ts +++ b/packages/core/src/event.ts @@ -1,23 +1,10 @@ -import type { EventEffect, Event } from '@remake/data/event' +import type { Event } from '@remake/data/event' import events from '@remake/data/event' -import type { Properties, Effect } from '@/state' +import type { Properties } from '@/state' import type { GameState, ProfileState } from '@/state' -import { stateEffect, createFlatState } from '@/state' +import { propsEffect, createFlatState } from '@/state' import { check } from '@remake/condition' - -export function convertEffect(effect: EventEffect): Effect { - const converted = {} as Effect - const properties = [] as [keyof Properties, number][] - if (effect.CHR) properties.push(['charm', effect.CHR]) - if (effect.INT) properties.push(['intelligence', effect.INT]) - if (effect.STR) properties.push(['strength', effect.STR]) - if (effect.MNY) properties.push(['money', effect.MNY]) - if (effect.SPR) properties.push(['spirit', effect.SPR]) - if (properties.length > 0) - converted.properties = Object.fromEntries(properties) - if (effect.LIF) converted.life = effect.LIF - return converted -} +import { produce } from 'immer' export interface TriggerResult { state: GameState @@ -29,17 +16,24 @@ export function trigger( state: GameState, profile: ProfileState, ): TriggerResult { - const event = events.get(eventId) - if (!event) - throw new Error(`[@remake/core][event] id:${eventId} not found.`) - const effect = event.effect ? convertEffect(event.effect) : {} - effect.events = [eventId] - const newState = stateEffect(state, effect) + const { effect, branch } = events.get(eventId)! + const newState = produce(state, draft => { + draft.events.add(eventId) + if (!effect) return + if (effect.LIF) draft.life += effect.LIF + const pe = {} as Partial + if (effect.CHR) pe.charm = effect.CHR + if (effect.INT) pe.intelligence = effect.INT + if (effect.STR) pe.strength = effect.STR + if (effect.MNY) pe.money = effect.MNY + if (effect.SPR) pe.spirit = effect.SPR + draft.props = propsEffect(draft.props, pe) + }) const flatState = createFlatState(newState, profile) - if (event.branch) { - for (const branch of event.branch) { - if (check(flatState, branch.condition)) { - const result = trigger(branch.event, newState, profile) + if (branch) { + for (const { condition, event } of branch) { + if (check(flatState, condition)) { + const result = trigger(event, newState, profile) return { state: result.state, events: [eventId, ...result.events], diff --git a/packages/core/src/game.ts b/packages/core/src/game.ts index 56d8e92..17f48e8 100644 --- a/packages/core/src/game.ts +++ b/packages/core/src/game.ts @@ -1,4 +1,5 @@ -export interface GameEffects { - save: (profile: any) => Promise - load: () => Promise -} +import type { GameState, ProfileState } from '@/state' +import { enableMapSet } from 'immer' +enableMapSet() + +export type { GameState, ProfileState } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e69de29..2fbd5c4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -0,0 +1 @@ +export * from '@/game' diff --git a/packages/core/src/state.spec.ts b/packages/core/src/state.spec.ts index a459567..b26bb79 100644 --- a/packages/core/src/state.spec.ts +++ b/packages/core/src/state.spec.ts @@ -5,13 +5,11 @@ import achievements from '@remake/data/achievement' import events from '@remake/data/event' import talents from '@remake/data/talent' -import { createFlatState } from './state' - -const A = ['AGE', 'CHR', 'INT', 'STR', 'MNY', 'SPR', 'LIF', 'TLT', 'EVT', 'TMS'] -const B = ['HAGE', 'HCHR', 'HINT', 'HSTR', 'HMNY', 'HSPR'] -const C = ['LAGE', 'LCHR', 'LINT', 'LSTR', 'LMNY', 'LSPR'] -const D = ['AEVT', 'ATLT', 'SUM'] -const FlatProperties = new Set([...A, ...B, ...C, ...D]) +import type { GameState, ProfileState, Properties } from './state' +import { createFlatState, createHLProperties, propsEffect } from './state' +import { SupportedFlatStateKeys } from './state' +import { enableMapSet } from 'immer' +enableMapSet() /** * 核心递归辅助函数:从多维语法树节点中提取所有属性名 @@ -42,8 +40,8 @@ function getPropertiesFromCondition(condition: string): Set { return properties } -describe('FlatState', () => { - test('[Cover] event(include, exclude, branch)', () => { +describe('State', () => { + test('flat cover event(include, exclude, branch)', () => { const unmappedKeys = new Set() for (const item of events.values()) { const conditionsToTrack: { label: string; text: string }[] = [] @@ -68,7 +66,7 @@ describe('FlatState', () => { for (const { label, text } of conditionsToTrack) { const usedProperties = getPropertiesFromCondition(text) for (const key of usedProperties) { - if (!FlatProperties.has(key)) { + if (!SupportedFlatStateKeys.has(key)) { unmappedKeys.add( `[事件 ID: ${item.id}] 的 "${label}" 字段使用了未定义的键名: "${key}" (完整条件: "${text}")`, ) @@ -85,7 +83,7 @@ describe('FlatState', () => { expect(unmappedKeys.size).toBe(0) }) - test('[Cover] talent(condition)', () => { + test('flat cover talent(condition)', () => { const unmappedKeys = new Set() const allTalentItems = talents.values() @@ -95,7 +93,7 @@ describe('FlatState', () => { const usedProperties = getPropertiesFromCondition(conditionStr) for (const key of usedProperties) { - if (!FlatProperties.has(key)) { + if (!SupportedFlatStateKeys.has(key)) { unmappedKeys.add( `[天赋 ID: ${item.id}] 使用了未定义的键名: "${key}" (完整条件: "${conditionStr}")`, ) @@ -111,7 +109,7 @@ describe('FlatState', () => { expect(unmappedKeys.size).toBe(0) }) - test('[Cover] achievement(condition)', () => { + test('flat cover achievement(condition)', () => { const unmappedKeys = new Set() const allAchievementItems = achievements.values() @@ -121,7 +119,7 @@ describe('FlatState', () => { const usedProperties = getPropertiesFromCondition(conditionStr) for (const key of usedProperties) { - if (!FlatProperties.has(key)) { + if (!SupportedFlatStateKeys.has(key)) { unmappedKeys.add( `[成就 ID: ${item.id}] 使用了未定义的键名: "${key}" (完整条件: "${conditionStr}")`, ) @@ -137,37 +135,39 @@ describe('FlatState', () => { expect(unmappedKeys.size).toBe(0) }) - test('get', () => { + test('flat get', () => { const game = { - lowest: { - age: 1, - charm: 2, - intelligence: 3, - strength: 4, - money: 5, - spirit: 6, - }, - properties: { - age: 11, - charm: 12, - intelligence: 13, - strength: 14, - money: 15, - spirit: 16, - }, - highest: { - age: 21, - charm: 22, - intelligence: 23, - strength: 24, - money: 25, - spirit: 26, + props: { + lowest: { + age: 1, + charm: 2, + intelligence: 3, + strength: 4, + money: 5, + spirit: 6, + }, + current: { + age: 11, + charm: 12, + intelligence: 13, + strength: 14, + money: 15, + spirit: 16, + }, + highest: { + age: 21, + charm: 22, + intelligence: 23, + strength: 24, + money: 25, + spirit: 26, + }, }, life: 31, talents: new Set([33]), events: new Set([34, 35]), achievements: new Set([36]), - } + } as GameState const profile = { times: 41, @@ -175,30 +175,30 @@ describe('FlatState', () => { talents: new Set([43]), achievements: new Set([44]), events: new Set([45, 46]), - } + } as ProfileState const flatState = createFlatState(game, profile) - expect(flatState['AGE']).toBe(game.properties.age) - expect(flatState['CHR']).toBe(game.properties.charm) - expect(flatState['INT']).toBe(game.properties.intelligence) - expect(flatState['STR']).toBe(game.properties.strength) - expect(flatState['MNY']).toBe(game.properties.money) - expect(flatState['SPR']).toBe(game.properties.spirit) + expect(flatState['AGE']).toBe(game.props.current.age) + expect(flatState['CHR']).toBe(game.props.current.charm) + expect(flatState['INT']).toBe(game.props.current.intelligence) + expect(flatState['STR']).toBe(game.props.current.strength) + expect(flatState['MNY']).toBe(game.props.current.money) + expect(flatState['SPR']).toBe(game.props.current.spirit) + expect(flatState['HAGE']).toBe(game.props.highest.age) + expect(flatState['HCHR']).toBe(game.props.highest.charm) + expect(flatState['HINT']).toBe(game.props.highest.intelligence) + expect(flatState['HSTR']).toBe(game.props.highest.strength) + expect(flatState['HMNY']).toBe(game.props.highest.money) + expect(flatState['HSPR']).toBe(game.props.highest.spirit) + expect(flatState['LAGE']).toBe(game.props.lowest.age) + expect(flatState['LCHR']).toBe(game.props.lowest.charm) + expect(flatState['LINT']).toBe(game.props.lowest.intelligence) + expect(flatState['LSTR']).toBe(game.props.lowest.strength) + expect(flatState['LMNY']).toBe(game.props.lowest.money) + expect(flatState['LSPR']).toBe(game.props.lowest.spirit) expect(flatState['LIF']).toBe(game.life) expect(flatState['TLT']).toEqual(game.talents) expect(flatState['EVT']).toEqual(game.events) - expect(flatState['LAGE']).toBe(game.lowest.age) - expect(flatState['HAGE']).toBe(game.highest.age) - expect(flatState['LCHR']).toBe(game.lowest.charm) - expect(flatState['HCHR']).toBe(game.highest.charm) - expect(flatState['LINT']).toBe(game.lowest.intelligence) - expect(flatState['HINT']).toBe(game.highest.intelligence) - expect(flatState['LSTR']).toBe(game.lowest.strength) - expect(flatState['HSTR']).toBe(game.highest.strength) - expect(flatState['LMNY']).toBe(game.lowest.money) - expect(flatState['HMNY']).toBe(game.highest.money) - expect(flatState['LSPR']).toBe(game.lowest.spirit) - expect(flatState['HSPR']).toBe(game.highest.spirit) expect(flatState['TMS']).toBe(profile.times) expect(flatState['AEVT']).toEqual(profile.events) expect(flatState['ATLT']).toEqual(profile.talents) @@ -207,4 +207,36 @@ describe('FlatState', () => { Math.floor((22 + 23 + 24 + 25 + 26) * 2 + 21 / 2), ) }) + + const allocation = { charm: 5, intelligence: 5, strength: 5, money: 5 } + test('createHLProperties', () => { + const props = createHLProperties(allocation) + expect(props.current.charm).toBe(allocation.charm) + expect(props.current.intelligence).toBe(allocation.intelligence) + expect(props.current.strength).toBe(allocation.strength) + expect(props.current.money).toBe(allocation.money) + expect(props.current.age).toBe(-1) + expect(props.current.spirit).toBe(0) + }) + + test('propsEffect', () => { + const props = createHLProperties(allocation) + const effect = { + age: 6, + charm: -2, + intelligence: 3, + strength: 4, + money: -5, + spirit: 7, + } + const effected = propsEffect(props, effect) + for (const key in effect) { + const k = key as keyof Properties + const c = props.current[k] + const be = c + effect[k] + expect(c).toBe(be) + expect(effected.highest[k]).toBe(Math.max(c, be)) + expect(effected.lowest[k]).toBe(Math.min(c, be)) + } + }) }) diff --git a/packages/core/src/state.ts b/packages/core/src/state.ts index bf8359c..4508ddb 100644 --- a/packages/core/src/state.ts +++ b/packages/core/src/state.ts @@ -1,11 +1,9 @@ import type { Talent } from '@remake/data/talent' import type { Event } from '@remake/data/event' import type { Achievement } from '@remake/data/achievement' -import { produce, enableMapSet } from 'immer' +import { produce } from 'immer' import { sum } from '@remake/vitex' -enableMapSet() - /** 基础的属性 */ export interface Properties { age: number @@ -16,22 +14,35 @@ export interface Properties { spirit: number } +export interface HLProperties { + current: Properties // 当前属性 + highest: Properties // 历史最高属性 + lowest: Properties // 历史最低属性 +} + export type Allocation = Omit +export function createProperties(allocation: Allocation) { + return { ...allocation, age: -1, spirit: 0 } +} + +export function createHLProperties(allocation: Allocation) { + const current = createProperties(allocation) + return { current, highest: { ...current }, lowest: { ...current } } +} export interface GameState { - properties: Properties // 本局属性 - highest: Properties // 本局最高属性 - lowest: Properties // 本局最低属性 + props: HLProperties // 本局属性 life: number // 本局生命值 talents: Set // 本局拥有的天赋 events: Set // 本局触发过的事件 achievements: Set // 本局达成的成就 + talentTriggers: Map // 本局天赋触发次数 } /** 持久化存储的数据 */ export interface ProfileState { times: number // 游戏次数 - external?: Talent['id'] // 继承的天赋 + lockedTalent?: Talent['id'] // 继承的天赋 talents: Set // 拥有过的天赋 events: Set // 触发过的事件 achievements: Set // 达成的成就 @@ -43,39 +54,37 @@ export function createState( allocation: Allocation, talents?: Iterable, ): GameState { - const properties = { ...allocation, age: -1, spirit: 0 } return { - properties, - highest: { ...properties }, - lowest: { ...properties }, + props: createHLProperties(allocation), life: 1, talents: new Set(talents), events: new Set(), achievements: new Set(), + talentTriggers: new Map(), } } export interface FlatState { - AGE: GameState['properties']['age'] - CHR: GameState['properties']['charm'] - INT: GameState['properties']['intelligence'] - STR: GameState['properties']['strength'] - MNY: GameState['properties']['money'] - SPR: GameState['properties']['spirit'] + AGE: GameState['props']['current']['age'] + CHR: GameState['props']['current']['charm'] + INT: GameState['props']['current']['intelligence'] + STR: GameState['props']['current']['strength'] + MNY: GameState['props']['current']['money'] + SPR: GameState['props']['current']['spirit'] + HAGE: GameState['props']['highest']['age'] + HCHR: GameState['props']['highest']['charm'] + HINT: GameState['props']['highest']['intelligence'] + HSTR: GameState['props']['highest']['strength'] + HMNY: GameState['props']['highest']['money'] + HSPR: GameState['props']['highest']['spirit'] + LAGE: GameState['props']['lowest']['age'] + LCHR: GameState['props']['lowest']['charm'] + LINT: GameState['props']['lowest']['intelligence'] + LSTR: GameState['props']['lowest']['strength'] + LMNY: GameState['props']['lowest']['money'] + LSPR: GameState['props']['lowest']['spirit'] LIF: GameState['life'] TLT: GameState['talents'] EVT: GameState['events'] - LAGE: GameState['lowest']['age'] - HAGE: GameState['highest']['age'] - LCHR: GameState['lowest']['charm'] - HCHR: GameState['highest']['charm'] - LINT: GameState['lowest']['intelligence'] - HINT: GameState['highest']['intelligence'] - LSTR: GameState['lowest']['strength'] - HSTR: GameState['highest']['strength'] - LMNY: GameState['lowest']['money'] - HMNY: GameState['highest']['money'] - LSPR: GameState['lowest']['spirit'] - HSPR: GameState['highest']['spirit'] TMS: ProfileState['times'] AEVT: ProfileState['events'] @@ -97,38 +106,40 @@ type FlatMapper = ( ) => FlatState[Key] const FlatMappers = { - AGE: state => state.game.properties.age, - CHR: state => state.game.properties.charm, - INT: state => state.game.properties.intelligence, - STR: state => state.game.properties.strength, - MNY: state => state.game.properties.money, - SPR: state => state.game.properties.spirit, + AGE: state => state.game.props.current.age, + CHR: state => state.game.props.current.charm, + INT: state => state.game.props.current.intelligence, + STR: state => state.game.props.current.strength, + MNY: state => state.game.props.current.money, + SPR: state => state.game.props.current.spirit, + HAGE: state => state.game.props.highest.age, + HCHR: state => state.game.props.highest.charm, + HINT: state => state.game.props.highest.intelligence, + HSTR: state => state.game.props.highest.strength, + HMNY: state => state.game.props.highest.money, + HSPR: state => state.game.props.highest.spirit, + LAGE: state => state.game.props.lowest.age, + LCHR: state => state.game.props.lowest.charm, + LINT: state => state.game.props.lowest.intelligence, + LSTR: state => state.game.props.lowest.strength, + LMNY: state => state.game.props.lowest.money, + LSPR: state => state.game.props.lowest.spirit, LIF: state => state.game.life, TLT: state => state.game.talents, EVT: state => state.game.events, - LAGE: state => state.game.lowest.age, - HAGE: state => state.game.highest.age, - LCHR: state => state.game.lowest.charm, - HCHR: state => state.game.highest.charm, - LINT: state => state.game.lowest.intelligence, - HINT: state => state.game.highest.intelligence, - LSTR: state => state.game.lowest.strength, - HSTR: state => state.game.highest.strength, - LMNY: state => state.game.lowest.money, - HMNY: state => state.game.highest.money, - LSPR: state => state.game.lowest.spirit, - HSPR: state => state.game.highest.spirit, TMS: state => state.profile.times, AEVT: state => state.profile.events, ATLT: state => state.profile.talents, AACH: state => state.profile.achievements, - SUM: state => { - const { age, ...others } = state.game.highest + SUM: ({ game: { props } }) => { + const { age, ...others } = props.highest const s = sum(Object.values(others)) return Math.floor(s * 2 + age / 2) }, } as { [Key in FlatStateKey]: FlatMapper } +export const SupportedFlatStateKeys = new Set(Object.keys(FlatMappers)) + const flatStateHandle = { get(target: FlatTarget, prop: Key) { return FlatMappers[prop]?.(target) @@ -140,43 +151,25 @@ export function createFlatState(game: GameState, profile: ProfileState) { return new Proxy({ game, profile }, flatStateHandle) as unknown as FlatState } -export interface Effect { - properties?: Partial - life?: number - talents?: Iterable - events?: Iterable - achievements?: Iterable -} - -export function stateEffect(state: GameState, effect: Effect) { - return produce(state, draft => { - for (const key in effect.properties) { +export function propsEffect(hlp: HLProperties, effect: Partial) { + return produce(hlp, draft => { + for (const key in effect) { const prop = key as keyof Properties - const value = effect.properties[prop]! - draft.properties[prop] += value + const value = effect[prop]! + draft.current[prop] += value draft.highest[prop] = Math.max( draft.highest[prop], - draft.properties[prop], + draft.current[prop], ) draft.lowest[prop] = Math.min( draft.lowest[prop], - draft.properties[prop], + draft.current[prop], ) } - if (effect.life) draft.life += effect.life - if (effect.talents) - draft.talents = new Set([...draft.talents, ...effect.talents]) - if (effect.events) - draft.events = new Set([...draft.events, ...effect.events]) - if (effect.achievements) - draft.achievements = new Set([ - ...draft.achievements, - ...effect.achievements, - ]) }) } -function highestProperties(a: Properties, b?: Properties): Properties { +export function highestProperties(a: Properties, b?: Properties): Properties { if (!b) return { ...a } const result = {} as Properties for (const key of Object.keys(a) as (keyof Properties)[]) { @@ -185,7 +178,7 @@ function highestProperties(a: Properties, b?: Properties): Properties { return result } -function lowestProperties(a: Properties, b?: Properties): Properties { +export function lowestProperties(a: Properties, b?: Properties): Properties { if (!b) return { ...a } const result = {} as Properties for (const key of Object.keys(a) as (keyof Properties)[]) { @@ -197,15 +190,15 @@ function lowestProperties(a: Properties, b?: Properties): Properties { export function nextProfile( profile: ProfileState, state: GameState, - external?: Talent['id'], + lockedTalent?: Talent['id'], ) { return { times: profile.times + 1, talents: profile.talents.union(state.talents), events: profile.events.union(state.events), achievements: profile.achievements.union(state.achievements), - highest: highestProperties(state.highest, profile.highest), - lowest: lowestProperties(state.lowest, profile.lowest), - external: external ?? profile.external, + highest: highestProperties(state.props.highest, profile.highest), + lowest: lowestProperties(state.props.lowest, profile.lowest), + lockedTalent: lockedTalent ?? profile.lockedTalent, } } diff --git a/packages/core/src/talent.spec.ts b/packages/core/src/talent.spec.ts new file mode 100644 index 0000000..1609a74 --- /dev/null +++ b/packages/core/src/talent.spec.ts @@ -0,0 +1,98 @@ +import { expect, test, describe } from 'bun:test' +import type { ProfileState } from './state' +import type { TalentPullOptions } from './talent' +import { pull, exclude, additionalPoints, replacement } from './talent' +import { createState } from './state' +import talents from '@remake/data/talent' +import { produce } from 'immer' +import { enableMapSet } from 'immer' +enableMapSet() + +describe('Talent', () => { + const profile = { + times: 0, + talents: new Set([]), + achievements: new Set([]), + events: new Set([]), + } as ProfileState + + const options = { + count: 10, + rate: { + base: new Map([ + [0, 1000 - 111], + [1, 100], + [2, 10], + [3, 1], + ]), + additions: {}, + }, + } as TalentPullOptions + + test('pull external', () => { + const p = produce(profile, draft => { + draft.lockedTalent = 1001 + }) + const result = pull(options, p) + expect(result.length).toBe(options.count) + expect(result).toContain(p.lockedTalent!) + }) + + test('pull rate', () => { + const opts = produce(options, draft => { + draft.rate.additions = { + times: () => + new Map([ + [0, { mode: 'multiply', value: 0 }], + [1, { mode: 'multiply', value: 0 }], + [2, { mode: 'multiply', value: 0 }], + [3, { mode: 'multiply', value: 1 }], + ]), + } + }) + const result = pull(opts, profile) + expect(result.some(id => talents.get(id)?.grade === 3)).toBe(true) + }) + + test('exclude', () => { + expect(exclude(1003, [1004])).toBe(1004) + expect(exclude(1004, [1003])).toBe(1003) + expect(exclude(1001, [1002, 1003])).toBe(null) + }) + + test('additionalPoints', () => { + const result = additionalPoints([1001, 1002, 1003, 1007, 1019]) + expect(result).toEqual({ + source: [ + { talent: 1007, points: 2 }, + { talent: 1019, points: 4 }, + ], + points: 6, + }) + }) + + const allo = { charm: 5, intelligence: 5, strength: 5, money: 5 } + test('replacement talent [阴间福袋]', () => { + const talent = 1145 + const state = createState(allo, [talent]) + const result = replacement(state) + expect(result.changed).toBe(true) + expect(result.state.talents.size).toBeGreaterThan(1) + expect(result.chains.get(talent)).toBeDefined() + const source = talents.get(talent)!.replacement!.talent! + const replaced = result.chains.get(talent)!.at(0)! + expect(source.map(t => t[0])).toContain(replaced) + }) + + test('replacement grade [橙色转盘]', () => { + const talent = 1144 + const state = createState(allo, [talent]) + const result = replacement(state) + expect(result.changed).toBe(true) + expect(result.state.talents.size).toBeGreaterThan(1) + expect(result.chains.get(talent)).toBeDefined() + const source = talents.get(talent)!.replacement!.grade! + const replaced = result.chains.get(talent)!.at(0)! + expect(talents.get(replaced)?.grade).toBe(source) + }) +}) diff --git a/packages/core/src/talent.ts b/packages/core/src/talent.ts index fd95c04..f7c35dd 100644 --- a/packages/core/src/talent.ts +++ b/packages/core/src/talent.ts @@ -1,16 +1,17 @@ -import type { TalentEffect } from '@remake/data/talent' +import type { Talent, TalentGrade } from '@remake/data/talent' import talents from '@remake/data/talent' -import type { Properties } from './state' -import { pick } from '@remake/vitex' +import type { Properties } from '@/state' +import type { GameState, ProfileState } from '@/state' +import { propsEffect, createFlatState } from './state' +import { check } from '@remake/condition' +import { pick, pickWeight } from '@remake/vitex' +import { produce } from 'immer' -const TalentEffectPropertiesKeyMapper = { - MNY: () => 'money', - STR: () => 'strength', - INT: () => 'intelligence', - CHR: () => 'charm', - SPR: () => 'spirit', - RND: () => pick(['money', 'strength', 'intelligence', 'charm', 'spirit'])!, -} as Record keyof Properties> +const Grades: TalentGrade[] = [0, 1, 2, 3] as const +const GradeMap = Map.groupBy( + talents.keys().filter(t => !talents.get(t)!.exclusive), + t => talents.get(t)!.grade, +) export const count = talents.size @@ -18,4 +19,197 @@ export function get(talent: number) { return talents.get(talent) } -export function random(talent: number) {} +export type TalentPullRate = Map +export type TalentPullRateAddition = { + mode: 'add' | 'multiply' + value: number +} +export type TalentPullRateAdditions = { + [key in keyof ProfileState]?: ( + value: ProfileState[key], + ) => Map +} +export interface TalentRateOptions { + base: TalentPullRate + additions: TalentPullRateAdditions +} +const AddidtionInit = Grades.map(g => [g, 1]) as [TalentGrade, number][] +function talentRateWithAddition( + { base, additions }: TalentRateOptions, + profile: ProfileState, +) { + const rate = new Map(AddidtionInit) + for (const [key, getAddition] of Object.entries(additions)) { + const value = profile[key as keyof ProfileState] + for (const [k, v] of getAddition(value as any).entries()) { + const current = rate.get(k)! + if (v.mode === 'add') { + rate.set(k, current + v.value) + } else if (v.mode === 'multiply') { + rate.set(k, current * v.value) + } + } + } + for (const [g, addition] of rate.entries()) { + const b = base.get(g) + if (!b) throw new Error(`talent: base rate for grade ${g} is missing`) + rate.set(g, Math.max(b * addition, 0)) + } + return rate +} + +export interface TalentPullOptions { + count: number + rate: TalentRateOptions +} + +export function pull(options: TalentPullOptions, profile: ProfileState) { + const rate = Array.from( + talentRateWithAddition(options.rate, profile).entries(), + ) + const result = [] + if (profile.lockedTalent) result.push(profile.lockedTalent) + const map = new Map(Grades.map(g => [g, new Set(GradeMap.get(g))])) + for (let i = options.count - result.length; i > 0; i--) { + const grade = pickWeight(rate)! ?? 0 + const set = map.get(grade)! + if (set.size === 0) continue + const id = pick(Array.from(set))! + result.push(id) + set.delete(id) + } + return result +} + +export function exclude(talent: Talent['id'], list: Iterable) { + const { exclude } = talents.get(talent)! + for (const t of list) { + if (exclude) { + for (const e of exclude) { + if (t == e) return t + } + } + const excludeReverse = talents.get(t)!.exclude + if (excludeReverse) { + for (const e of excludeReverse) { + if (talent == e) return t + } + } + } + return null +} + +export interface TalentReplacementResult { + state: GameState + chains: Map + changed: boolean +} + +function chainReplace(t: Talent['id'], ts: Set) { + const { replacement: r } = talents.get(t)! + if (!r) return null + let picked: Talent['id'] | null = null + if (r.talent) { + const filtered = r.talent.filter(([id]) => exclude(id, ts) == null) + picked = pickWeight(filtered) + } else if (r.grade) { + const filtered = GradeMap.get(r.grade)!.filter( + id => exclude(id, ts) == null, + ) + picked = pick(filtered) + } + if (!picked) return null + const nts = new Set(ts) + nts.add(picked) + const next = chainReplace(picked, nts) as Talent['id'][] | null + if (next) return [picked, ...next] + return [picked] +} + +export function replacement(state: GameState): TalentReplacementResult { + const chains = new Map() as TalentReplacementResult['chains'] + const newTalents = new Set(state.talents) + let changed = false + for (const talent of state.talents) { + const chain = chainReplace(talent, newTalents) + if (!chain) continue + changed = true + chains.set(talent, chain) + chain.forEach(t => newTalents.add(t)) + } + if (!changed) return { state, chains, changed } + const newState = produce(state, draft => { + draft.talents = newTalents + }) + return { state: newState, chains, changed } +} + +export interface AdditionalPoint { + talent: Talent['id'] + points: number +} + +export interface AdditionalPoints { + source: AdditionalPoint[] + points: number +} + +export function additionalPoints(list: Talent['id'][]) { + const source: AdditionalPoint[] = [] + let total = 0 + for (const id of list) { + const { points } = talents.get(id)! + if (!points) continue + source.push({ talent: id, points }) + total += points + } + return { source, points: total } +} + +const TalentEffectRandomProperties = [ + 'money', + 'strength', + 'intelligence', + 'charm', + 'spirit', +] as (keyof Properties)[] +export interface TalentTriggerResult { + state: GameState + triggers: Talent['id'][] + changed: boolean +} + +export function trigger( + state: GameState, + profile: ProfileState, +): TalentTriggerResult { + const flatState = createFlatState(state, profile) + const triggers: Talent['id'][] = [] + for (const talent of state.talents) { + const { condition, max } = talents.get(talent)! + if (state.talentTriggers.get(talent) ?? 0 >= max) continue + if (condition && !check(flatState, condition)) continue + triggers.push(talent) + } + if (triggers.length === 0) return { state, triggers, changed: false } + const newState = produce(state, draft => { + for (const talent of triggers) { + const times = draft.talentTriggers.get(talent) ?? 0 + draft.talentTriggers.set(talent, times + 1) + const { effect } = talents.get(talent)! + if (!effect) continue + const pe = {} as Partial + if (effect.CHR) pe.charm = effect.CHR + if (effect.INT) pe.intelligence = effect.INT + if (effect.STR) pe.strength = effect.STR + if (effect.MNY) pe.money = effect.MNY + if (effect.SPR) pe.spirit = effect.SPR + if (effect.RND) { + const key = pick(TalentEffectRandomProperties)! + pe[key] = (pe[key] ?? 0) || effect.RND + } + draft.props = propsEffect(draft.props, pe) + } + }) + return { state: newState, triggers, changed: true } +} diff --git a/packages/data/src/talent.types.ts b/packages/data/src/talent.types.ts index 785fd01..7a1b1e1 100644 --- a/packages/data/src/talent.types.ts +++ b/packages/data/src/talent.types.ts @@ -20,14 +20,21 @@ export type TalentEffect = { /** 权重天赋 */ export type TalentWithWeight = [number, number] -/** 天赋盲盒 */ -export type TalentReplacement = { - /** 指定列表盲盒 */ - talent?: TalentWithWeight[] - /** 指定稀有度盲盒 */ - grade?: TalentGrade[] +/** 指定列表盲盒 */ +export type TalentReplacementTalent = { + talent: TalentWithWeight[] + grade?: never } +/** 指定稀有度盲盒 */ +export type TalentReplacementGrade = { + grade: TalentGrade + talent?: never +} + +/** 天赋盲盒 */ +export type TalentReplacement = TalentReplacementTalent | TalentReplacementGrade + /** 天赋 */ export type Talent = { /** 序号 */ @@ -45,11 +52,13 @@ export type Talent = { /** 专属天赋 */ readonly exclusive?: boolean /** 初始可用属性点调整 */ - readonly status?: number + readonly points?: number /** 互斥天赋 */ readonly exclude?: number[] /** 天赋盲盒 */ readonly replacement?: TalentReplacement + /** 天赋触发上限 */ + readonly max: number } // @vt-types-end @@ -80,7 +89,8 @@ export const transformers = { ) return [talent, weight ?? 1] }) - if (val.grade) val.grade = val.grade.map(Number) + if (val.grade) val.grade = Number(val.grade) return val }, + max: (val: any) => Number(val) || 1, } diff --git a/packages/data/src/talent.xlsx b/packages/data/src/talent.xlsx index 68bbd136390a09db2fcbc1685fbb4453acabd5a4..c3db11c94a4dd5031eea62241275f261cc78d031 100644 GIT binary patch literal 37221 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 P@Zdkc=h=^Z{_np4p?nY| literal 36730 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 zð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 diff --git a/packages/vitex/index.ts b/packages/vitex/index.ts index 74ab3fc..2aa3cf5 100644 --- a/packages/vitex/index.ts +++ b/packages/vitex/index.ts @@ -5,7 +5,7 @@ export function random(max: number, min: number = 0, rng?: RNG): number { } export function pick(items: T[], rng?: RNG) { if (items.length === 0) return null - return items[random(items.length - 1, 0, rng)] + return items[random(items.length - 1, 0, rng)] ?? null } export type WeightItem = [T, number] export function pickWeight(items: WeightItem[], rng?: RNG) {