Squashed commit of the following:

commit b421082374
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Fri Dec 31 20:26:20 2021 +0800

    update specialthanks

commit 765a69294b
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Tue Dec 14 19:10:03 2021 +0800

    fix ios goto

commit 8eb8c3ca72
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Tue Dec 14 19:03:37 2021 +0800

    add event grade

commit ab705dd46c
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Tue Dec 14 18:48:42 2021 +0800

    build version

commit a1e4232369
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Tue Dec 14 18:47:05 2021 +0800

    change talent replace before property allocate

commit f4d7b3ae3c
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Tue Dec 14 18:06:18 2021 +0800

    add event grade

commit 0567e40ddc
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Thu Dec 2 21:28:10 2021 +0800

    add particle

commit f17003925e
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Thu Dec 2 20:14:26 2021 +0800

    change ui

commit f8beda1d8f
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Thu Dec 2 19:08:30 2021 +0800

    change popup message ui

commit 915a2aa17d
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Thu Dec 2 18:34:29 2021 +0800

    new build

commit b5edabc104
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Thu Dec 2 18:18:46 2021 +0800

    add hint message

commit a143feadaf
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Thu Dec 2 17:57:31 2021 +0800

    fix copy bug

commit eab1975791
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Thu Dec 2 17:56:28 2021 +0800

    add save/load

commit acf9b92134
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Thu Dec 2 14:35:33 2021 +0800

    add theme switch

commit 488a6054ab
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Wed Dec 1 18:30:36 2021 +0800

    fix uiManager bug

commit f0317eaf14
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Wed Dec 1 16:07:03 2021 +0800

    add message popup

commit eb02d1ee6f
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Nov 28 21:22:54 2021 +0800

    change relocation

commit 60503eae47
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Nov 28 19:27:16 2021 +0800

    change ScaleButton -> Laya.runtime.ScaleButton

commit 52ef5909c1
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Nov 28 19:26:31 2021 +0800

    change ScaleButton -> Laya.runtime.ScaleButton

commit 0284f6aa71
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Nov 28 19:05:24 2021 +0800

    config webpack

commit 0e272ffd99
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Nov 28 13:17:41 2021 +0800

    add thanks

commit e1e2ab4701
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Nov 28 11:40:21 2021 +0800

    add light theme

commit 4f55c00f86
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sat Nov 27 22:27:39 2021 +0800

    fix bug

commit 1d07bdfc46
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sat Nov 27 21:48:47 2021 +0800

    daily update

commit 52d6a0ca8f
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Wed Nov 24 23:10:04 2021 +0800

    daily update

commit f8dd720d4d
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Wed Nov 24 19:08:13 2021 +0800

    daily update

commit d088c1a862
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Nov 21 21:41:46 2021 +0800

    remove useless

commit a711fcf3ed
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Tue Nov 16 00:53:05 2021 +0800

    add achievement popup
    add page adaptive
    add talent extends

commit 05e02de3db
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sat Nov 13 01:24:04 2021 +0800

    add github and discord

commit bff0109cc5
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sat Nov 13 00:30:01 2021 +0800

    add trajectory auto mode

commit 54c04f65c2
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Fri Nov 12 19:54:31 2021 +0800

    fix property judge negative number bug

commit 660739530b
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Fri Nov 12 19:48:58 2021 +0800

    update achievement

commit c94e9e7433
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Thu Nov 11 21:37:45 2021 +0800

    update daily

commit c06cd9ea62
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Tue Nov 9 00:08:05 2021 +0800

    update daily

commit 2666983f89
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Nov 7 23:00:22 2021 +0800

    add uiManager auto scan and load skin resource

commit 22fa5d755a
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Nov 7 22:17:04 2021 +0800

    update framework

commit f40698d63e
Author: Vick Scarlet <scarlet_vick@outlook.com>
Date:   Sun Oct 31 00:13:22 2021 +0800

    add laya
This commit is contained in:
Vick Scarlet
2021-12-31 20:31:26 +08:00
parent 99640a5813
commit 150c991648
372 changed files with 305714 additions and 5801 deletions

View File

@@ -0,0 +1,68 @@
import { clone } from '../functions/util.js';
import { checkCondition } from '../functions/condition.js';
class Achievement {
constructor() {}
// 时机
Opportunity = {
START: "START", // 分配完成点数,点击开始新人生后
TRAJECTORY: "TRAJECTORY", // 每一年的人生经历中
SUMMARY: "SUMMARY", // 人生结束,点击人生总结后
END: "END", // 游戏完成,点击重开 重开次数在这之后才会+1
};
#achievements;
initial({achievements}) {
this.#achievements = achievements;
return this.count;
}
get count() {
return Object.keys(this.#achievements).length;
}
list(property) {
return Object
.values(this.#achievements)
.map(({
id, name, opportunity,
description, hide, grade,
})=>({
id, name, opportunity,
description, hide, grade,
isAchieved: this.isAchieved(id, property),
}));
}
get(achievementId) {
const achievement = this.#achievements[achievementId];
if(!achievement) throw new Error(`[ERROR] No Achievement[${achievementId}]`);
return clone(achievement);
}
check(achievementId, property) {
const { condition } = this.get(achievementId);
return checkCondition(property, condition);
}
isAchieved(achievementId, property) {
for(const [achieved] of (property.get(property.TYPES.ACHV)||[]))
if(achieved == achievementId) return true;
return false;
}
achieve(opportunity, property) {
this.list(property)
.filter(({isAchieved})=>!isAchieved)
.filter(({opportunity: o})=>o==opportunity)
.filter(({id})=>this.check(id, property))
.forEach(({id})=>{
property.achieve(property.TYPES.ACHV, id)
$$event('achievement', this.get(id))
});
}
}
export default Achievement;

57
src/modules/event.js Normal file
View File

@@ -0,0 +1,57 @@
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) continue;
event.branch = event.branch.map(b=>{
b = b.split(':');
b[1] = Number(b[1]);
return b;
});
}
return this.count;
}
get count() {
return Object.keys(this.#events).length;
}
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];
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, grade } = this.get(eventId);
if(branch)
for(const [cond, next] of branch)
if(checkCondition(property, cond))
return { effect, next, description, grade };
return { effect, postEvent, description, grade };
}
}
export default Event;

260
src/modules/life.js Normal file
View File

@@ -0,0 +1,260 @@
import { clone, weightRandom, getListValuesMap, getConvertedMap } from '../functions/util.js'
import Property from './property.js';
import Event from './event.js';
import Talent from './talent.js';
import Achievement from './achievement.js';
class Life {
constructor() {
this.#property = new Property();
this.#event = new Event();
this.#talent = new Talent();
this.#achievement = new Achievement();
}
#property;
#event;
#talent;
#achievement;
#triggerTalents;
#defaultPropertyPoints;
#talentSelectLimit;
#propertyAllocateLimit;
#defaultPropertys;
#specialThanks;
#initialData;
async initial(i18nLoad, commonLoad) {
const [age, talents, events, achievements, specialThanks] = await Promise.all([
i18nLoad('age'),
i18nLoad('talents'),
i18nLoad('events'),
i18nLoad('achievement'),
commonLoad('specialthanks'),
]);
this.#specialThanks = specialThanks;
const total = {
[this.PropertyTypes.TACEV]: this.#achievement.initial({achievements}),
[this.PropertyTypes.TEVT]: this.#event.initial({events}),
[this.PropertyTypes.TTLT]: this.#talent.initial({talents}),
};
this.#property.initial({age, total});
}
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 = {}, // default propertys
talentConfig, // config for talent
propertyConfig, // config for property
} = {}) {
this.#defaultPropertyPoints = defaultPropertyPoints;
this.#talentSelectLimit = talentSelectLimit;
this.#propertyAllocateLimit = propertyAllocateLimit;
this.#defaultPropertys = defaultPropertys;
this.#talent.config(talentConfig);
this.#property.config(propertyConfig);
}
remake(talents) {
this.#initialData = clone(this.#defaultPropertys);
this.#initialData.TLT = clone(talents);
this.#triggerTalents = {};
return this.talentReplace(this.#initialData.TLT);
}
start(allocation) {
for(const key in allocation) {
this.#initialData[key] = clone(allocation[key]);
}
this.#property.restart(this.#initialData);
this.doTalent()
this.#property.restartLastStep();
this.#achievement.achieve(
this.AchievementOpportunity.START,
this.#property
);
}
getPropertyPoints() {
return this.#defaultPropertyPoints + this.#talent.allocationAddition(this.#initialData.TLT);
}
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));
const isEnd = this.#property.isEnd();
const content = [talentContent, eventContent].flat();
this.#achievement.achieve(
this.AchievementOpportunity.TRAJECTORY,
this.#property
)
return { age, content, isEnd };
}
talentReplace(talents) {
const result = this.#talent.replace(talents);
const contents = [];
for(const id in result) {
talents.push(result[id]);
const source = this.#talent.get(id);
const target = this.#talent.get(result[id]);
contents.push({
type: 'talentReplace',
source, target
});
}
return contents;
}
doTalent(talents) {
if(talents) this.#property.change(this.PropertyTypes.TLT, talents);
talents = this.#property.get(this.PropertyTypes.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.PropertyTypes.TLT,
name,
grade,
description,
})
if(!effect) continue;
this.#property.effect(effect);
}
return contents;
}
doEvent(eventId) {
const { effect, next, description, postEvent, grade } = this.#event.do(eventId, this.#property);
this.#property.change(this.PropertyTypes.EVT, eventId);
this.#property.effect(effect);
const content = {
type: this.PropertyTypes.EVT,
description,
postEvent,
grade,
}
if(next) return [content, this.doEvent(next)].flat();
return [content];
}
random(events) {
return weightRandom(
events.filter(
([eventId])=>this.#event.check(eventId, this.#property)
)
);
}
talentRandom() {
return this.#talent.talentRandom(
this.lastExtendTalent,
this.#getPropertys(
this.PropertyTypes.TMS,
this.PropertyTypes.CACHV,
)
);
}
talentExtend(talentId) {
this.#property.set(this.PropertyTypes.EXT, talentId);
}
exclusive(talents, exclusive) {
return this.#talent.exclusive(talents, exclusive);
}
#getJudges(...types) {
return getListValuesMap(types.flat(), key => this.#property.judge(key));
}
#getPropertys(...types) {
return getListValuesMap(types.flat(), key => this.#property.get(key));
}
get lastExtendTalent() {
return this.#property.get(this.PropertyTypes.EXT);
}
get summary() {
this.#achievement.achieve(
this.AchievementOpportunity.SUMMARY,
this.#property
)
const pt = this.PropertyTypes;
return this.#getJudges(pt.SUM,
pt.HAGE, pt.HCHR, pt.HINT,
pt.HSTR, pt.HMNY, pt.HSPR,
);
}
get statistics() {
const pt = this.PropertyTypes;
return this.#getJudges( pt.TMS,
pt.CACHV, pt.RTLT, pt.REVT,
);
}
get achievements() {
const ticks = {};
this.#property
.get(this.PropertyTypes.ACHV)
.forEach(([id, tick]) => ticks[id] = tick);
return this
.#achievement
.list(this.#property)
.sort((
{id: a, grade: ag, hide: ah},
{id: b, grade: bg, hide: bh}
)=>{
a = ticks[a];
b = ticks[b];
if(a&&b) return b - a;
if(!a&&!b) {
if(ah&&bh) return bg - ag;
if(ah) return 1;
if(bh) return -1;
return bg - ag;
}
if(!a) return 1;
if(!b) return -1;
});
}
get PropertyTypes() { return this.#property.TYPES; }
get AchievementOpportunity() { return this.#achievement.Opportunity; }
get talentSelectLimit() { return this.#talentSelectLimit; }
get propertyAllocateLimit() { return clone(this.#propertyAllocateLimit); }
get propertys() { return this.#property.getPropertys(); }
get times() { return this.#property.get(this.PropertyTypes.TMS) || 0; }
set times(v) {
this.#property.set(this.PropertyTypes.TMS, v);
this.#achievement.achieve(
this.AchievementOpportunity.END,
this.#property
)
}
get specialThanks() { return this.#specialThanks; }
}
export default Life;

426
src/modules/property.js Normal file
View File

@@ -0,0 +1,426 @@
import { max, min, sum, clone, listRandom } from '../functions/util.js';
class Property {
constructor() {}
TYPES = {
// 本局
AGE: "AGE", // 年龄 age AGE
CHR: "CHR", // 颜值 charm CHR
INT: "INT", // 智力 intelligence INT
STR: "STR", // 体质 strength STR
MNY: "MNY", // 家境 money MNY
SPR: "SPR", // 快乐 spirit SPR
LIF: "LIF", // 生命 life LIFE
TLT: "TLT", // 天赋 talent TLT
EVT: "EVT", // 事件 event EVT
TMS: "TMS", // 次数 times TMS
// Auto calc
LAGE: "LAGE", // 最低年龄 Low Age
HAGE: "HAGE", // 最高年龄 High Age
LCHR: "LCHR", // 最低颜值 Low Charm
HCHR: "HCHR", // 最高颜值 High Charm
LINT: "LINT", // 最低智力 Low Intelligence
HINT: "HINT", // 最高智力 High Intelligence
LSTR: "LSTR", // 最低体质 Low Strength
HSTR: "HSTR", // 最高体质 High Strength
LMNY: "LMNY", // 最低家境 Low Money
HMNY: "HMNY", // 最高家境 High Money
LSPR: "LSPR", // 最低快乐 Low Spirit
HSPR: "HSPR", // 最高快乐 High Spirit
SUM: "SUM", // 总评 summary SUM
EXT: "EXT", // 继承天赋
// 总计
// Achievement Total
ATLT: "ATLT", // 拥有过的天赋 Achieve Talent
AEVT: "AEVT", // 触发过的事件 Achieve Event
ACHV: "ACHV", // 达成的成就 Achievement
CTLT: "CTLT", // 天赋选择数 Count Talent
CEVT: "CEVT", // 事件收集数 Count Event
CACHV: "CACHV", // 成就达成数 Count Achievement
// 总数
TTLT: "TTLT", // 总天赋数 Total Talent
TEVT: "TEVT", // 总事件数 Total Event
TACHV: "TACHV", // 总成就数 Total Achievement
// 比率
REVT: "REVT", // 事件收集率 Rate Event
RTLT: "RTLT", // 天赋选择率 Rate Talent
RACHV: "RACHV", // 成就达成率 Rate Achievement
// SPECIAL
RDM: 'RDM', // 随机属性 random RDM
};
// 特殊类型
SPECIAL = {
RDM: [ // 随机属性 random RDM
this.TYPES.CHR,
this.TYPES.INT,
this.TYPES.STR,
this.TYPES.MNY,
this.TYPES.SPR,
]
}
#ageData;
#data = {};
#total;
#judge;
initial({age, total}) {
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 };
}
this.#total = total;
}
config({judge = {}}) {
this.#judge = judge;
}
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]: [],
[this.TYPES.LAGE]: Infinity,
[this.TYPES.LCHR]: Infinity,
[this.TYPES.LINT]: Infinity,
[this.TYPES.LSTR]: Infinity,
[this.TYPES.LSPR]: Infinity,
[this.TYPES.LMNY]: Infinity,
[this.TYPES.HAGE]: -Infinity,
[this.TYPES.HCHR]: -Infinity,
[this.TYPES.HINT]: -Infinity,
[this.TYPES.HSTR]: -Infinity,
[this.TYPES.HMNY]: -Infinity,
[this.TYPES.HSPR]: -Infinity,
};
for(const key in data)
this.change(key, data[key]);
}
restartLastStep() {
this.#data[this.TYPES.LAGE] = this.get(this.TYPES.AGE);
this.#data[this.TYPES.LCHR] = this.get(this.TYPES.CHR);
this.#data[this.TYPES.LINT] = this.get(this.TYPES.INT);
this.#data[this.TYPES.LSTR] = this.get(this.TYPES.STR);
this.#data[this.TYPES.LSPR] = this.get(this.TYPES.SPR);
this.#data[this.TYPES.LMNY] = this.get(this.TYPES.MNY);
this.#data[this.TYPES.HAGE] = this.get(this.TYPES.AGE);
this.#data[this.TYPES.HCHR] = this.get(this.TYPES.CHR);
this.#data[this.TYPES.HINT] = this.get(this.TYPES.INT);
this.#data[this.TYPES.HSTR] = this.get(this.TYPES.STR);
this.#data[this.TYPES.HMNY] = this.get(this.TYPES.MNY);
this.#data[this.TYPES.HSPR] = this.get(this.TYPES.SPR);
}
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]);
case this.TYPES.LAGE:
case this.TYPES.LCHR:
case this.TYPES.LINT:
case this.TYPES.LSTR:
case this.TYPES.LMNY:
case this.TYPES.LSPR:
return min(
this.#data[prop],
this.get(this.fallback(prop))
);
case this.TYPES.HAGE:
case this.TYPES.HCHR:
case this.TYPES.HINT:
case this.TYPES.HSTR:
case this.TYPES.HMNY:
case this.TYPES.HSPR:
return max(
this.#data[prop],
this.get(this.fallback(prop))
);
case this.TYPES.SUM:
const HAGE = this.get(this.TYPES.HAGE);
const HCHR = this.get(this.TYPES.HCHR);
const HINT = this.get(this.TYPES.HINT);
const HSTR = this.get(this.TYPES.HSTR);
const HMNY = this.get(this.TYPES.HMNY);
const HSPR = this.get(this.TYPES.HSPR);
return Math.floor(sum(HCHR, HINT, HSTR, HMNY, HSPR)*2 + HAGE/2);
case this.TYPES.TMS:
return this.lsget('times') || 0;
case this.TYPES.EXT:
return this.lsget('extendTalent') || null;
case this.TYPES.ATLT:
case this.TYPES.AEVT:
case this.TYPES.ACHV:
return this.lsget(prop) || [];
case this.TYPES.CTLT:
case this.TYPES.CEVT:
case this.TYPES.CACHV:
return this.get(
this.fallback(prop)
).length;
case this.TYPES.TTLT:
case this.TYPES.TEVT:
case this.TYPES.TACHV:
return this.#total[prop];
case this.TYPES.RTLT:
case this.TYPES.REVT:
case this.TYPES.RACHV:
const fb = this.fallback(prop);
return this.get(fb[0]) / this.get(fb[1]);
default: return 0;
}
}
fallback(prop) {
switch(prop) {
case this.TYPES.LAGE:
case this.TYPES.HAGE: return this.TYPES.AGE;
case this.TYPES.LCHR:
case this.TYPES.HCHR: return this.TYPES.CHR;
case this.TYPES.LINT:
case this.TYPES.HINT: return this.TYPES.INT;
case this.TYPES.LSTR:
case this.TYPES.HSTR: return this.TYPES.STR;
case this.TYPES.LMNY:
case this.TYPES.HMNY: return this.TYPES.MNY;
case this.TYPES.LSPR:
case this.TYPES.HSPR: return this.TYPES.SPR;
case this.TYPES.CTLT: return this.TYPES.ATLT;
case this.TYPES.CEVT: return this.TYPES.AEVT;
case this.TYPES.CACHV: return this.TYPES.ACHV;
case this.TYPES.LIF: return this.TYPES.LIF;
case this.TYPES.RTLT: return [this.TYPES.CTLT, this.TYPES.TTLT];
case this.TYPES.REVT: return [this.TYPES.CEVT, this.TYPES.TEVT];
case this.TYPES.RACHV: return [this.TYPES.CACHV, this.TYPES.TACHV];
default: return;
}
}
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.hl(prop, this.#data[prop] = clone(value));
this.achieve(prop, value);
return;
case this.TYPES.TMS:
this.lsset('times', parseInt(value) || 0);
return;
case this.TYPES.EXT:
this.lsset('extendTalent', value);
return
default: return;
}
}
getPropertys() {
return clone({
[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),
});
}
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.hl(prop, this.#data[prop] += Number(value));
return;
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);
this.achieve(prop, value);
return;
case this.TYPES.TMS:
this.set(
prop,
this.get(prop) + parseInt(value)
);
return;
default: return;
}
}
hookSpecial(prop) {
switch(prop) {
case this.TYPES.RDM: return listRandom(this.SPECIAL.RDM);
default: return prop;
}
}
effect(effects) {
for(let prop in effects)
this.change(
this.hookSpecial(prop),
Number(effects[prop])
);
}
judge(prop) {
const value = this.get(prop);
const d = this.#judge[prop];
let length = d.length;
// progress judge
// const p = 1/length;
// const progress = () => {
// const min = d[length][0] || 0;
// const max = d[length+1]?.[0] || value;
// if(max == min) return 1;
// return p * (length + (value - min) / (max - min));
// }
const progress = () => Math.max(Math.min(value, 10), 0) / 10;
while(length--) {
const [min, grade, judge] = d[length];
if(!length || min==void 0 || value >= min) return {prop, value, judge, grade, progress: progress()};
}
}
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]);
}
hl(prop, value) {
let keys;
switch(prop) {
case this.TYPES.AGE: keys = [this.TYPES.LAGE, this.TYPES.HAGE]; break;
case this.TYPES.CHR: keys = [this.TYPES.LCHR, this.TYPES.HCHR]; break;
case this.TYPES.INT: keys = [this.TYPES.LINT, this.TYPES.HINT]; break;
case this.TYPES.STR: keys = [this.TYPES.LSTR, this.TYPES.HSTR]; break;
case this.TYPES.MNY: keys = [this.TYPES.LMNY, this.TYPES.HMNY]; break;
case this.TYPES.SPR: keys = [this.TYPES.LSPR, this.TYPES.HSPR]; break;
default: return;
}
const [l, h] = keys;
this.#data[l] = min(this.#data[l], value);
this.#data[h] = max(this.#data[h], value);
}
achieve(prop, newData) {
let key;
switch(prop) {
case this.TYPES.ACHV:
const lastData = this.lsget(prop);
this.lsset(
prop,
(lastData || []).concat([[newData, Date.now()]])
);
return;
case this.TYPES.TLT: key = this.TYPES.ATLT; break;
case this.TYPES.EVT: key = this.TYPES.AEVT; break;
default: return;
}
const lastData = this.lsget(key) || [];
this.lsset(
key,
Array.from(
new Set(
lastData
.concat(newData||[])
.flat()
)
)
)
}
lsget(key) {
const data = localStorage.getItem(key);
if(data === null) return;
return JSON.parse(data);
}
lsset(key, value) {
localStorage.setItem(
key,
JSON.stringify(value)
);
}
}
export default Property;

201
src/modules/talent.js Normal file
View File

@@ -0,0 +1,201 @@
import { clone, weightRandom } from '../functions/util.js';
import { checkCondition, extractMaxTriggers } from '../functions/condition.js';
class Talent {
constructor() {}
#talents;
#talentPullCount;
#talentRate;
#additions;
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);
if(talent.replacement) {
for(let key in talent.replacement) {
const obj = {};
for(let value of talent.replacement[key]) {
value = `${value}`.split('*');
obj[value[0]||0] = Number(value[1]) || 1;
}
talent.replacement[key] = obj;
}
}
}
return this.count;
}
get count() {
return Object.keys(this.#talents).length;
}
config({
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 = {}, // additional additions
} = {}) {
this.#talentPullCount = talentPullCount;
this.#talentRate = talentRate;
this.#additions = additions;
}
check(talentId, property) {
const { condition } = this.get(talentId);
return checkCondition(property, condition);
}
get(talentId) {
const talent = this.#talents[talentId];
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;
}
getAddition(type, value) {
if(!this.#additions[type]) return {};
for(const [min, addition] of this.#additions[type]) {
if(value >= min) return addition;
}
return {};
}
getRate(additionValues = {}) {
const rate = clone(this.#talentRate);
const addition = { 1:1, 2:1, 3:1, };
Object.keys(additionValues).forEach(key => {
const addi = this.getAddition(key, additionValues[key])
for(const grade in addi)
addition[grade] += addi[grade];
});
for(const grade in addition)
rate[grade] *= addition[grade];
return rate;
}
talentRandom(include, additionValues) {
const rate = this.getRate(additionValues);
const randomGrade = () => {
let randomNumber = Math.floor(Math.random() * rate.total);
if((randomNumber -= rate[3]) < 0) return 3;
if((randomNumber -= rate[2]) < 0) return 2;
if((randomNumber -= rate[1]) < 0) return 1;
return 0;
}
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(this.#talentPullCount)
.fill(1).map((v, i)=>{
if(!i && include) return include;
let grade = randomGrade();
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 };
}
replace(talents) {
const getReplaceList = (talent, talents) => {
const { replacement } = this.get(talent);
if(!replacement) return null;
const list = [];
if(replacement.grade) {
this.forEach(({id, grade})=>{
if(!replacement.grade[grade]) return;
if(this.exclusive(talents, id)) return;
list.push([id, replacement.grade[grade]]);
})
}
if(replacement.talent) {
for(let id in replacement.talent) {
id = Number(id);
if(this.exclusive(talents, id)) continue;
list.push([id, replacement.talent[id]]);
}
}
return list;
}
const replace = (talent, talents) => {
const replaceList = getReplaceList(talent, talents);
if(!replaceList) return talent;
const rand = weightRandom(replaceList);
return replace(
rand, talents.concat(rand)
);
}
const newTalents = clone(talents);
const result = {};
for(const talent of talents) {
const replaceId = replace(talent, newTalents);
if(replaceId != talent) {
result[talent] = replaceId;
newTalents.push(replaceId);
}
}
return result;
}
forEach(callback) {
if(typeof callback != 'function') return;
for(const id in this.#talents)
callback(clone(this.#talents[id]), id);
}
}
export default Talent;