增加微信小程序版

This commit is contained in:
uiiang
2021-09-08 16:04:03 +08:00
parent e420fdf8fd
commit 1c29c3a737
207 changed files with 5936 additions and 0 deletions

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,96 @@
import talentsData from './talents.js';
import ageData from './age.js';
import eventsData from './events.js';
import { max, sum } from '../functions/util.js';
import { summary } from "../functions/summary.js";
//"\d*": ->
//"age": "(\d*)", -> "_id": "$1", "age": "$1",
//"\d*": \{ -> {
//"id": -> "_id":
function allTalents() {
// wx.setStorage({
// key: 'talentsData',
// data: talentsData
// })
return talentsData.slice(0)
}
function allAge() {
// wx.setStorage({
// key: 'agedata',
// data: ageData
// })
return ageData.slice(0)
}
function allEvents() {
// wx.setStorage({
// key: 'eventsData',
// data: eventsData
// })
return eventsData.slice(0)
}
function randomTalents(max) {
const result = getRandomInRange(talentsData, max)
return result
}
function getRandomInRange(arr, count) {
var shuffled = arr.slice(0), i = arr.length, min = i - count, temp, index;
while (i-- > min) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled.slice(min).sort(function(a,b){return a-b;});
}
function buildSummary(records, type) {
const value = max(records.map(({[type]:v})=>v));
const { judge, grade } = summary(type, value);
return { judge, grade, value };
}
function finalSummary(records) {
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 { judge, grade, value };
}
function computeTalentsStatus(talents) {
var status = talents.map(function(item) {
if ('status' in item) {
return item.status
} else {
return 0
}
})
return status
}
function computeUseableProp(max, status) {
var proNum = max
status.forEach(function(item){
proNum = proNum + item
})
return proNum
}
function randomProp(max, init){
// console.log('randomProperty', t)
var arr = init
while(max>0) {
const sub = Math.round(Math.random() * (Math.min(max, 10) - 1)) + 1;
while(true) {
const select = Math.floor(Math.random() * 4) % 4;
if(arr[select] - sub <0) continue;
arr[select] -= sub;
max -= sub;
break;
}
}
return arr
}
export { randomTalents, getRandomInRange, allTalents, allAge, allEvents, buildSummary, finalSummary, computeTalentsStatus, computeUseableProp, randomProp };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,61 @@
import { clone } from './functions/util.js';
import { checkCondition } from './functions/condition.js';
class Event {
constructor() {}
#events;
// 初始化传参数去掉{}
initial(events) {
this.#events = events;
for(const id in events) {
const event = events[id];
if(!event.branch || !'branch' in event) continue;
//判断事件是否被处理过
event.branch = event.branch.map(b=>{
b = b.indexOf(":") != -1?b.split(':'):b;
b[1] = Number(b[1]);
return b;
});
}
}
check(eventId, property) {
const { include, exclude, NoRandom } = this.get(eventId);
if(NoRandom) return false;
if(exclude && checkCondition(property, exclude)) return false;
if(include) return checkCondition(property, include);
return true;
}
get(eventId) {
// const event = this.#events[eventId];
// console.log('event.js get',eventId, this.#events)
var event
this.#events.forEach(function(item){
if (item._id == eventId) {
event = item
}
})
if(!event) throw new Error(`[ERROR] No Event[${eventId}]`);
return clone(event);
}
information(eventId) {
const { event: description } = this.get(eventId)
return { description };
}
do(eventId, property) {
const { effect, branch, event: description, postEvent } = this.get(eventId);
if(branch)
for(const [cond, next] of branch)
if(checkCondition(property, cond))
return { effect, next, description };
return { effect, postEvent, description };
}
}
export default Event;

View File

@@ -0,0 +1,129 @@
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 checkCondition(property, condition) {
const conditions = parseCondition(condition);
return checkParsedConditions(property, conditions);
}
function checkParsedConditions(property, conditions) {
if(!Array.isArray(conditions)) return checkProp(property, conditions);
if(conditions.length == 0) return true;
if(conditions.length == 1) return checkParsedConditions(property, conditions[0]);
let ret = checkParsedConditions(property, conditions[0]);
for(let i=1; i<conditions.length; i+=2) {
switch(conditions[i]) {
case '&':
if(ret) ret = checkParsedConditions(property, conditions[i+1]);
break;
case '|':
if(ret) return true;
ret = checkParsedConditions(property, conditions[i+1]);
break;
default: return false;
}
}
return ret;
}
function checkProp(property, 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 = property.get(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 extractMaxTriggers(condition) {
// Assuming only age related talents can be triggered multiple times.
const RE_AGE_CONDITION = /AGE\?\[([0-9\,]+)\]/;
const match_object = RE_AGE_CONDITION.exec(condition);
if (match_object == null) {
// Not age related, single trigger.
return 1;
}
const age_list = match_object[1].split(",");
return age_list.length;
}
export { checkCondition, extractMaxTriggers };

View File

@@ -0,0 +1,89 @@
const data = {
"CHR": [
{"judge": "地狱", "grade": 0},
{"min":1, "judge": "折磨", "grade": 0},
{"min":2, "judge": "不佳", "grade": 0},
{"min":4, "judge": "普通", "grade": 0},
{"min":7, "judge": "优秀", "grade": 1},
{"min":9, "judge": "罕见", "grade": 2},
{"min":11, "judge": "逆天", "grade": 3},
],
"MNY": [
{"judge": "地狱", "grade": 0},
{"min":1, "judge": "折磨", "grade": 0},
{"min":2, "judge": "不佳", "grade": 0},
{"min":4, "judge": "普通", "grade": 0},
{"min":7, "judge": "优秀", "grade": 1},
{"min":9, "judge": "罕见", "grade": 2},
{"min":11, "judge": "逆天", "grade": 3},
],
"SPR": [
{"judge": "地狱", "grade": 0},
{"min":1, "judge": "折磨", "grade": 0},
{"min":2, "judge": "不幸", "grade": 0},
{"min":4, "judge": "普通", "grade": 0},
{"min":7, "judge": "幸福", "grade": 1},
{"min":9, "judge": "极乐", "grade": 2},
{"min":11, "judge": "天命", "grade": 3},
],
"INT": [
{"judge": "地狱", "grade": 0},
{"min":1, "judge": "折磨", "grade": 0},
{"min":2, "judge": "不佳", "grade": 0},
{"min":4, "judge": "普通", "grade": 0},
{"min":7, "judge": "优秀", "grade": 1},
{"min":9, "judge": "罕见", "grade": 2},
{"min":11, "judge": "逆天", "grade": 3},
{"min":21, "judge": "识海", "grade": 3},
{"min":131, "judge": "元神", "grade": 3},
{"min":501, "judge": "仙魂", "grade": 3},
],
"STR": [
{"judge": "地狱", "grade": 0},
{"min":1, "judge": "折磨", "grade": 0},
{"min":2, "judge": "不佳", "grade": 0},
{"min":4, "judge": "普通", "grade": 0},
{"min":7, "judge": "优秀", "grade": 1},
{"min":9, "judge": "罕见", "grade": 2},
{"min":11, "judge": "逆天", "grade": 3},
{"min":21, "judge": "凝气", "grade": 3},
{"min":101, "judge": "筑基", "grade": 3},
{"min":401, "judge": "金丹", "grade": 3},
{"min":1001, "judge": "元婴", "grade": 3},
{"min":2001, "judge": "仙体", "grade": 3},
],
"AGE": [
{"judge": "胎死腹中", "grade": 0},
{"min":1, "judge": "早夭", "grade": 0},
{"min":10, "judge": "少年", "grade": 0},
{"min":18, "judge": "盛年", "grade": 0},
{"min":40, "judge": "中年", "grade": 0},
{"min":60, "judge": "花甲", "grade": 1},
{"min":70, "judge": "古稀", "grade": 1},
{"min":80, "judge": "杖朝", "grade": 2},
{"min":90, "judge": "南山", "grade": 2},
{"min":95, "judge": "不老", "grade": 3},
{"min":100, "judge": "修仙", "grade": 3},
{"min":500, "judge": "仙寿", "grade": 3},
],
"SUM": [
{"judge": "地狱", "grade": 0},
{"min":41, "judge": "折磨", "grade": 0},
{"min":50, "judge": "不佳", "grade": 0},
{"min":60, "judge": "普通", "grade": 0},
{"min":80, "judge": "优秀", "grade": 1},
{"min":100, "judge": "罕见", "grade": 2},
{"min":110, "judge": "逆天", "grade": 3},
{"min":120, "judge": "传说", "grade": 3},
]
}
function summary(type, value) {
let length = data[type].length;
while(length--) {
const {min, judge, grade} = data[type][length];
if(min==void 0 || value >= min) return {judge, grade};
}
}
export { summary };

View File

@@ -0,0 +1,31 @@
function clone(value) {
switch(typeof value) {
case 'object':
if(Array.isArray(value)) return value.map(v=>clone(v));
const newObj = {};
for(const key in value) newObj[key] = clone(value[key]);
return newObj;
default: return value;
}
}
function max(...arr) {
return Math.max(...arr.flat());
}
function min(...arr) {
return Math.min(...arr.flat());
}
function sum(...arr) {
let s = 0;
arr.flat().forEach(v=>s+=v);
return s;
}
function average(...arr) {
const s = sum(...arr);
return s / arr.flat().length;
}
export { clone, max, min, sum, average };

View File

@@ -0,0 +1,135 @@
import Property from './property.js';
import Event from './event.js';
import Talent from './talent.js';
import {allAge, allEvents, allTalents} from './data/dataUtils.js';
class Life {
constructor() {
this.#property = new Property();
this.#event = new Event();
this.#talent = new Talent();
}
#property;
#event;
#talent;
#triggerTalents;
async initial() {
// console.log('life.js initial', property, event, talent)
// 加载数据方式改为从本地读取JS
var _this = this
_this.#property.initial(allAge());
_this.#talent.initial(allTalents());
_this.#event.initial(allEvents());
// console.log('life.js initial 2 p', _this.#property,'e', _this.#event, 'e',_this.#talent)
// var age = []
// var talents = []
// var events = []
// const age = await json('age');
// const talents = await json('talents');
// const events = await json('events');
}
restart(allocation) {
this.#triggerTalents = {};
this.#property.restart(allocation);
this.doTalent();
this.#property.record();
}
getTalentAllocationAddition(talents) {
return this.#talent.allocationAddition(talents);
}
getTalentCurrentTriggerCount(talentId) {
return this.#triggerTalents[talentId] || 0;
}
next() {
const {age, event, talent} = this.#property.ageNext();
const talentContent = this.doTalent(talent);
const eventContent = this.doEvent(this.random(event));
this.#property.record();
const isEnd = this.#property.isEnd();
const content = [talentContent, eventContent].flat();
return { age, content, isEnd };
}
doTalent(talents) {
if(talents) this.#property.change(this.#property.TYPES.TLT, talents);
talents = this.#property.get(this.#property.TYPES.TLT)
.filter(talentId => this.getTalentCurrentTriggerCount(talentId) < this.#talent.get(talentId).max_triggers);
const contents = [];
for(const talentId of talents) {
const result = this.#talent.do(talentId, this.#property);
if(!result) continue;
this.#triggerTalents[talentId] = this.getTalentCurrentTriggerCount(talentId) + 1;
const { effect, name, description, grade } = result;
contents.push({
type: this.#property.TYPES.TLT,
name,
grade,
description,
})
if(!effect) continue;
this.#property.effect(effect);
}
return contents;
}
doEvent(eventId) {
const { effect, next, description, postEvent } = this.#event.do(eventId, this.#property);
this.#property.change(this.#property.TYPES.EVT, eventId);
this.#property.effect(effect);
const content = {
type: this.#property.TYPES.EVT,
description,
postEvent,
}
if(next) return [content, this.doEvent(next)].flat();
return [content];
}
random(events) {
events = events.filter(([eventId])=>this.#event.check(eventId, this.#property));
let totalWeights = 0;
for(const [, weight] of events)
totalWeights += weight;
let random = Math.random() * totalWeights;
for(const [eventId, weight] of events)
if((random-=weight)<0)
return eventId;
return events[events.length-1];
}
talentRandom() {
return this.#talent.talentRandom(JSON.parse(localStorage.extendTalent||'null'));
}
talentExtend(talentId) {
localStorage.extendTalent = JSON.stringify(talentId);
}
getRecord() {
return this.#property.getRecord();
}
getLastRecord() {
return this.#property.getLastRecord();
}
exclusive(talents, exclusive) {
return this.#talent.exclusive(talents, exclusive);
}
}
export default Life;

View File

@@ -0,0 +1,166 @@
import { clone } from './functions/util.js';
class Property {
constructor() {}
TYPES = {
AGE: "AGE",
CHR: "CHR",
INT: "INT",
STR: "STR",
MNY: "MNY",
SPR: "SPR",
LIF: "LIF",
TLT: "TLT",
EVT: "EVT",
};
#ageData;
#data;
#record;
// 初始化传参数去掉{}
initial(age) {
this.#ageData = age;
for(const a in age) {
let { event, talent } = age[a];
if(!Array.isArray(event))
event = event?.split(',') || [];
event = event.map(v=>{
const value = `${v}`.split('*').map(n=>Number(n));
if(value.length==1) value.push(1);
return value;
});
if(!Array.isArray(talent))
talent = talent?.split(',') || [];
talent = talent.map(v=>Number(v));
age[a] = { event, talent };
}
}
restart(data) {
this.#data = {
[this.TYPES.AGE]: -1,
[this.TYPES.CHR]: 0,
[this.TYPES.INT]: 0,
[this.TYPES.STR]: 0,
[this.TYPES.MNY]: 0,
[this.TYPES.SPR]: 0,
[this.TYPES.LIF]: 1,
[this.TYPES.TLT]: [],
[this.TYPES.EVT]: [],
};
for(const key in data)
this.change(key, data[key]);
this.#record = [];
}
get(prop) {
switch(prop) {
case this.TYPES.AGE:
case this.TYPES.CHR:
case this.TYPES.INT:
case this.TYPES.STR:
case this.TYPES.MNY:
case this.TYPES.SPR:
case this.TYPES.LIF:
case this.TYPES.TLT:
case this.TYPES.EVT:
return clone(this.#data[prop]);
default: return 0;
}
}
set(prop, value) {
switch(prop) {
case this.TYPES.AGE:
case this.TYPES.CHR:
case this.TYPES.INT:
case this.TYPES.STR:
case this.TYPES.MNY:
case this.TYPES.SPR:
case this.TYPES.LIF:
case this.TYPES.TLT:
case this.TYPES.EVT:
this.#data[prop] = clone(value);
break;
default: return 0;
}
}
record() {
this.#record.push({
[this.TYPES.AGE]: this.get(this.TYPES.AGE),
[this.TYPES.CHR]: this.get(this.TYPES.CHR),
[this.TYPES.INT]: this.get(this.TYPES.INT),
[this.TYPES.STR]: this.get(this.TYPES.STR),
[this.TYPES.MNY]: this.get(this.TYPES.MNY),
[this.TYPES.SPR]: this.get(this.TYPES.SPR),
});
}
getRecord() {
return clone(this.#record);
}
getLastRecord() {
return clone(this.#record[this.#record.length - 1]);
}
change(prop, value) {
if(Array.isArray(value)) {
for(const v of value)
this.change(prop, Number(v));
return;
}
switch(prop) {
case this.TYPES.AGE:
case this.TYPES.CHR:
case this.TYPES.INT:
case this.TYPES.STR:
case this.TYPES.MNY:
case this.TYPES.SPR:
case this.TYPES.LIF:
this.#data[prop] += Number(value);
break;
case this.TYPES.TLT:
case this.TYPES.EVT:
const v = this.#data[prop];
if(value<0) {
const index = v.indexOf(value);
if(index!=-1) v.splice(index,1);
}
if(!v.includes(value)) v.push(value);
break;
default: return;
}
}
effect(effects) {
for(const prop in effects)
this.change(prop, Number(effects[prop]));
}
isEnd() {
return this.get(this.TYPES.LIF) < 1;
}
ageNext() {
this.change(this.TYPES.AGE, 1);
const age = this.get(this.TYPES.AGE);
const {event, talent} = this.getAgeData(age);
return {age, event, talent};
}
getAgeData(age) {
return clone(this.#ageData[age]);
}
}
export default Property;

View File

@@ -0,0 +1,103 @@
import { clone } from './functions/util.js';
import { checkCondition, extractMaxTriggers } from './functions/condition.js';
class Talent {
constructor() {}
#talents;
// 初始化传参数去掉{}
initial(talents) {
this.#talents = talents;
for(const id in talents) {
const talent = talents[id];
talent.id= Number(id);
talent.grade = Number(talent.grade);
talent.max_triggers = extractMaxTriggers(talent.condition);
}
}
check(talentId, property) {
const { condition } = this.get(talentId);
return checkCondition(property, condition);
}
get(talentId) {
// console.log('talent.js get',talentId,this.#talents)
var talent
this.#talents.forEach(function(item){
if (item._id == talentId) {
talent = item
}
})
if(!talent) throw new Error(`[ERROR] No Talent[${talentId}]`);
return clone(talent);
}
information(talentId) {
const { grade, name, description } = this.get(talentId)
return { grade, name, description };
}
exclusive(talends, exclusiveId) {
const { exclusive } = this.get(exclusiveId);
if(!exclusive) return null;
for(const talent of talends) {
for(const e of exclusive) {
if(talent == e) return talent;
}
}
return null;
}
talentRandom(include) {
// 1000, 100, 10, 1
const talentList = {};
for(const talentId in this.#talents) {
const { id, grade, name, description } = this.#talents[talentId];
if(id == include) {
include = { grade, name, description, id };
continue;
}
if(!talentList[grade]) talentList[grade] = [{ grade, name, description, id }];
else talentList[grade].push({ grade, name, description, id });
}
return new Array(10)
.fill(1).map((v, i)=>{
if(!i && include) return include;
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 Number(this.get(talents).status) || 0;
}
do(talentId, property) {
const { effect, condition, grade, name, description } = this.get(talentId);
if(condition && !checkCondition(property, condition))
return null;
return { effect, grade, name, description };
}
}
export default Talent;