mirror of
https://github.com/VickScarlet/lifeRestart.git
synced 2026-03-24 13:33:26 +08:00
add repl version
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -105,4 +105,6 @@ dist
|
|||||||
|
|
||||||
utils/xlsxTransform-*
|
utils/xlsxTransform-*
|
||||||
|
|
||||||
/.idea
|
/.idea
|
||||||
|
|
||||||
|
__localStorage.json
|
||||||
11
.vscode/launch.json
vendored
11
.vscode/launch.json
vendored
@@ -22,6 +22,15 @@
|
|||||||
"skipFiles": [
|
"skipFiles": [
|
||||||
"<node_internals>/**"
|
"<node_internals>/**"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach by Process ID",
|
||||||
|
"processId": "${command:PickProcess}",
|
||||||
|
"request": "attach",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"type": "node"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
457
repl/app.js
Normal file
457
repl/app.js
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
import { max, sum } from '../src/functions/util.js';
|
||||||
|
import { summary } from '../src/functions/summary.js'
|
||||||
|
import { readFile } from 'fs/promises';
|
||||||
|
import Life from '../src/life.js';
|
||||||
|
|
||||||
|
global.json = async fileName => JSON.parse(await readFile(`data/${fileName}.json`));
|
||||||
|
|
||||||
|
class App {
|
||||||
|
constructor() {
|
||||||
|
this.#life = new Life();
|
||||||
|
}
|
||||||
|
|
||||||
|
Steps= {
|
||||||
|
TALENT: 'talent',
|
||||||
|
PROPERTY: 'property',
|
||||||
|
TRAJECTORY: 'trajectory',
|
||||||
|
SUMMARY: 'summary',
|
||||||
|
};
|
||||||
|
|
||||||
|
#step = this.Steps.SUMMARY;
|
||||||
|
#life;
|
||||||
|
#talentSelected = new Set();
|
||||||
|
#talentExtend = new Set();
|
||||||
|
#input;
|
||||||
|
#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[104m', '\x1B[49m'], // Bright Blue BG
|
||||||
|
grade2b: ['\x1B[105m', '\x1B[49m'], // Bright Magenta BG
|
||||||
|
grade3b: ['\x1B[103m', '\x1B[49m'], // 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 = global.localStorage.talentExtend;
|
||||||
|
await this.#life.initial();
|
||||||
|
this.output('\rLoading Complete.\ntype \x1B[4m/remake\x1B[24m to start', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
io(input, output, exit) {
|
||||||
|
this.#input = input;
|
||||||
|
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': return this.next(true);
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
case 'alloc':
|
||||||
|
case 'allocation':
|
||||||
|
case '/alloc':
|
||||||
|
case '/allocation': return this.alloc(...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) {
|
||||||
|
return `Help ---
|
||||||
|
x
|
||||||
|
exit
|
||||||
|
/exit exit
|
||||||
|
|
||||||
|
r
|
||||||
|
remake
|
||||||
|
/remake remake
|
||||||
|
|
||||||
|
s
|
||||||
|
select
|
||||||
|
/select select
|
||||||
|
|
||||||
|
u
|
||||||
|
unselect
|
||||||
|
/unselect unselect
|
||||||
|
|
||||||
|
n
|
||||||
|
next
|
||||||
|
/next next
|
||||||
|
|
||||||
|
auto
|
||||||
|
/auto auto play
|
||||||
|
|
||||||
|
?
|
||||||
|
h
|
||||||
|
help
|
||||||
|
/?
|
||||||
|
/h
|
||||||
|
/help show this message`;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto(arg) {
|
||||||
|
this.#auto = arg != 'off';
|
||||||
|
return this.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
remake() {
|
||||||
|
if(this.#talentExtend) {
|
||||||
|
this.#life.talentExtend(this.#talentExtend)
|
||||||
|
global.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个天赋');
|
||||||
|
|
||||||
|
const exclusive = this.#life.exclusive(
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
list() {
|
||||||
|
let description, list, check;
|
||||||
|
switch(this.#step) {
|
||||||
|
case this.Steps.TALENT:
|
||||||
|
description = '🎉 请选择3个天赋';
|
||||||
|
list = this.#randomTalents;
|
||||||
|
check = talent=>this.#talentSelected.has(talent);
|
||||||
|
break;
|
||||||
|
case this.Steps.SUMMARY:
|
||||||
|
description = '🎉 你可以选一个天赋继承';
|
||||||
|
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.#propertyAllocation.total = 20 + this.#life.getTalentAllocationAddition(
|
||||||
|
Array.from(this.#talentSelected).map(({id})=>id)
|
||||||
|
);
|
||||||
|
this.#propertyAllocation.TLT = Array.from(this.#talentSelected).map(({id})=>id);
|
||||||
|
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.restart(this.#propertyAllocation);
|
||||||
|
return this.trajectory(enter);
|
||||||
|
case this.Steps.TRAJECTORY:
|
||||||
|
if(!this.#isEnd) return this.trajectory(enter);
|
||||||
|
if(this.#interval) clearInterval(this.#interval);
|
||||||
|
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.#auto = false;
|
||||||
|
} else if(this.#auto) {
|
||||||
|
this.#interval = setInterval(
|
||||||
|
()=>{
|
||||||
|
const trajectory = this.next();
|
||||||
|
if(this.#isEnd && this.#interval) clearInterval(this.#interval);
|
||||||
|
if(!this.#isEnd) return this.output(`${trajectory}\n`);
|
||||||
|
return this.output(trajectory, true);
|
||||||
|
}
|
||||||
|
, 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 `Property Allocation []
|
||||||
|
🎉 剩余点数 ${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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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':
|
||||||
|
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();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return warn('⚠ 未知的tag');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
random() {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
summary() {
|
||||||
|
|
||||||
|
const records = this.#life.getRecord();
|
||||||
|
const s = (type, func)=>{
|
||||||
|
const value = func(records.map(({[type]:v})=>v));
|
||||||
|
const { judge, grade } = summary(type, value);
|
||||||
|
return { judge, grade, value };
|
||||||
|
};
|
||||||
|
|
||||||
|
const style = (name, grade, judge, value) => this.style(`grade${grade}b`, `${name}:${value} ${judge}`);
|
||||||
|
const judge = (name, type, func) => {
|
||||||
|
const { judge, grade, value } = s(type, func);
|
||||||
|
return style(name, grade, judge, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'🎉 总评',
|
||||||
|
judge('颜值', 'CHR', max),
|
||||||
|
judge('智力', 'INT', max),
|
||||||
|
judge('体质', 'STR', max),
|
||||||
|
judge('家境', 'MNY', max),
|
||||||
|
judge('快乐', 'SPR', max),
|
||||||
|
judge('享年', 'AGE', max),
|
||||||
|
(()=>{
|
||||||
|
const m = type=>max(records.map(({[type]: value})=>value));
|
||||||
|
const value = Math.floor(sum(m('CHR'), m('INT'), m('STR'), m('MNY'), m('SPR'))*2 + m('AGE')/2);
|
||||||
|
const { judge, grade } = summary('SUM', value);
|
||||||
|
return style('总评', grade, judge, value );
|
||||||
|
})(),
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
30
repl/index.js
Normal file
30
repl/index.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import App from './app.js';
|
||||||
|
import { readFile, writeFile } from 'fs/promises';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
global.localStorage = JSON.parse(await readFile('__localStorage.json'));
|
||||||
|
} catch (e) {
|
||||||
|
global.localStorage = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
global.dumpLocalStorage = async ()=>await writeFile('__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();
|
||||||
|
// });
|
||||||
Reference in New Issue
Block a user