mirror of
https://github.com/VickScarlet/lifeRestart.git
synced 2026-04-08 08:58:55 +08:00
add talent life
This commit is contained in:
133
src/condition.js
133
src/condition.js
@@ -1,133 +0,0 @@
|
||||
class Condition {
|
||||
constructor(initialData={}) {
|
||||
this.initial(initialData);
|
||||
}
|
||||
|
||||
initial({prop}) {
|
||||
if(prop) this.prop = prop;
|
||||
}
|
||||
|
||||
parse(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;
|
||||
}
|
||||
|
||||
check(condition) {
|
||||
const conditions = this.parse(condition);
|
||||
return this.checkParsedConditions(conditions);
|
||||
}
|
||||
|
||||
checkParsedConditions(conditions) {
|
||||
if(!Array.isArray(conditions)) return this.checkProp(conditions);
|
||||
if(conditions.length == 0) return true;
|
||||
if(conditions.length == 1) return this.checkParsedConditions(conditions[0]);
|
||||
|
||||
let ret = this.checkParsedConditions(conditions[0]);
|
||||
for(let i=1; i<conditions.length; i+=2) {
|
||||
switch(conditions[i]) {
|
||||
case '&':
|
||||
if(ret) ret = this.checkParsedConditions(conditions[i+1]);
|
||||
break;
|
||||
case '|':
|
||||
if(ret) return true;
|
||||
ret = this.checkParsedConditions(conditions[i+1]);
|
||||
break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
checkProp(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 = this.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;
|
||||
}
|
||||
}
|
||||
|
||||
getProp(prop) {
|
||||
return this.prop.get(prop);
|
||||
}
|
||||
|
||||
get prop() {return this._prop;}
|
||||
set prop(p) {this._prop = p;}
|
||||
}
|
||||
|
||||
export default Condition;
|
||||
109
src/event.js
109
src/event.js
@@ -1,86 +1,15 @@
|
||||
import {clone} from './util.js';
|
||||
import { clone } from './functions/util.js';
|
||||
import { checkCondition } from './functions/condition.js';
|
||||
|
||||
class Event {
|
||||
constructor(initialData={}) {
|
||||
this.initial(initialData);
|
||||
}
|
||||
|
||||
initial({prop, condition, events, pools}) {
|
||||
if(prop) this.prop = prop;
|
||||
if(condition) this.condition = condition;
|
||||
if(events) this.events = events;
|
||||
if(pools) this.pools = pools;
|
||||
}
|
||||
|
||||
random() {
|
||||
const age = this.getProp(this.prop.TYPES.AGE);
|
||||
const pool = this.filterPool(
|
||||
this.getPool(age)
|
||||
);
|
||||
|
||||
let totalWeights = 0;
|
||||
for(const [,weight] of pool)
|
||||
totalWeights += weight;
|
||||
|
||||
let random = Math.random() * totalWeights;
|
||||
for(const [event,weight] of pool)
|
||||
if((random-=weight)<0)
|
||||
return event;
|
||||
}
|
||||
|
||||
check(eventId) {
|
||||
const { Include, Exclude } = this.get(eventId);
|
||||
if(Exclude && this.checkCondition(Exclude)) return false;
|
||||
if(Include) return this.checkCondition(Include);
|
||||
return true;
|
||||
}
|
||||
|
||||
checkCondition(condition) {
|
||||
return this.condition.check(condition);
|
||||
}
|
||||
|
||||
filterPool(pool) {
|
||||
return pool.filter(([event])=>this.check(event));
|
||||
}
|
||||
|
||||
get(eventId) {
|
||||
const event = this.events[eventId];
|
||||
if(!event) console.error(`[ERROR] No Event[${eventId}]`);
|
||||
return clone(event);
|
||||
}
|
||||
|
||||
getPool(age) {
|
||||
return clone(this.pools[age].Pool) || [];
|
||||
}
|
||||
|
||||
getProp(prop) {
|
||||
return this.prop.get(prop);
|
||||
}
|
||||
|
||||
get prop() {return this._prop;}
|
||||
set prop(p) {this._prop = p;}
|
||||
|
||||
get condition() {return this._condition;}
|
||||
set condition(c) {this._condition = c;}
|
||||
|
||||
get pools() {return this._pools;}
|
||||
set pools(p) {
|
||||
this._pools = p;
|
||||
|
||||
for(const age in p)
|
||||
p[age].Pool = p[age].Pool?.map(v=>{
|
||||
const value = v.split('*').map(n=>Number(n));
|
||||
if(value.length==1) value.push(1);
|
||||
return value;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
get events() {return this._events;}
|
||||
set events(e) {
|
||||
this._events = e;
|
||||
for(const id in e) {
|
||||
const event = e[id];
|
||||
initial({events}) {
|
||||
this.#events = events;
|
||||
for(const id in events) {
|
||||
const event = events[id];
|
||||
if(!event.branch) continue;
|
||||
event.branch = event.branch.map(b=>{
|
||||
b = b.split(':');
|
||||
@@ -90,6 +19,32 @@ class Event {
|
||||
}
|
||||
}
|
||||
|
||||
check(eventId) {
|
||||
const { include, exclude } = this.get(eventId);
|
||||
if(exclude && checkCondition(exclude)) return false;
|
||||
if(include) return checkCondition(include);
|
||||
return true;
|
||||
}
|
||||
|
||||
get(eventId) {
|
||||
const event = this.#events[eventId];
|
||||
if(!event) throw new Error(`[ERROR] No Event[${eventId}]`);
|
||||
return clone(event);
|
||||
}
|
||||
|
||||
description(eventId) {
|
||||
return this.get(eventId).description;
|
||||
}
|
||||
|
||||
do(eventId) {
|
||||
const { effect, branch } = this.get(eventId);
|
||||
if(branch)
|
||||
for(const [cond, next] of branch)
|
||||
if(checkCondition(cond))
|
||||
return { effect, next };
|
||||
return { effect };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Event;
|
||||
116
src/functions/condition.js
Normal file
116
src/functions/condition.js
Normal file
@@ -0,0 +1,116 @@
|
||||
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(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;
|
||||
}
|
||||
}
|
||||
|
||||
export { checkCondition };
|
||||
47
src/life.js
Normal file
47
src/life.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import { readFile } from 'fs/promises';
|
||||
import Property from './property.js';
|
||||
import Event from './event.js';
|
||||
import Talent from './talent.js';
|
||||
|
||||
class Life {
|
||||
constructor() {
|
||||
this.#property = new Property();
|
||||
this.#event = new Event();
|
||||
this.#talent = new Talent();
|
||||
}
|
||||
|
||||
async initial() {
|
||||
const age = JSON.parse(await readFile('data/age.json'));
|
||||
const talents = JSON.parse(await readFile('data/talents.json'));
|
||||
const events = JSON.parse(await readFile('data/events.json'));
|
||||
|
||||
this.#property.initial({age});
|
||||
this.#talent.initial({talents});
|
||||
this.#event.initial({events});
|
||||
}
|
||||
|
||||
restart(allocation) {
|
||||
this.#prop.restart(allocation);
|
||||
}
|
||||
|
||||
next() {
|
||||
const {age, event, talent} = this.#prop.ageNext();
|
||||
|
||||
const eventId = this.random(event);
|
||||
}
|
||||
|
||||
random(events) {
|
||||
events.filter(([eventId])=>this.#event.check(eventId));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export default Life;
|
||||
88
src/prop.js
88
src/prop.js
@@ -1,88 +0,0 @@
|
||||
import {clone} from './util.js';
|
||||
|
||||
class Prop {
|
||||
constructor(initialData={}) {
|
||||
this.initial(initialData);
|
||||
}
|
||||
|
||||
initial(data) {
|
||||
this._data = {
|
||||
AGE: 0
|
||||
};
|
||||
for(const key in data)
|
||||
this.set(key, data[key]);
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
change(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:
|
||||
this._data[prop] += 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;
|
||||
}
|
||||
}
|
||||
|
||||
get TYPES() {
|
||||
return {
|
||||
AGE: "AGE",
|
||||
CHR: "CHR",
|
||||
INT: "INT",
|
||||
STR: "STR",
|
||||
MNY: "MNY",
|
||||
SPR: "SPR",
|
||||
LIF: "LIF",
|
||||
TLT: "TLT",
|
||||
EVT: "EVT",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Prop;
|
||||
131
src/property.js
Normal file
131
src/property.js
Normal file
@@ -0,0 +1,131 @@
|
||||
import { clone } from './functions/util.js';
|
||||
|
||||
class Property {
|
||||
constructor(initialData={}) {
|
||||
this.initial(initialData);
|
||||
}
|
||||
|
||||
TYPES = {
|
||||
AGE: "AGE",
|
||||
CHR: "CHR",
|
||||
INT: "INT",
|
||||
STR: "STR",
|
||||
MNY: "MNY",
|
||||
SPR: "SPR",
|
||||
LIF: "LIF",
|
||||
TLT: "TLT",
|
||||
EVT: "EVT",
|
||||
};
|
||||
|
||||
initial({age}) {
|
||||
|
||||
this.#ageData = age;
|
||||
for(const age in a)
|
||||
a[age].event = a[age].event?.map(v=>{
|
||||
const value = v.split('*').map(n=>Number(n));
|
||||
if(value.length==1) value.push(1);
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
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, value);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
change(prop, value) {
|
||||
if(Array.isArray(value)) {
|
||||
for(const v of value)
|
||||
this.change(prop, 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);
|
||||
}
|
||||
|
||||
ageNext() {
|
||||
this.change(this.TYPES.AGE, 1);
|
||||
const age = this.get(this.TYPES.AGE);
|
||||
const {event, talent} = this.getAge(age);
|
||||
return {age, event, talent};
|
||||
}
|
||||
|
||||
getAgeData(age) {
|
||||
return clone(this.#ageData[age]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Property;
|
||||
33
src/talent.js
Normal file
33
src/talent.js
Normal file
@@ -0,0 +1,33 @@
|
||||
class Talent {
|
||||
constructor(initialData={}) {
|
||||
this.initial(initialData);
|
||||
}
|
||||
|
||||
initial({talent}) {
|
||||
this.#talent = talent;
|
||||
}
|
||||
|
||||
check(talentId) {
|
||||
const { condition } = this.get(talentId);
|
||||
return checkCondition(condition);
|
||||
}
|
||||
|
||||
get(talentId) {
|
||||
const talent = this.#talent[talentId];
|
||||
if(!talent) throw new Error(`[ERROR] No Talent[${talentId}]`);
|
||||
return clone(talent);
|
||||
}
|
||||
|
||||
description(talentId) {
|
||||
return this.get(talentId).description;
|
||||
}
|
||||
|
||||
do(talentId) {
|
||||
const { effect, condition } = this.get(talentId);
|
||||
if(condition && !checkCondition(condition))
|
||||
return null;
|
||||
return { effect };
|
||||
}
|
||||
}
|
||||
|
||||
export default Talent;
|
||||
Reference in New Issue
Block a user