mirror of
https://github.com/VickScarlet/lifeRestart.git
synced 2026-08-28 17:26:47 +08:00
update: ts
This commit is contained in:
@@ -0,0 +1,870 @@
|
||||
import { fileURLToPath } from 'url'
|
||||
import { dirname } from 'path'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
import { readFile } from 'fs/promises'
|
||||
import Life from '../srco/modules/life.js'
|
||||
import $lang from '../srco/i18n/zh-cn.js'
|
||||
globalThis.$lang = $lang
|
||||
|
||||
globalThis.json = async fileName =>
|
||||
JSON.parse(await readFile(`${__dirname}/../public/data/${fileName}.json`))
|
||||
|
||||
globalThis.$$eventMap = new Map()
|
||||
globalThis.$$event = (tag, data) => {
|
||||
const listener = $$eventMap.get(tag)
|
||||
if (listener) listener.forEach(fn => fn(data))
|
||||
}
|
||||
globalThis.$$on = (tag, fn) => {
|
||||
let listener = $$eventMap.get(tag)
|
||||
if (!listener) {
|
||||
listener = new Set()
|
||||
$$eventMap.set(tag, listener)
|
||||
}
|
||||
listener.add(fn)
|
||||
}
|
||||
globalThis.$$off = (tag, fn) => {
|
||||
const listener = $$eventMap.get(tag)
|
||||
if (listener) listener.delete(fn)
|
||||
}
|
||||
|
||||
class App {
|
||||
constructor() {
|
||||
this.#life.config({
|
||||
defaultPropertyPoints: 20, // default number of points for a property
|
||||
talentSelectLimit: 3, // max number of talents that can be selected
|
||||
propertyAllocateLimit: [0, 10], // scoop of properties that can be allocated,
|
||||
defaultPropertys: { SPR: 5 }, // default properties
|
||||
talentConfig: {
|
||||
// config for talent
|
||||
talentPullCount: 10, // number of talents to pull from the talent pool
|
||||
talentRate: { 1: 100, 2: 10, 3: 1, total: 1000 }, // rate of talent pull
|
||||
additions: {
|
||||
TMS: [
|
||||
[10, { 2: 1 }],
|
||||
[30, { 2: 2 }],
|
||||
[50, { 2: 3 }],
|
||||
[70, { 2: 4 }],
|
||||
[100, { 2: 5 }],
|
||||
],
|
||||
CACHV: [
|
||||
[10, { 2: 1 }],
|
||||
[30, { 2: 2 }],
|
||||
[50, { 2: 3 }],
|
||||
[70, { 2: 4 }],
|
||||
[100, { 2: 5 }],
|
||||
],
|
||||
},
|
||||
},
|
||||
propertyConfig: {
|
||||
// config for property
|
||||
judge: {
|
||||
// type: [min, grade, judge]
|
||||
RTLT: [
|
||||
[0, 0],
|
||||
[0.3, 1],
|
||||
[0.6, 2],
|
||||
[0.9, 3],
|
||||
],
|
||||
REVT: [
|
||||
[0, 0],
|
||||
[0.2, 1],
|
||||
[0.4, 2],
|
||||
[0.6, 3],
|
||||
],
|
||||
TMS: [
|
||||
[0, 0, 'UI_Remake_Times_Judge_Level_0'],
|
||||
[10, 1, 'UI_Remake_Times_Judge_Level_1'],
|
||||
[30, 1, 'UI_Remake_Times_Judge_Level_2'],
|
||||
[50, 2, 'UI_Remake_Times_Judge_Level_3'],
|
||||
[70, 2, 'UI_Remake_Times_Judge_Level_4'],
|
||||
[100, 3, 'UI_Remake_Times_Judge_Level_5'],
|
||||
],
|
||||
CACHV: [
|
||||
[0, 0, 'UI_Achievement_Count_Judge_Level_0'],
|
||||
[10, 1, 'UI_Achievement_Count_Judge_Level_1'],
|
||||
[30, 1, 'UI_Achievement_Count_Judge_Level_2'],
|
||||
[50, 2, 'UI_Achievement_Count_Judge_Level_3'],
|
||||
[70, 2, 'UI_Achievement_Count_Judge_Level_4'],
|
||||
[100, 3, 'UI_Achievement_Count_Judge_Level_5'],
|
||||
],
|
||||
HCHR: [
|
||||
[0, 0, 'UI_Judge_Level_0'],
|
||||
[1, 0, 'UI_Judge_Level_1'],
|
||||
[2, 0, 'UI_Judge_Level_2'],
|
||||
[4, 0, 'UI_Judge_Level_3'],
|
||||
[7, 1, 'UI_Judge_Level_4'],
|
||||
[9, 2, 'UI_Judge_Level_5'],
|
||||
[11, 3, 'UI_Judge_Level_6'],
|
||||
],
|
||||
HMNY: [
|
||||
[0, 0, 'UI_Judge_Level_0'],
|
||||
[1, 0, 'UI_Judge_Level_1'],
|
||||
[2, 0, 'UI_Judge_Level_2'],
|
||||
[4, 0, 'UI_Judge_Level_3'],
|
||||
[7, 1, 'UI_Judge_Level_4'],
|
||||
[9, 2, 'UI_Judge_Level_5'],
|
||||
[11, 3, 'UI_Judge_Level_6'],
|
||||
],
|
||||
HSPR: [
|
||||
[0, 0, 'UI_Spirit_Judge_Level_0'],
|
||||
[1, 0, 'UI_Spirit_Judge_Level_1'],
|
||||
[2, 0, 'UI_Spirit_Judge_Level_2'],
|
||||
[4, 0, 'UI_Spirit_Judge_Level_3'],
|
||||
[7, 1, 'UI_Spirit_Judge_Level_4'],
|
||||
[9, 2, 'UI_Spirit_Judge_Level_5'],
|
||||
[11, 3, 'UI_Spirit_Judge_Level_6'],
|
||||
],
|
||||
HINT: [
|
||||
[0, 0, 'UI_Judge_Level_0'],
|
||||
[1, 0, 'UI_Judge_Level_1'],
|
||||
[2, 0, 'UI_Judge_Level_2'],
|
||||
[4, 0, 'UI_Judge_Level_3'],
|
||||
[7, 1, 'UI_Judge_Level_4'],
|
||||
[9, 2, 'UI_Judge_Level_5'],
|
||||
[11, 3, 'UI_Judge_Level_6'],
|
||||
[21, 3, 'UI_Intelligence_Judge_Level_7'],
|
||||
[131, 3, 'UI_Intelligence_Judge_Level_8'],
|
||||
[501, 3, 'UI_Intelligence_Judge_Level_9'],
|
||||
],
|
||||
HSTR: [
|
||||
[0, 0, 'UI_Judge_Level_0'],
|
||||
[1, 0, 'UI_Judge_Level_1'],
|
||||
[2, 0, 'UI_Judge_Level_2'],
|
||||
[4, 0, 'UI_Judge_Level_3'],
|
||||
[7, 1, 'UI_Judge_Level_4'],
|
||||
[9, 2, 'UI_Judge_Level_5'],
|
||||
[11, 3, 'UI_Judge_Level_6'],
|
||||
[21, 3, 'UI_Strength_Judge_Level_7'],
|
||||
[101, 3, 'UI_Strength_Judge_Level_8'],
|
||||
[401, 3, 'UI_Strength_Judge_Level_9'],
|
||||
[1001, 3, 'UI_Strength_Judge_Level_10'],
|
||||
[2001, 3, 'UI_Strength_Judge_Level_11'],
|
||||
],
|
||||
HAGE: [
|
||||
[0, 0, 'UI_AGE_Judge_Level_0'],
|
||||
[1, 0, 'UI_AGE_Judge_Level_1'],
|
||||
[10, 0, 'UI_AGE_Judge_Level_2'],
|
||||
[18, 0, 'UI_AGE_Judge_Level_3'],
|
||||
[40, 0, 'UI_AGE_Judge_Level_4'],
|
||||
[60, 1, 'UI_AGE_Judge_Level_5'],
|
||||
[70, 1, 'UI_AGE_Judge_Level_6'],
|
||||
[80, 2, 'UI_AGE_Judge_Level_7'],
|
||||
[90, 2, 'UI_AGE_Judge_Level_8'],
|
||||
[95, 3, 'UI_AGE_Judge_Level_9'],
|
||||
[100, 3, 'UI_AGE_Judge_Level_10'],
|
||||
[500, 3, 'UI_AGE_Judge_Level_11'],
|
||||
],
|
||||
SUM: [
|
||||
[0, 0, 'UI_Judge_Level_0'],
|
||||
[41, 0, 'UI_Judge_Level_1'],
|
||||
[50, 0, 'UI_Judge_Level_2'],
|
||||
[60, 0, 'UI_Judge_Level_3'],
|
||||
[80, 1, 'UI_Judge_Level_4'],
|
||||
[100, 2, 'UI_Judge_Level_5'],
|
||||
[110, 3, 'UI_Judge_Level_6'],
|
||||
[120, 3, 'UI_Judge_Level_7'],
|
||||
],
|
||||
},
|
||||
},
|
||||
characterConfig: {
|
||||
// config for character
|
||||
characterPullCount: 3,
|
||||
rateableKnife: 10,
|
||||
propertyWeight: [
|
||||
[0, 1],
|
||||
[1, 2],
|
||||
[2, 3],
|
||||
[3, 4],
|
||||
[4, 5],
|
||||
[5, 6],
|
||||
[6, 5],
|
||||
[7, 4],
|
||||
[8, 3],
|
||||
[9, 2],
|
||||
[10, 1],
|
||||
],
|
||||
talentWeight: [
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[3, 3],
|
||||
[4, 2],
|
||||
[5, 1],
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
Steps = {
|
||||
TALENT: 'talent',
|
||||
PROPERTY: 'property',
|
||||
TRAJECTORY: 'trajectory',
|
||||
SUMMARY: 'summary',
|
||||
}
|
||||
#language = 'zh-cn'
|
||||
#step = this.Steps.SUMMARY
|
||||
#life = new Life()
|
||||
#talentSelected = new Set()
|
||||
#talentExtend = new Set()
|
||||
#auto
|
||||
#isEnd
|
||||
#propertyAllocation
|
||||
#output
|
||||
#exit
|
||||
#interval
|
||||
#style = {
|
||||
warn: ['\x1B[93m', '\x1B[39m'], // Bright Yellow
|
||||
grade1: ['\x1B[94m', '\x1B[39m'], // Bright Blue
|
||||
grade2: ['\x1B[95m', '\x1B[39m'], // Bright Magenta
|
||||
grade3: ['\x1B[93m', '\x1B[39m'], // Bright Yellow
|
||||
grade1b: ['\x1B[94m\x1B[7m', '\x1B[0m'], // Bright Blue BG
|
||||
grade2b: ['\x1B[95m\x1B[7m', '\x1B[0m'], // Bright Magenta BG
|
||||
grade3b: ['\x1B[93m\x1B[7m', '\x1B[0m'], // Bright Yellow BG
|
||||
}
|
||||
#randomTalents
|
||||
|
||||
style(type, str) {
|
||||
const style = this.#style[type]
|
||||
if (!style) return str
|
||||
return `${style[0]}${str}${style[1]}`
|
||||
}
|
||||
|
||||
async initial() {
|
||||
this.output('Now Loading...')
|
||||
this.#talentExtend = localStorage.talentExtend
|
||||
await this.#life.initial(
|
||||
dataSet => json(`${this.#language}/${dataSet}`),
|
||||
json,
|
||||
)
|
||||
this.output(
|
||||
`\rLoading Complete.
|
||||
人生重开模拟器
|
||||
这垃圾人生一秒也不想待了
|
||||
|
||||
键入 \x1B[4m/remake\x1B[24m 开始游戏
|
||||
键入 \x1B[4m/help\x1B[24m 获取帮助`,
|
||||
true,
|
||||
)
|
||||
$$on('achievement', ({ name }) =>
|
||||
this.output(`
|
||||
-------------------------
|
||||
解锁成就【${name}】
|
||||
-------------------------
|
||||
`),
|
||||
)
|
||||
}
|
||||
|
||||
io(input, output, exit) {
|
||||
this.#output = output
|
||||
this.#exit = exit
|
||||
input(command => {
|
||||
const ret = this.repl(command)
|
||||
if (!ret) return
|
||||
if (typeof ret == 'string') return this.output(ret, true)
|
||||
if (Array.isArray(ret)) return this.output(...ret)
|
||||
const { message, isRepl } = ret
|
||||
return this.output(message, isRepl)
|
||||
})
|
||||
}
|
||||
|
||||
output(data, isRepl) {
|
||||
if (!this.#output) return
|
||||
this.#output(data, isRepl)
|
||||
}
|
||||
|
||||
exit(code) {
|
||||
if (this.#exit) this.#exit(code)
|
||||
process.exit(code)
|
||||
}
|
||||
|
||||
repl(command) {
|
||||
command = command.split(/\s+/)
|
||||
switch (command.shift()) {
|
||||
case 'r':
|
||||
case 'remake':
|
||||
case '/remake':
|
||||
return this.remake()
|
||||
|
||||
case 's':
|
||||
case 'select':
|
||||
case '/select':
|
||||
return this.select(...command)
|
||||
|
||||
case 'u':
|
||||
case 'unselect':
|
||||
case '/unselect':
|
||||
return this.unselect(...command)
|
||||
|
||||
case 'n':
|
||||
case 'next':
|
||||
case '/next':
|
||||
case '':
|
||||
return this.next(true)
|
||||
|
||||
case 'a':
|
||||
case 'alloc':
|
||||
case 'allocate':
|
||||
case 'attrib':
|
||||
case 'attribute':
|
||||
case '/alloc':
|
||||
case '/allocate':
|
||||
case '/attrib':
|
||||
case '/attribute':
|
||||
return this.attrib(...command)
|
||||
|
||||
case 'rd':
|
||||
case 'random':
|
||||
case '/random':
|
||||
return this.random()
|
||||
|
||||
case 'at':
|
||||
case 'auto':
|
||||
case '/auto':
|
||||
return this.auto(...command)
|
||||
|
||||
case 'x':
|
||||
case 'exit':
|
||||
case '/exit':
|
||||
return this.exit(0)
|
||||
|
||||
case '?':
|
||||
case 'h':
|
||||
case 'help':
|
||||
case '/?':
|
||||
case '/h':
|
||||
case '/help':
|
||||
default:
|
||||
return this.help(...command)
|
||||
}
|
||||
}
|
||||
|
||||
help(key) {
|
||||
switch (key) {
|
||||
case 'x':
|
||||
case 'exit':
|
||||
case '/exit':
|
||||
return `退出
|
||||
x, exit, /exit 命令同等效果`
|
||||
|
||||
case 'r':
|
||||
case 'remake':
|
||||
case '/remake':
|
||||
return `重开
|
||||
r, remake, /remake 命令同等效果`
|
||||
|
||||
case 's':
|
||||
case 'select':
|
||||
case '/select':
|
||||
return `选择
|
||||
s, select, /select 命令同等效果
|
||||
|
||||
Example: /select 1 2 3 意味着选择 1 2 3 三个天赋
|
||||
|
||||
/select <id1> [id2] [id3]
|
||||
|
||||
参数解释 <id1> 通常来说要指定至少一个id
|
||||
虽然不指定也可以
|
||||
[id2]
|
||||
[id3] 可以不指定`
|
||||
|
||||
case 'u':
|
||||
case 'unselect':
|
||||
case '/unselect':
|
||||
return `取消选择
|
||||
u, unselect,
|
||||
/unselect 命令同等效果
|
||||
|
||||
Example: /unselect 1 2 3
|
||||
意味着取消选择 1 2 3 三个天赋
|
||||
|
||||
参数解释 /unselect <id1> [id2] [id3]
|
||||
|
||||
<id1> 通常来说要指定至少一个id
|
||||
虽然不指定也可以
|
||||
[id2]
|
||||
[id3] 可以不指定`
|
||||
|
||||
case 'a':
|
||||
case 'alloc':
|
||||
case 'allocate':
|
||||
case 'attrib':
|
||||
case 'attribute':
|
||||
case '/alloc':
|
||||
case '/allocate':
|
||||
case '/attrib':
|
||||
case '/attribute':
|
||||
return `分配或查看属性点
|
||||
a, alloc, allocate, attrib, attribute
|
||||
/alloc, /allocate, /attrib, /attribute 命令同等效果
|
||||
|
||||
Example: /attribute
|
||||
/allocate STR 1
|
||||
/allocate INT -3
|
||||
/allocate CHR +5
|
||||
|
||||
效果 在属性分配时分配属性点
|
||||
在人生的过程中查看当前属性点
|
||||
|
||||
参数解释 /allocate <TAG> <[+/-]value>
|
||||
|
||||
<TAG> 表示要分配的属性标签
|
||||
可选有
|
||||
CHR, chr, c, C 表示颜值
|
||||
INT, int, i, I 表示智力
|
||||
STR, str, s, S 表示体质
|
||||
MNY, mny, m, M 表示家境
|
||||
必填
|
||||
|
||||
<[+/-]value>
|
||||
表示属性的调整
|
||||
其中
|
||||
+ 表示在当前基础上增加
|
||||
- 表示在当前基础上减少
|
||||
无符号表示直接设置为此值
|
||||
必填`
|
||||
|
||||
case 'n':
|
||||
case 'next':
|
||||
case '/next':
|
||||
return `继续
|
||||
n, next, /next 命令同等效果
|
||||
|
||||
效果 通常用于各步骤结束后
|
||||
例如: 选择天赋后
|
||||
分配属性后
|
||||
每个年龄事件后
|
||||
总评后
|
||||
继承天赋后`
|
||||
|
||||
case 'at':
|
||||
case 'auto':
|
||||
case '/auto':
|
||||
return `自动播放
|
||||
at, auto, /auto 命令同等效果
|
||||
|
||||
效果 用于人生的过程中
|
||||
每个年龄会自动下一年
|
||||
播放速度 1 秒 1 年`
|
||||
|
||||
case '?':
|
||||
case 'h':
|
||||
case 'help':
|
||||
case '/?':
|
||||
case '/h':
|
||||
case '/help':
|
||||
return `显示帮助
|
||||
?, h, help
|
||||
/?, /h, /help 命令同等效果
|
||||
|
||||
Example: /help
|
||||
/help /select
|
||||
|
||||
参数解释 /help [command]
|
||||
|
||||
[command] 要详细显示帮助的命令
|
||||
可以不填`
|
||||
}
|
||||
return `Help ---
|
||||
命令 说明 示例
|
||||
x
|
||||
exit
|
||||
/exit 退出 /exit
|
||||
|
||||
r
|
||||
remake
|
||||
/remake 重开 /remake
|
||||
|
||||
s
|
||||
select
|
||||
/select 选择天赋 /select <id1> [id2] [id3]
|
||||
|
||||
u
|
||||
unselect
|
||||
/unselect 取消选择 /unselect <id1> [id2] [id3]
|
||||
|
||||
a
|
||||
alloc
|
||||
allocate
|
||||
attrib
|
||||
attribute
|
||||
/alloc
|
||||
/allocate
|
||||
/attrib
|
||||
/attribute 分配或查看属性点 /allocate <TAG> <[+/-]value>
|
||||
|
||||
n
|
||||
next
|
||||
/next 继续 /next
|
||||
|
||||
at
|
||||
auto
|
||||
/auto 自动播放 /auto
|
||||
|
||||
?
|
||||
h
|
||||
help
|
||||
/?
|
||||
/h
|
||||
/help 显示帮助 /help [command]`
|
||||
}
|
||||
|
||||
auto(arg) {
|
||||
this.#auto = arg != 'off'
|
||||
return this.next(true)
|
||||
}
|
||||
|
||||
remake() {
|
||||
if (this.#talentExtend) {
|
||||
this.#life.talentExtend(this.#talentExtend)
|
||||
dumpLocalStorage()
|
||||
this.#talentExtend = null
|
||||
}
|
||||
|
||||
this.#isEnd = false
|
||||
this.#talentSelected.clear()
|
||||
this.#propertyAllocation = {
|
||||
CHR: 0,
|
||||
INT: 0,
|
||||
STR: 0,
|
||||
MNY: 0,
|
||||
SPR: 5,
|
||||
}
|
||||
this.#step = this.Steps.TALENT
|
||||
this.#randomTalents = this.#life.talentRandom()
|
||||
return this.list()
|
||||
}
|
||||
|
||||
select(...select) {
|
||||
switch (this.#step) {
|
||||
case this.Steps.TALENT:
|
||||
return this.talentSelect(...select)
|
||||
case this.Steps.SUMMARY:
|
||||
return this.talentExtend(...select)
|
||||
}
|
||||
}
|
||||
|
||||
unselect(...select) {
|
||||
switch (this.#step) {
|
||||
case this.Steps.TALENT:
|
||||
return this.talentUnSelect(...select)
|
||||
case this.Steps.SUMMARY:
|
||||
return this.talentExtendCancle(...select)
|
||||
}
|
||||
}
|
||||
|
||||
talentSelect(...select) {
|
||||
const warn = str => `${this.list()}\n${this.style('warn', str)}`
|
||||
for (const number of select) {
|
||||
const s = this.#randomTalents[number]
|
||||
if (!s) return warn(`${number} 为未知天赋`)
|
||||
if (this.#talentSelected.has(s)) continue
|
||||
if (this.#talentSelected.size == 3)
|
||||
return warn(
|
||||
'你只能选3个天赋。请使用 \x1B[4m/unselect\x1B[24m 取消选择你不想要的天赋',
|
||||
)
|
||||
|
||||
const exclusive = this.#life.exclude(
|
||||
Array.from(this.#talentSelected).map(({ id }) => id),
|
||||
s.id,
|
||||
)
|
||||
|
||||
if (exclusive != null)
|
||||
for (const { name, id } of this.#talentSelected)
|
||||
if (id == exclusive)
|
||||
return warn(
|
||||
`天赋【${s.name}】与已选择的天赋【${name}】冲突`,
|
||||
)
|
||||
|
||||
this.#talentSelected.add(s)
|
||||
}
|
||||
|
||||
return this.list()
|
||||
}
|
||||
|
||||
talentUnSelect(...select) {
|
||||
for (const number of select) {
|
||||
const s = this.#randomTalents[number]
|
||||
if (this.#talentSelected.has(s)) this.#talentSelected.delete(s)
|
||||
}
|
||||
return this.list()
|
||||
}
|
||||
|
||||
talentExtend(select) {
|
||||
const warn = str => `${this.list()}\n${this.style('warn', str)}`
|
||||
const list = Array.from(this.#talentSelected)
|
||||
const s = list[select]
|
||||
if (!s) return warn(`${select} 为未知天赋`)
|
||||
this.#talentExtend = s.id
|
||||
return this.list()
|
||||
}
|
||||
|
||||
talentExtendCancle() {
|
||||
this.#talentExtend = null
|
||||
return this.list()
|
||||
}
|
||||
|
||||
list() {
|
||||
let description, list, check
|
||||
switch (this.#step) {
|
||||
case this.Steps.TALENT:
|
||||
description = '🎉 请选择(\x1B[4m/select\x1B[24m)3 个天赋'
|
||||
list = this.#randomTalents
|
||||
check = talent => this.#talentSelected.has(talent)
|
||||
break
|
||||
case this.Steps.SUMMARY:
|
||||
description =
|
||||
'🎉 你可以选(\x1B[4m/select\x1B[24m)一个天赋继承'
|
||||
list = Array.from(this.#talentSelected)
|
||||
check = ({ id }) => this.#talentExtend == id
|
||||
break
|
||||
}
|
||||
if (!list) return ''
|
||||
|
||||
return [
|
||||
description,
|
||||
list.map((talent, i) =>
|
||||
this.style(
|
||||
`grade${talent.grade}b`,
|
||||
`${check(talent) ? '√' : ' '} ${i} ${talent.name}(${
|
||||
talent.description
|
||||
})`,
|
||||
),
|
||||
),
|
||||
]
|
||||
.flat()
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
next(enter) {
|
||||
const warn = (a, b) =>
|
||||
`${a}\n${this.style('warn', this.style('warn', b))}`
|
||||
switch (this.#step) {
|
||||
case this.Steps.TALENT:
|
||||
if (this.#talentSelected.size != 3)
|
||||
return warn(this.list(), `请选择 3 个天赋`)
|
||||
this.#step = this.Steps.PROPERTY
|
||||
this.#life.remake(
|
||||
Array.from(this.#talentSelected).map(({ id }) => id),
|
||||
)
|
||||
this.#propertyAllocation.total = this.#life.getPropertyPoints()
|
||||
return this.prop()
|
||||
case this.Steps.PROPERTY:
|
||||
const less = this.less()
|
||||
if (less > 0)
|
||||
return warn(this.prop(), `你还有 ${less} 属性点没有分配完`)
|
||||
this.#step = this.Steps.TRAJECTORY
|
||||
delete this.#propertyAllocation.total
|
||||
this.#life.start(this.#propertyAllocation)
|
||||
return this.trajectory(enter)
|
||||
case this.Steps.TRAJECTORY:
|
||||
if (!this.#isEnd) return this.trajectory(enter)
|
||||
this.#step = this.Steps.SUMMARY
|
||||
return `${this.summary()}\n\n${this.list()}`
|
||||
case this.Steps.SUMMARY:
|
||||
return this.remake()
|
||||
}
|
||||
}
|
||||
|
||||
trajectory(enter) {
|
||||
if (enter) {
|
||||
if (this.#interval) {
|
||||
clearInterval(this.#interval)
|
||||
this.#interval = null
|
||||
this.#auto = false
|
||||
} else if (this.#auto) {
|
||||
this.#interval = setInterval(
|
||||
() => {
|
||||
const trajectory = this.next()
|
||||
if (this.#isEnd && this.#interval) {
|
||||
clearInterval(this.#interval)
|
||||
this.#interval = null
|
||||
}
|
||||
if (!this.#isEnd) return this.output(`${trajectory}\n`)
|
||||
return this.output(trajectory, true)
|
||||
},
|
||||
Number(this.#auto) || 1000,
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const trajectory = this.#life.next()
|
||||
const { age, content, isEnd } = trajectory
|
||||
if (isEnd) this.#isEnd = true
|
||||
return `${age}岁:\t${content
|
||||
.map(({ type, description, grade, name, postEvent }) => {
|
||||
switch (type) {
|
||||
case 'TLT':
|
||||
return `天赋【${name}】发动:${description}`
|
||||
case 'EVT':
|
||||
return (
|
||||
description + (postEvent ? `\n\t${postEvent}` : '')
|
||||
)
|
||||
}
|
||||
})
|
||||
.join('\n\t')}`
|
||||
}
|
||||
|
||||
prop() {
|
||||
const { CHR, INT, STR, MNY } = this.#propertyAllocation
|
||||
return `🎉 属性分配
|
||||
请使用 \x1B[4m/alloc\x1B[24m <TAG> <value> 分配属性
|
||||
剩余点数 ${this.less()}
|
||||
|
||||
属性(TAG) 当前值
|
||||
颜值(CHR) ${CHR}
|
||||
智力(INT) ${INT}
|
||||
体质(STR) ${STR}
|
||||
家境(MNY) ${MNY}
|
||||
`
|
||||
}
|
||||
|
||||
less() {
|
||||
const { total, CHR, INT, STR, MNY } = this.#propertyAllocation
|
||||
return total - CHR - INT - STR - MNY
|
||||
}
|
||||
|
||||
attrib(tag, value) {
|
||||
switch (this.#step) {
|
||||
case this.Steps.PROPERTY:
|
||||
return this.alloc(tag, value)
|
||||
|
||||
case this.Steps.TRAJECTORY:
|
||||
return this.showProperty()
|
||||
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
showProperty() {
|
||||
let property = this.#life.getLastRecord()
|
||||
return `当前属性
|
||||
|
||||
属性(TAG) 当前值
|
||||
颜值(CHR) ${property.CHR}
|
||||
智力(INT) ${property.INT}
|
||||
体质(STR) ${property.STR}
|
||||
家境(MNY) ${property.MNY}
|
||||
快乐(SPR) ${property.SPR}`
|
||||
}
|
||||
|
||||
alloc(tag, value) {
|
||||
const warn = str => `${this.prop()}\n${this.style('warn', str)}`
|
||||
if (!value) return warn('⚠ 分配的数值没有给定')
|
||||
const isSet = !(value[0] == '-' || value[0] == '+')
|
||||
|
||||
value = Number(value)
|
||||
if (isNaN(value)) return warn('⚠ 分配的数值不正确')
|
||||
|
||||
switch (tag) {
|
||||
case 'c':
|
||||
case 'chr':
|
||||
case 'C':
|
||||
tag = 'CHR'
|
||||
break
|
||||
case 'i':
|
||||
case 'int':
|
||||
case 'I':
|
||||
tag = 'INT'
|
||||
break
|
||||
case 's':
|
||||
case 'S':
|
||||
case 'str':
|
||||
tag = 'STR'
|
||||
break
|
||||
case 'm':
|
||||
case 'M':
|
||||
case 'mny':
|
||||
tag = 'MNY'
|
||||
break
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case 'CHR':
|
||||
case 'INT':
|
||||
case 'STR':
|
||||
case 'MNY':
|
||||
break
|
||||
default:
|
||||
return warn('⚠ 未知的tag')
|
||||
}
|
||||
|
||||
if (isSet) value = value - this.#propertyAllocation[tag]
|
||||
|
||||
const tempLess = this.less() - value
|
||||
const tempSet = this.#propertyAllocation[tag] + value
|
||||
|
||||
if (tempLess < 0) return warn('⚠ 你没有更多的点数可以分配了')
|
||||
if (tempLess > this.#propertyAllocation.total || tempSet < 0)
|
||||
return warn('⚠ 不能分配负数属性')
|
||||
if (tempSet > 10) return warn('⚠ 单项属性最高分配10点')
|
||||
|
||||
this.#propertyAllocation[tag] += value
|
||||
|
||||
return this.prop()
|
||||
}
|
||||
|
||||
random() {
|
||||
switch (this.#step) {
|
||||
case this.Steps.TALENT:
|
||||
this.#talentSelected.clear()
|
||||
Array.from(this.#randomTalents)
|
||||
.sort(() => Math.random() - 0.5)
|
||||
.slice(0, 3)
|
||||
.forEach(talent => {
|
||||
this.#talentSelected.add(talent)
|
||||
})
|
||||
return this.list()
|
||||
case this.Steps.PROPERTY:
|
||||
let t = this.#propertyAllocation.total
|
||||
const arr = [10, 10, 10, 10]
|
||||
while (t > 0) {
|
||||
const sub =
|
||||
Math.round(Math.random() * (Math.min(t, 10) - 1)) + 1
|
||||
while (true) {
|
||||
const select = Math.floor(Math.random() * 4) % 4
|
||||
if (arr[select] - sub < 0) continue
|
||||
arr[select] -= sub
|
||||
t -= sub
|
||||
break
|
||||
}
|
||||
}
|
||||
this.#propertyAllocation.CHR = 10 - arr[0]
|
||||
this.#propertyAllocation.INT = 10 - arr[1]
|
||||
this.#propertyAllocation.STR = 10 - arr[2]
|
||||
this.#propertyAllocation.MNY = 10 - arr[3]
|
||||
return this.prop()
|
||||
case this.Steps.SUMMARY:
|
||||
this.#talentExtend = Array.from(this.#talentSelected).sort(
|
||||
() => Math.random() - 0.5,
|
||||
)[0].id
|
||||
return this.list()
|
||||
}
|
||||
}
|
||||
|
||||
summary() {
|
||||
const summaryData = this.#life.summary
|
||||
const format = (name, type) => {
|
||||
const { judge, grade, value } = summaryData[type]
|
||||
return this.style(
|
||||
`grade${grade}b`,
|
||||
`${name}:${value} ${$lang[judge]}`,
|
||||
)
|
||||
}
|
||||
|
||||
return [
|
||||
'🎉 总评',
|
||||
format('颜值', this.#life.PropertyTypes.HCHR),
|
||||
format('智力', this.#life.PropertyTypes.HINT),
|
||||
format('体质', this.#life.PropertyTypes.HSTR),
|
||||
format('家境', this.#life.PropertyTypes.HMNY),
|
||||
format('快乐', this.#life.PropertyTypes.HSPR),
|
||||
format('享年', this.#life.PropertyTypes.HAGE),
|
||||
format('总评', this.#life.PropertyTypes.SUM),
|
||||
].join('\n')
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -0,0 +1,44 @@
|
||||
import { fileURLToPath } from 'url'
|
||||
import { dirname } from 'path'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
import App from './app.js'
|
||||
import { readFile, writeFile } from 'fs/promises'
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
globalThis.localStorage = JSON.parse(
|
||||
await readFile(__dirname + '/__localStorage.json')
|
||||
)
|
||||
} catch (e) {
|
||||
globalThis.localStorage = {}
|
||||
}
|
||||
localStorage.getItem = key =>
|
||||
localStorage[key] === void 0 ? null : localStorage[key]
|
||||
localStorage.setItem = (key, value) => (localStorage[key] = value)
|
||||
|
||||
globalThis.dumpLocalStorage = async () =>
|
||||
await writeFile(
|
||||
__dirname + '/__localStorage.json',
|
||||
JSON.stringify(global.localStorage)
|
||||
)
|
||||
|
||||
const app = new App()
|
||||
app.io(
|
||||
repl => process.stdin.on('data', data => repl(data.toString().trim())),
|
||||
(data, isRepl) => process.stdout.write(`${data}${isRepl ? '\n>' : ''}`),
|
||||
code => process.exit(code)
|
||||
)
|
||||
await app.initial()
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
// process.stdin.setRawMode(true);
|
||||
|
||||
// process.openStdin().on('keypress', function (chunk, key) {
|
||||
// process.stdout.write('Get Chunk: ' + chunk + '\n');
|
||||
// if (key && key.ctrl && key.name == 'c') process.exit();
|
||||
// });
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@remake/console",
|
||||
"type": "module",
|
||||
"version": "3.0.0",
|
||||
"author": "Vick Scarlet <vick@syaro.io>",
|
||||
"scripts": {
|
||||
"start": "bun run src/index.ts",
|
||||
"lint": "eslint .",
|
||||
"test": "bun test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@remake/core": "workspace:*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"types": ["bun"],
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
},
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noImplicitAny": true
|
||||
},
|
||||
"include": ["**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user