This commit is contained in:
Vick Scarlet
2021-08-17 17:07:14 +08:00
parent 776bc33de1
commit dd194c899a
20 changed files with 2660 additions and 573 deletions

1
.gitignore vendored
View File

@@ -103,5 +103,4 @@ dist
# TernJS port file
.tern-port
view/
utils/xlsxTransform-*

2
.vscode/launch.json vendored
View File

@@ -8,7 +8,7 @@
"type": "node",
"request": "launch",
"name": "test",
"program": "${workspaceFolder}/test.js",
"program": "${workspaceFolder}/test",
"skipFiles": [
"<node_internals>/**"
]

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -3,37 +3,37 @@
"id": "1001",
"name": "随身玉佩",
"description": "2岁时不会死",
"rare": "0"
"grade": "0"
},
"1002": {
"id": "1002",
"name": "红肚兜",
"description": "3岁时不会死",
"rare": "0"
"grade": "0"
},
"1003": {
"id": "1003",
"name": "生而为男",
"description": "性别一定为男",
"rare": "1"
"grade": "1"
},
"1004": {
"id": "1004",
"name": "生而为女",
"description": "性别一定为女",
"rare": "1"
"grade": "1"
},
"1005": {
"id": "1005",
"name": "动漫高手",
"description": "入宅的可能性翻6倍",
"rare": "2"
"grade": "2"
},
"1006": {
"id": "1006",
"name": "乐天派",
"description": "快乐+1",
"rare": "0",
"grade": "0",
"effect": {
"SPR": "1"
}
@@ -42,14 +42,14 @@
"id": "1007",
"name": "天赋异禀",
"description": "初始可用属性点+2",
"rare": "1",
"grade": "1",
"status": "2"
},
"1008": {
"id": "1008",
"name": "天生抑郁",
"description": "快乐-3",
"rare": "0",
"grade": "0",
"effect": {
"SPR": "-3"
}
@@ -58,7 +58,7 @@
"id": "1009",
"name": "网络巨魔",
"description": "快乐+2",
"rare": "1",
"grade": "1",
"effect": {
"SPR": "2"
}
@@ -67,31 +67,31 @@
"id": "1010",
"name": "天龙人",
"description": "你拥有北京户口",
"rare": "2"
"grade": "2"
},
"1011": {
"id": "1011",
"name": "独生子女",
"description": "你没有兄弟姐妹",
"rare": "0"
"grade": "0"
},
"1012": {
"id": "1012",
"name": "乡间微风",
"description": "你出生在农村",
"rare": "0"
"grade": "0"
},
"1013": {
"id": "1013",
"name": "城中高楼",
"description": "你出生在城市",
"rare": "0"
"grade": "0"
},
"1014": {
"id": "1014",
"name": "美籍华人",
"description": "你有美国国籍",
"rare": "2",
"grade": "2",
"effect": {
"MNY": "3"
}
@@ -100,25 +100,25 @@
"id": "1015",
"name": "家中老大",
"description": "你最受父母宠爱",
"rare": "1"
"grade": "1"
},
"1016": {
"id": "1016",
"name": "水性良好",
"description": "不会被淹死",
"rare": "0"
"grade": "0"
},
"1017": {
"id": "1017",
"name": "先天免疫",
"description": "你不会得艾滋病",
"rare": "0"
"grade": "0"
},
"1018": {
"id": "1018",
"name": "天选之人",
"name": "人类进化",
"description": "所有属性+1",
"rare": "2",
"grade": "2",
"effect": {
"SPR": "1"
},
@@ -130,32 +130,32 @@
"1019": {
"id": "1019",
"name": "超凡",
"description": "初始可用属性点+3",
"rare": "2"
"description": "初始可用属性点+4",
"grade": "2"
},
"1020": {
"id": "1020",
"name": "父母美貌",
"description": "颜值+2",
"rare": "1"
"grade": "1"
},
"1021": {
"id": "1021",
"name": "红颜薄命",
"description": "颜值+2体质-2",
"rare": "0"
"grade": "0"
},
"1022": {
"id": "1022",
"name": "属蛇",
"description": "不会被蛇咬死",
"rare": "0"
"grade": "0"
},
"1023": {
"id": "1023",
"name": "半神",
"description": "所有属性+2",
"rare": "3",
"grade": "3",
"effect": {
"SPR": "2"
},
@@ -168,49 +168,49 @@
"id": "1024",
"name": "人中龙凤",
"description": "天生双重性别",
"rare": "2"
"grade": "2"
},
"1025": {
"id": "1025",
"name": "阴阳之外",
"description": "天生无性别",
"rare": "2"
"grade": "2"
},
"1026": {
"id": "1026",
"name": "彩虹之下",
"description": "可能和同性交往",
"rare": "0"
"grade": "0"
},
"1027": {
"id": "1027",
"name": "斩情证道",
"description": "终生不恋爱结婚",
"rare": "1"
"grade": "1"
},
"1028": {
"id": "1028",
"name": "桃花连连",
"description": "恋爱机会提升",
"rare": "0"
"grade": "0"
},
"1029": {
"id": "1029",
"name": "平安童年",
"description": "12岁前父母都健在",
"rare": "1"
"grade": "1"
},
"1030": {
"id": "1030",
"name": "宠物大师",
"description": "宠物不会意外死亡",
"rare": "0"
"grade": "0"
},
"1031": {
"id": "1031",
"name": "天生残疾",
"description": "体质-2",
"rare": "0",
"grade": "0",
"effect": {
"STR": "-2"
}
@@ -219,7 +219,7 @@
"id": "1032",
"name": "早产儿",
"description": "所有属性-1",
"rare": "0",
"grade": "0",
"effect": {
"SPR": "-1"
},
@@ -232,163 +232,308 @@
"id": "1033",
"name": "十死无生",
"description": "体质-10",
"rare": "0"
"grade": "0"
},
"1034": {
"id": "1034",
"name": "家运不顺",
"description": "家境-2",
"rare": "0"
"grade": "0"
},
"1035": {
"id": "1035",
"name": "头着地",
"description": "智力-2",
"rare": "0"
"grade": "0"
},
"1036": {
"id": "1036",
"name": "胎教",
"description": "智力+1",
"rare": "0"
"grade": "0"
},
"1037": {
"id": "1037",
"name": "班中红人",
"description": "和同学容易处好关系",
"rare": "0"
"grade": "0"
},
"1038": {
"id": "1038",
"name": "骑士",
"description": "能轻松学会骑车",
"rare": "0"
"grade": "0"
},
"1039": {
"id": "1039",
"name": "永远的神",
"description": "电竞天才",
"rare": "1"
"grade": "1"
},
"1040": {
"id": "1040",
"name": "黄帝",
"description": "赌毒不沾",
"rare": "0"
"grade": "0"
},
"1041": {
"id": "1041",
"name": "丁克",
"description": "不生孩子",
"rare": "1"
"grade": "1"
},
"1042": {
"id": "1042",
"name": "少数民族",
"description": "高考+5分",
"rare": "0"
"grade": "0"
},
"1043": {
"id": "1043",
"name": "老司机",
"description": "你的家人不会发生车祸",
"rare": "0"
"grade": "0"
},
"1044": {
"id": "1044",
"name": "低压",
"description": "你的家人不会心脏病",
"rare": "0"
"grade": "0"
},
"1045": {
"id": "1045",
"name": "战功",
"description": "你退伍后会当官",
"rare": "0"
"grade": "0"
},
"1046": {
"id": "1046",
"name": "不孕不育",
"description": "你生不出孩子",
"rare": "1"
"grade": "1"
},
"1047": {
"id": "1047",
"name": "白头偕老",
"description": "爱人至少能活到70岁",
"rare": "1"
"grade": "1"
},
"1048": {
"id": "1048",
"name": "一个小盒子",
"name": "神秘的小盒子",
"description": "100岁时才能开启",
"rare": "3"
"grade": "3"
},
"1049": {
"id": "1049"
"id": "1049",
"name": "三十而立",
"description": "30岁时家境+2",
"condition": "age?[30]",
"grade": "0",
"effect": {
"MNY": "2"
}
},
"1050": {
"id": "1050"
"id": "1050",
"name": "四十不惑",
"description": "40岁时智力+2",
"condition": "age?[40]",
"grade": "0",
"effect": {
"INT": "2"
}
},
"1051": {
"id": "1051"
"id": "1051",
"name": "知天命",
"description": "50岁时智力、快乐+1",
"condition": "age?[50]",
"grade": "0",
"effect": {
"SPR": "1"
},
"INT": "1"
},
"1052": {
"id": "1052"
"id": "1052",
"name": "耳顺",
"description": "60岁时快乐+2",
"condition": "age?[60]",
"grade": "0",
"effect": {
"SPR": "2"
}
},
"1053": {
"id": "1053"
"id": "1053",
"name": "从心所欲",
"description": "70岁时快乐+3",
"condition": "age?[70]",
"grade": "0",
"effect": {
"SPR": "3"
}
},
"1054": {
"id": "1054"
"id": "1054",
"name": "老当益壮",
"description": "60岁时体质+2",
"condition": "age?[60]",
"grade": "1",
"effect": {
"STR": "2"
}
},
"1055": {
"id": "1055"
"id": "1055",
"name": "鹤发童颜",
"description": "70岁时颜值+3",
"condition": "age?[70]",
"grade": "0",
"effect": {
"CHR": "3"
}
},
"1056": {
"id": "1056"
"id": "1056",
"name": "学前启蒙",
"description": "5岁时智力+2",
"condition": "age?[5]",
"grade": "1",
"effect": {
"INT": "2"
}
},
"1057": {
"id": "1057"
"id": "1057",
"name": "十八变",
"description": "18岁时颜值+2",
"condition": "age?[18]",
"grade": "1",
"effect": {
"CHR": "2"
}
},
"1058": {
"id": "1058"
"id": "1058",
"name": "迟来之财",
"description": "90岁时家境+4",
"condition": "age?[90]",
"grade": "0",
"effect": {
"MNY": "4"
}
},
"1059": {
"id": "1059"
"id": "1059",
"name": "理财达人",
"description": "30、40、50岁时家境+1",
"condition": "age?[30,40,50]",
"grade": "0",
"effect": {
"MNY": "1"
}
},
"1060": {
"id": "1060"
"id": "1060",
"name": "成熟",
"description": "12、18岁时智力+1",
"condition": "age?[12,18]",
"grade": "1",
"effect": {
"INT": "1"
}
},
"1061": {
"id": "1061"
"id": "1061",
"name": "形象管理",
"description": "16、24岁时颜值+1",
"condition": "age?[16,24]",
"grade": "1",
"effect": {
"CHR": "1"
}
},
"1062": {
"id": "1062"
"id": "1062",
"name": "成年礼",
"description": "18岁时快乐+1",
"condition": "age?[18]",
"grade": "0",
"effect": {
"SPR": "1"
}
},
"1063": {
"id": "1063"
"id": "1063",
"name": "开光之胎",
"description": "初始可用属性点+1",
"grade": "0",
"status": "1"
},
"1064": {
"id": "1064"
"id": "1064",
"name": "天命",
"description": "初始可用属性点+8",
"grade": "3",
"status": "8"
},
"1065": {
"id": "1065"
"id": "1065",
"name": "祖传药丸",
"description": "功能不明",
"grade": "1"
},
"1066": {
"id": "1066"
"id": "1066",
"name": "贵人相助",
"description": "家境为0时家境+1",
"condition": "MNY<1",
"grade": "0",
"effect": {
"MNY": "1"
}
},
"1067": {
"id": "1067"
"id": "1067",
"name": "乐天派",
"description": "快乐为0时快乐+1",
"condition": "SPR<1",
"grade": "1",
"effect": {
"SPR": "1"
}
},
"1068": {
"id": "1068"
"id": "1068",
"name": "命悬一线",
"description": "体质为0时体质+1",
"condition": "STR<1",
"grade": "0",
"effect": {
"STR": "1"
}
},
"1069": {
"id": "1069"
"id": "1069",
"name": "智可生财",
"description": "若20岁时智力>8家境+2",
"condition": "(age?[20])&(INT>8)",
"grade": "0",
"effect": {
"MNY": "2"
}
},
"1070": {
"id": "1070"
"id": "1070",
"name": "舔狗甚多",
"description": "若20岁时颜值>8快乐+2",
"condition": "(age?[20])&(CHR>8)",
"grade": "0",
"effect": {
"SPR": "2"
}
},
"1071": {
"id": "1071"

Binary file not shown.

362
src/app.js Normal file
View File

@@ -0,0 +1,362 @@
import Life from './life.js'
class App{
constructor(){
this.#life = new Life();
}
#life;
#pages;
#talentSelected = new Set();
#totalMax=20;
#isEnd = false;
async initial() {
this.initPages();
this.switch('loading');
await this.#life.initial();
this.switch('index');
}
initPages() {
// Loading
const loadingPage = $(`
<div id="main">
<div id="title">
人生重开模拟器<br>
<div style="font-size:1.5rem; font-weight:normal;">加载中...</div>
</div>
</div>
`);
// Index
const indexPage = $(`
<div id="main">
<div id="cnt" class="head">已重开1次</div>
<button id="rank">排行榜</button>
<div id="title">
人生重开模拟器<br>
<div style="font-size:1.5rem; font-weight:normal;">这垃圾人生一秒也不想呆了</div>
</div>
<button id="restart" class="mainbtn"><span class="iconfont">&#xe6a7;</span>立即重开</button>
<div class="hint">别卷了!没有排行榜</div>
</div>
`);
indexPage
.find('#restart')
.click(()=>this.switch('talent'));
const talentPage = $(`
<div id="main">
<div class="head" style="font-size: 1.6rem">天赋抽卡</div>
<button id="random" class="mainbtn" style="top: 50%;">10连抽</button>
<ul id="talents" class="selectlist"></ul>
<button id="next" class="mainbtn" style="top:auto; bottom:0.1em">请选择3个</button>
</div>
`);
// Talent
const createTalent = ({ grade, name, description }) => {
const element = $(`<li>${name}${description}</li>`)
switch(grade) {
case 1:
element.addClass('sprcial_blue');
break;
case 2:
element.addClass('sprcial_purple');
break;
case 3:
element.addClass('sprcial_orange');
break;
default: break;
}
return element;
};
talentPage
.find('#random')
.click(()=>{
talentPage.find('#random').hide();
const ul = talentPage.find('#talents');
this.#life.talentRandom()
.forEach(talent=>{
const li = createTalent(talent);
ul.append(li);
li.click(()=>{
if(li.hasClass('selected')) {
li.removeClass('selected')
this.#talentSelected.delete(talent.id);
} else {
if(this.#talentSelected.size==3) {
this.hint('只能选3个天赋');
return;
}
li.addClass('selected');
this.#talentSelected.add(talent.id);
}
});
});
});
talentPage
.find('#next')
.click(()=>{
if(this.#talentSelected.size!=3) {
this.hint('请选择3个天赋');
return;
}
this.#totalMax = 20 + this.#life.getTalentAllocationAddition(Array.from(this.#talentSelected));
this.switch('property');
})
// Property
const propertyPage = $(`
<div id="main">
<div class="head" style="font-size: 1.6rem">
调整初始属性<br>
<div id="total" style="font-size:1rem; font-weight:normal;">可用属性点0</div>
</div>
<ul id="propertyAllocation" class="propinitial"></ul>
<button id="random" class="mainbtn" style="top:auto; bottom:7rem">随机分配</button>
<button id="start" class="mainbtn" style="top:auto; bottom:0.1rem">开始新人生</button>
</div>
`);
const groups = {};
const total = ()=>{
let t = 0;
for(const type in groups)
t += groups[type].get();
return t;
}
const freshTotal = ()=>{
propertyPage.find('#total').text(`可用属性点:${this.#totalMax - total()}`);
}
const getBtnGroups = (name, min, max)=>{
const group = $(`<li>${name}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>`);
const btnSub = $(`<span class="iconfont propbtn">&#xe6a5;</span>`);
const inputBox = $(`<input value="0">`);
const btnAdd = $(`<span class="iconfont propbtn">&#xe6a6;</span>`);
group.append(btnSub);
group.append(inputBox);
group.append(btnAdd);
const limit = v=>{
v = Number(v)||0;
v = Math.round(v);
return v < min ? min : (
v > max ? max : v
)
}
const get = ()=>Number(inputBox.val());
const set = v=>{
inputBox.val(limit(v));
freshTotal();
}
btnAdd.click(()=>{
if(total() == this.#totalMax) {
this.hint('没用可分配的点数了');
return;
}
set(get()+1);
});
btnSub.click(()=>set(get()-1));
return {group, get, set};
}
groups.CHR = getBtnGroups("颜值", 0, 10); // 颜值 charm CHR
groups.INT = getBtnGroups("智力", 0, 10); // 智力 intelligence INT
groups.STR = getBtnGroups("体质", 0, 10); // 体质 strength STR
groups.MNY = getBtnGroups("家境", 0, 10); // 家境 money MNY
const ul = propertyPage.find('#propertyAllocation');
for(const type in groups) {
ul.append(groups[type].group);
}
propertyPage.find('#random')
.click(()=>{
let t = this.#totalMax;
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;
break;
}
t -= sub;
}
groups.CHR.set(arr[0]);
groups.INT.set(arr[1]);
groups.STR.set(arr[2]);
groups.MNY.set(arr[3]);
});
propertyPage.find('#start')
.click(()=>{
this.#life.restart({
CHR: groups.CHR.get(),
INT: groups.INT.get(),
STR: groups.STR.get(),
MNY: groups.MNY.get(),
TLT: Array.from(this.#talentSelected),
});
this.switch('trajectory');
this.#pages.trajectory.born();
});
// Trajectory
const trajectoryPage = $(`
<div id="main">
<ul id="lifeTrajectory" class="lifeTrajectory"></ul>
<button id="summary" class="mainbtn" style="top:auto; bottom:0.1rem">人生总结</button>
</div>
`);
trajectoryPage.find('#lifeTrajectory')
.click(()=>{
if(this.#isEnd) return;
const trajectory = this.#life.next();
const { age, content, isEnd } = trajectory;
const li = $(`<li><span>${age}岁:</span>${
content.map(
({type, description, grade, name, postEvent}) => {
switch(type) {
case 'TLT':
return `天赋${name}发动${description}`;
case 'EVT':
return description + (postEvent?`<br>${postEvent}`:'');
}
}
).join('<br>')
}</li>`);
li.appendTo('#lifeTrajectory');
if(isEnd) {
this.#isEnd = true;
trajectoryPage.find('#summary').show();
}
});
this.#pages = {
loading: {
page: loadingPage,
clear: ()=>{},
},
index: {
page: indexPage,
btnRank: indexPage.find('#rank'),
btnRestart: indexPage.find('#restart'),
hint: indexPage.find('.hint'),
cnt: indexPage.find('.cnt'),
clear: ()=>{
indexPage.find('.hint').hide();
const times = this.times;
const btnRank = indexPage.find('#rank');
const cnt = indexPage.find('.cnt');
if(times < 0) {
btnRank.hide();
cnt.hide();
return;
}
btnRank.show();
cnt.show();
cnt.text(`已重开${times}`);
},
},
talent: {
page: talentPage,
clear: ()=>{
talentPage.find('ul.selectlist').children().remove();
talentPage.find('#random').show();
this.#totalMax = 20;
},
},
property: {
page: propertyPage,
clear: ()=>{
freshTotal();
},
},
trajectory: {
page: trajectoryPage,
clear: ()=>{
trajectoryPage.find('#lifeTrajectory').children().remove();
trajectoryPage.find('#summary').hide();
this.#isEnd = false;
},
born: ()=>{
trajectoryPage.find('#lifeTrajectory').trigger("click");
}
},
}
}
switch(page) {
const p = this.#pages[page];
if(!p) return;
$('#main').remove();
p.clear();
p.page.appendTo('body');
}
hint(str) {
alert(str);
}
test() {
this.#life.restart({
CHR: 5, // 颜值 charm CHR
INT: 5, // 智力 intelligence INT
STR: 5, // 体质 strength STR
MNY: 5, // 家境 money MNY
SPR: 5, // 快乐 spirit SPR
TLT: [1004, 1005, 1009], // 天赋 talent TLT
});
const lifeTrajectory = [];
let trajectory;
do{
try{
trajectory = this.#life.next();
} catch(e) {
console.error(e);
// debugger
throw e;
}
lifeTrajectory.push(lifeTrajectory);
const { age, content } = trajectory;
console.debug(
`---------------------------------`,
`\n-- ${age}\n `,
content.map(
({type, description, grade, name, postEvent}) => {
switch(type) {
case 'TLT':
return `天赋【${name}】发动:${description}`;
case 'EVT':
return description + (postEvent?`\n ${postEvent}`:'');
}
}
).join('\n ')
);
} while(!trajectory.isEnd)
return lifeTrajectory;
}
main() {
}
get times() {return localStorage.times || 0;}
set times(v) {localStorage.times = parseInt(v) || 0};
}
export default App;

View File

@@ -15,9 +15,9 @@ class Life {
#triggerTalents;
async initial() {
const age = JSON.parse(await json('data/age.json'));
const talents = JSON.parse(await json('data/talents.json'));
const events = JSON.parse(await json('data/events.json'));
const age = await json('age');
const talents = await json('talents');
const events = await json('events');
this.#property.initial({age});
this.#talent.initial({talents});
@@ -30,6 +30,10 @@ class Life {
this.doTalent();
}
getTalentAllocationAddition(talents) {
return this.#talent.allocationAddition(talents);
}
next() {
const {age, event, talent} = this.#property.ageNext();
@@ -56,7 +60,7 @@ class Life {
contents.push({
type: this.#property.TYPES.TLT,
name,
rate,
grade,
desctiption,
})
if(!result.effect) continue;
@@ -91,6 +95,12 @@ class Life {
return eventId;
return events[events.length-1];
}
talentRandom() {
return this.#talent.talentRandom();
}
}
export default Life;

View File

@@ -27,6 +27,7 @@ class Property {
if(value.length==1) value.push(1);
return value;
});
age[a].talent = age[a].talent?.split(',').map(v=>Number(v));
}
}
@@ -114,7 +115,7 @@ class Property {
}
isEnd() {
return !this.get(this.TYPES.LIF);
return this.get(this.TYPES.LIF) < 1;
}
ageNext() {

View File

@@ -6,6 +6,11 @@ class Talent {
initial({talents}) {
this.#talents = talents;
for(const id in talents) {
const talent = talents[id];
talent.id= Number(id);
talent.grade = Number(talent.grade);
}
}
check(talentId) {
@@ -20,16 +25,53 @@ class Talent {
}
information(talentId) {
const { rate, name, description } = this.get(talentId)
return { rate, name, description };
const { grade, name, description } = this.get(talentId)
return { grade, name, description };
}
talentRandom() {
// 1000, 100, 10, 1
const talentList = {};
for(const talentId in this.#talents) {
const { id, grade, name, description } = this.#talents[talentId];
if(!talentList[grade]) talentList[grade] = [{ grade, name, description, id }];
else talentList[grade].push({ grade, name, description, id });
}
return new Array(10)
.fill(1).map(()=>{
const gradeRandom = Math.random();
let grade;
if(gradeRandom>=0.111) grade = 0;
else if(gradeRandom>=0.011) grade = 1;
else if(gradeRandom>=0.001) grade = 2;
else grade = 3;
while(talentList[grade].length == 0) grade--;
const length = talentList[grade].length;
const random = Math.floor(Math.random()*length) % length;
return talentList[grade].splice(random,1)[0];
});
}
allocationAddition(talents) {
if(Array.isArray(talents)) {
let addition = 0;
for(const talent of talents)
addition += this.allocationAddition(talent);
return addition;
}
return this.get(talents).status || 0;
}
do(talentId) {
const { effect, condition, initiative, rate, name, description } = this.get(talentId);
const { effect, condition, initiative, grade, name, description } = this.get(talentId);
if(!initiative) return null;
if(condition && !checkCondition(condition))
return null;
return { effect, rate, name, description };
return { effect, grade, name, description };
}
}

View File

@@ -1,7 +1,7 @@
import { readFile } from 'fs/promises';
import Life from '../src/life.js'
global.json = filePath => readFile(filePath);
global.json = async fileName => JSON.parse(await readFile(`data/${fileName}.json`));
async function debug() {
@@ -28,9 +28,9 @@ async function debug() {
}
lifeTrajectory.push(lifeTrajectory);
const { age, content } = trajectory;
console.debug(`---------------------------------`);
console.debug(`-- ${age}`);
console.debug(' ',
console.debug(
`---------------------------------`,
`\n-- ${age}\n `,
content.map(
({type, description, rate, name, postEvent}) => {
switch(type) {

180
view/condition_test.html Normal file
View File

@@ -0,0 +1,180 @@
颜值: <input class="prop" name="CHR" style="width:300px;" value="5" >(CHR)<br/>
智力: <input class="prop" name="INT" style="width:300px;" value="5" >(INT)<br/>
体质: <input class="prop" name="STR" style="width:300px;" value="5" >(STR)<br/>
家境: <input class="prop" name="MNY" style="width:300px;" value="5" >(MNY)<br/>
快乐: <input class="prop" name="SPR" style="width:300px;" value="5" >(SPR)<br/>
生命: <input class="prop" name="LIF" style="width:300px;" value="5" >(LIF)<br/>
天赋: <input class="prop" name="TLT" style="width:300px;" value="[5]" >(TLT)<br/>
事件: <input class="prop" name="EVT" style="width:300px;" value="[5]" >(EVT)<br/>
<br/><br/>
表达式:<input id="conditions" style="width:500px;"><br/><br/>
结果:<span id="result" style="color:red;"></span><br/><br/>
<button id="submit">&nbsp;&nbsp;&nbsp;&nbsp;</button><br/>
<script>
const DEFAULT_PROP = {
CHR: 5, // 颜值 charm CHR
INT: 5, // 智力 intelligence INT
STR: 5, // 体质 strength STR
MNY: 5, // 家境 money MNY
SPR: 5, // 快乐 spirit SPR
LIF: 5, // 生命 life LIF
TLT: [5], // 天赋 talent TLT
EVT: [5], // 事件 event EVT
};
document.querySelector("#submit").onclick = function() {
document.querySelectorAll('.prop').forEach(({name, value})=>DEFAULT_PROP[name] = JSON.parse(value));
conditions = document.querySelector("#conditions").value;
const result = check(conditions);
document.querySelector("#result").textContent = result;
}
function getProp(prop) {
switch(prop) {
case 'CHR':
case 'INT':
case 'STR':
case 'MNY':
case 'SPR':
case 'LIF':
case 'TLT':
case 'EVT': return DEFAULT_PROP[prop];
default: return null;
}
}
function check(condition) {
const conditions = parseCondition(condition);
return checkParsedCondition(conditions);
}
function checkParsedCondition(conditions) {
if(!Array.isArray(conditions)) return checkLogic(conditions);
if(conditions.length == 0) return true;
if(conditions.length == 1) return checkParsedCondition(conditions[0]);
let ret = checkParsedCondition(conditions[0]);
for(let i=1; i<conditions.length; i+=2) {
switch(conditions[i]) {
case '&':
if(ret) ret = checkParsedCondition(conditions[i+1]);
break;
case '|':
if(ret) return true;
ret = checkParsedCondition(conditions[i+1]);
break;
default: return false;
}
}
return ret;
}
function checkLogic(condition) {
const length = condition.length;
let i = condition.search(/[><\!\?=]/);
const prop = condition.substring(0,i);
const symbol = condition.substring(i, i+=(condition[i+1]=='='?2:1));
const d = condition.substring(i, length);
const propData = getProp(prop);
const conditionData = d[0]=='['? JSON.parse(d): Number(d);
switch(symbol) {
case '>': return propData > conditionData;
case '<': return propData < conditionData;
case '>=': return propData >= conditionData;
case '<=': return propData <= conditionData;
case '=':
if(Array.isArray(propData))
return propData.includes(conditionData);
return propData == conditionData;
case '!=':
if(Array.isArray(propData))
return !propData.includes(conditionData);
return propData == conditionData;
case '?':
if(Array.isArray(propData)) {
for(const p of propData)
if(conditionData.includes(p)) return true;
return false;
}
return conditionData.includes(propData);
case '!':
if(Array.isArray(propData)) {
for(const p of propData)
if(conditionData.includes(p)) return false;
return true;
}
return !conditionData.includes(propData);
default: return false;
}
}
function parseCondition(condition) {
const conditions = [];
const length = condition.length;
const stack = [];
stack.unshift(conditions);
let cursor = 0;
const catchString = i => {
const str = condition.substring(cursor, i).trim();
cursor = i;
if(str) stack[0].push(str);
};
for(let i=0; i<length; i++) {
switch(condition[i]) {
case ' ': continue;
case '(':
catchString(i);
cursor ++;
const sub = [];
stack[0].push(sub);
stack.unshift(sub);
break;
case ')':
catchString(i);
cursor ++;
stack.shift();
break;
case '|':
case '&':
catchString(i);
catchString(i+1);
break;
default: continue;
}
}
catchString(length);
return conditions;
}
// function debug(...conditions) {
// for(const condition of conditions)
// console.debug(check(condition), '\t', condition);
// }
//
// debug(
// '(STR<2&MNY>3)|(MNY<2&CHR<2)',
// '(STR<2&MNY>3)',
// '(STR>2&MNY>3)',
// '((((STR>2&MNY>2))))',
// '((((STR>2&MNY>2)|(MNY<2&CHR<2))))',
// '((((STR>2&MNY>2)|(MNY<2&CHR<2)&(STR>2&MNY>3))))',
// '((((STR>2&MNY>2)|(MNY<2&CHR<2))&(STR>2&MNY>3)))',
// 'EVT![1,2,3]',
// 'EVT![1,2]',
// 'EVT?[1,2,3]',
// 'EVT?[1,2]',
// );
</script>

BIN
view/iconfont.ttf Normal file

Binary file not shown.

BIN
view/iconfont.woff Normal file

Binary file not shown.

BIN
view/iconfont.woff2 Normal file

Binary file not shown.

21
view/index.html Normal file
View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>window.json = async fileName=>(await axios(`../data/${fileName}.json`)).data;</script>
<title>Document</title>
</head>
<body style="margin: 0; height: 100%"></body>
<script type="module">
import App from '../src/app.js';
const app = new App();
app
.initial()
.then(()=>app.main());
</script>
</html>

238
view/style.css Normal file
View File

@@ -0,0 +1,238 @@
@media (min-width:640px){html{font-size:24px;}}
@media (min-width:631px) and (max-width:639px){html{font-size:23.66px;}}
@media (min-width:622px) and (max-width:630px){html{font-size:23.33px;}}
@media (min-width:613px) and (max-width:621px){html{font-size:23px;}}
@media (min-width:604px) and (max-width:612px){html{font-size:22.66px;}}
@media (min-width:595px) and (max-width:603px){html{font-size:22.33px;}}
@media (min-width:586px) and (max-width:594px){html{font-size:22px;}}
@media (min-width:577px) and (max-width:585px){html{font-size:21.66px;}}
@media (min-width:568px) and (max-width:576px){html{font-size:21.33px;}}
@media (min-width:559px) and (max-width:567px){html{font-size:21px;}}
@media (min-width:550px) and (max-width:558px){html{font-size:20.66px;}}
@media (min-width:541px) and (max-width:549px){html{font-size:20.33px;}}
@media (min-width:533px) and (max-width:540px){html{font-size:20px;}}
@media (min-width:524px) and (max-width:532px){html{font-size:19.66px;}}
@media (min-width:515px) and (max-width:523px){html{font-size:19.33px;}}
@media (min-width:506px) and (max-width:514px){html{font-size:19px;}}
@media (min-width:497px) and (max-width:505px){html{font-size:18.66px;}}
@media (min-width:488px) and (max-width:496px){html{font-size:18.33px;}}
@media (min-width:480px) and (max-width:487px){html{font-size:18px;}}
@media (min-width:471px) and (max-width:479px){html{font-size:17.66px;}}
@media (min-width:462px) and (max-width:470px){html{font-size:17.33px;}}
@media (min-width:453px) and (max-width:461px){html{font-size:17px;}}
@media (min-width:444px) and (max-width:452px){html{font-size:17.12px;}}
@media (min-width:435px) and (max-width:443px){html{font-size:16.33px;}}
@media (min-width:426px) and (max-width:434px){html{font-size:16px;}}
@media (min-width:417px) and (max-width:425px){html{font-size:15.66px;}}
@media (min-width:408px) and (max-width:416px){html{font-size:15.33px;}}
@media (min-width:400px) and (max-width:407px){html{font-size:15px;}}
@media (min-width:391px) and (max-width:399px){html{font-size:14.66px;}}
@media (min-width:382px) and (max-width:390px){html{font-size:14.33px;}}
@media (min-width:374px) and (max-width:381px){html{font-size:14px;}}
@media (min-width:365px) and (max-width:373px){html{font-size:13.66px;}}
@media (min-width:356px) and (max-width:364px){html{font-size:13.33px;}}
@media (min-width:347px) and (max-width:355px){html{font-size:13px;}}
@media (min-width:338px) and (max-width:346px){html{font-size:12.66px;}}
@media (min-width:329px) and (max-width:337px){html{font-size:12.44px;}}
@media (max-width:328px){html{font-size:12px;}}
@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1628944689555') format('woff2'),
url('iconfont.woff?t=1628944689555') format('woff'),
url('iconfont.ttf?t=1628944689555') format('truetype');
}
html {
font-family: PingFangSC, 'Noto Sans CJK SC', 'MS Yahei';
}
#main {
align-content: center;
width: 100%;
height: 100%;
position: relative;
}
#title {
position: fixed;
font-size: 3rem;
font-weight: 700;
top: 35%;
left: 50%;
white-space: nowrap;
transform: translate(-50%,-50%);
text-align: center;
}
.mainbtn {
position: fixed;
top: 65%;
left: 50%;
padding: 0.8rem 1rem;
border: 1px #ccc solid;
border-radius: 0.2rem;
background-color:white;
font-size: 1.6rem;
white-space: nowrap;
transform: translate(-50%,-50%);
cursor: pointer;
z-index:2;
}
.iconfont {
font-family: "iconfont" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#rank {
position: fixed;
top: 1rem;
right: 1rem;
padding: 0.1rem 1rem;
border: none;
border-radius: 0.2rem;
background-color:lightsteelblue;
font-size: 1.4rem;
color: white;
cursor: pointer;
z-index:2;
}
.hint {
position: fixed;
bottom: 2rem;
left: 50%;
padding: 0.3rem 2rem;
border: none;
border-radius: 0.2rem;
background-color:gray;
font-size: 1.4rem;
color: white;
white-space: nowrap;
transform: translateX(-50%);
}
.head {
position: fixed;
font-size: 1.4rem;
top: 1.1rem;
left: 50%;
white-space: nowrap;
transform: translateX(-50%);
text-align: center;
}
.lifeTrajectory,
.propinitial,
.selectlist {
position: fixed;
list-style-type: none;
left: 50%;
top: 5rem;
bottom: 8.5rem;
width: 30rem;
max-width: calc(100% - 2rem);
margin: auto;
padding: 0;
overflow: auto;
transform: translateX(-50%);
}
.selectlist > li {
position: relative;
border: 1px #ccc solid;
display: inline-block;
width: 95%;
margin: 0.1rem auto;
font-size: 1.4rem;
text-align: center;
border-radius: 0.2rem;
cursor: pointer;
}
.selectlist > li::before {
position: absolute;
display: inline-block;
left: 0;
top: 0;
border-radius: 0.2rem 0 0 0.2rem;
margin: -1px;
padding: 1px;
height: 100%;
width: 1.5rem;
content: " ";
}
.sprcial_blue::before {
background-color: rgb(116, 191, 255);
}
.sprcial_purple::before {
background-color: rgb(226, 167, 255);
}
.sprcial_orange::before {
background-color: lightsalmon;
}
.selected {
background-color: gray;
color: white;
}
.propinitial {
top: 6rem;
bottom: 14rem;
}
.propinitial > li {
position: relative;
display: inline-block;
width: 95%;
margin: 0.1rem auto;
font-size: 1.4rem;
text-align: center;
border-radius: 0.2rem;
padding: 0.2rem;
}
.propinitial > li > input {
height: 2.2rem;
width: 2.2rem;
margin: 0 0.5rem;
padding: 0;
text-align: center;
font-size: 2rem;
border: 0.1rem #ccc solid;
}
.propbtn {
position: relative;
cursor: pointer;
font-size: 2rem;
}
.lifeTrajectory {
border: 1px lightblue solid;
background-color: aliceblue;
}
.lifeTrajectory > li {
position: relative;
width: calc(100% - 7rem);
margin: 0.5rem 0;
padding: 0.5rem 1rem 0.5rem 6rem;
font-size: 1.4rem;
background-color: white;
box-shadow: lightblue 0 0 0.4rem;
}
.lifeTrajectory > li > span {
position: absolute;
left: 0;
width: 6rem;
text-align: right;
}

31
view/test.html Normal file
View File

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>window.json = async fileName=>(await axios(`../data/${fileName}.json`)).data;</script>
<title>Document</title>
</head>
<body style="margin: 0; height: 100%">
<div id="main">
<ul id="lifeTrajectory" class="lifeTrajectory">
<li><span>0岁</span>你出生了,你是女孩</li>
<li><span>0岁</span>你出生了你出生了你出生了你出生了你出生了你出生了你出生了你出生了你出生了你出生了你出生了你出生了你出生了</li>
<li><span>0岁</span>你出生了,你是女孩</li>
<li><span>0岁</span>你出生了,你是女孩</li>
<li><span>0岁</span>你出生了,你是女孩</li>
<li><span>0岁</span>你出生了,你是女孩</li>
<li><span>0岁</span>你出生了,你是女孩</li>
<li><span>0岁</span>你出生了,你是女孩</li>
<li><span>0岁</span>你出生了,你是女孩</li>
<li><span>0岁</span>你出生了,你是女孩</li>
<li><span>0岁</span>你出生了,你是女孩</li>
</ul>
<button id="summary" class="mainbtn" style="top:auto; bottom:0.1rem">人生总结</button>
</div>
</body>
</html>