人生重开模拟器 的主逻辑:每一年先判断天赋再判断事件

  • Post author:
  • Post category:其他


人生重开模拟器 git

https://github.com/VickScarlet/lifeRestart



1.表

他的表很好懂,或者说其实看了表之后都不用强求看代码了hhh策划狂喜

天赋:

额外天赋,替换,互斥

在这里插入图片描述

在这里插入图片描述

事件:

属性是否大于小于或者是否有某天赋

在这里插入图片描述

年龄:

在这里插入图片描述

有没有一种可能

人生重来模拟器

是把每一年可能遇到的事件都枚举出来的

成就:

属性是否大于小于某一值,是否有某一事件



2.痛苦阅读代码的过程

windows11 系统下,根据 readme,使用 npm 开头的系列指令启动游戏

在这里插入图片描述

进入人生模拟之后使用 f10,然后很快发现在 life.js 中,他每过一年就是调用一次 next()

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);
        return { age, content, isEnd };
    }


this

是这个类的实例自己,往上看,这是一个 life 类

class Life {
    constructor() {
        this.#property = new Property(this);
        this.#event = new Event(this);
        this.#talent = new Talent(this);
        this.#achievement = new Achievement(this);
        this.#character = new Character(this);
    }

    Module = {
        PROPERTY: 'PROPERTY',
        TALENT: 'TALENT',
        EVENT: 'EVENT',
        ACHIEVEMENT: 'ACHIEVEMENT',
        CHARACTER: 'CHARACTER',
    }

    Function = {
        CONDITION: 'CONDITION',
        UTIL: 'UTIL',
    }

    #property;
    #event;
    #talent;
    #achievement;
    #character;
    #triggerTalents;
    #defaultPropertyPoints;
    #talentSelectLimit;
    #propertyAllocateLimit;
    #defaultPropertys;
    #specialThanks;
    #initialData;

	//后略


#property

是这个类的属性,

ageNext()

在 property.js

ageNext() {
        this.change(this.TYPES.AGE, 1);
        const age = this.get(this.TYPES.AGE);
        const {event, talent} = this.getAgeData(age);
        return {age, event, talent};
    }

这个函数中,

this



Property

类的实例

class Property {
    constructor(system) {
        this.#system = system;
    }

    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,
        ]
    }

    #system;
    #ageData;
    #data = {};
    #total;
    #judge;

	//后略


change()

是类的方法

    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;
        }
    }

如果输入了一个 value 是向量,就 foreach 这个向量,然后对每一个向量元素都对 prop 使用 change,这样就可以使用一个处理 1 对 1 的函数处理 1 对向量了,好厉害

不同的属性有不同的加方式,再细的就不看了

回到

ageNext()

,接下来是

get

    get(prop) {
        const util = this.#util;
        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 util.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 util.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 util.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(util.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;
        }
    }

有的属性是返回

clone

,有的是返回

fallback

,嗯……不知道为什么不是

callback

,难道不应该是回调的意思吗


clone()

在 util.js

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;
    }
}

如果是要克隆的实参是对象的话

如果这个实参是向量,照惯例,通过遍历,将一个向量实参拆成向量元素实参

然后复制这个对象类型的实参的键值对,值可能是一个向量,所以还要用一个

clone()

返回值

然后返回这个复制出来的对象

哦……就是一个可以复制对象的函数

    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;
        }
    }

原来 fallback 是回退的意思……那应该是它备份了一些属性……?不懂,不看了

回到

ageNext()

,接下来是

getAgeData()

getAgeData(age) {
        return this.#system.clone(this.#ageData[age]);
    }

然后又用到一个

ageData

在这里插入图片描述

我不理解啊……为什么

ageData

初始化是

= age

,之后就可以使用

[age]

,又没有什么对象

不懂,跳过

回到

next()

,下一行是

doTalent()

    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);
            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;
    }

第一行要使用

change()

来改变

talents

            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;

这输入的是

talent

,虽然还不知道

talent

是什么,但是应该也是对象把……为啥对象会可能小于 0……太奇怪了

估计是为了处理异常现象……?不知道,跳过

js 中的 const 是底层 const,所以可以修改指向的值。如果

this.#data[prop]

中没有这个

talent

,就把它加进去,嗯,合理

然后

achieve()

    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()
                )
            )
        )
    }

这个时候传进来的是

this.PropertyTypes.TLT

,得到

key = this.TYPES.ATLT

,传入

lsget()

    lsget(key) {
        const data = localStorage.getItem(key);
        if(data === null || data === 'undefined') return;
        return JSON.parse(data);
    }

似乎是要开始读表了,先回去看一下 ATLT

ATLT: "ATLT", // 拥有过的天赋 Achieve Talent

他从本地储存取出这个 ATLT,那应该是在初始化的时候存好了

但是我设断点看的时候一直没有出现这个 ATLT……尴尬了

算了,不管这个

在这里插入图片描述

在这里插入图片描述

AEVT: "AEVT", // 触发过的事件 Achieve Event
ACHV: "ACHV", // 达成的成就 Achievement

然后就是解析 JSON 字符串,nb

在这里插入图片描述

好吧,那还是没有读表

或者说难道本地储存里面就放着表?

不懂,跳过

回到

achieve()

,到了

        const lastData = this.lsget(key) || [];
        this.lsset(
            key,
            Array.from(
                new Set(
                    lastData
                        .concat(newData||[])
                        .flat()
                )
            )
        )

使用

Array.from()



lsset()

传入第二个实参

新建的 Set 是

Array.from()

唯一一个实参,因此这里

Array.from()

的唯一工作是把这个 Set 转换成数组

Set 之中是

lastData



newData

拼接。当数值参与逻辑或运算时,结果为true,会返回第一个为真的值;如果结果为false,会返回第二个为假的值。[] 为假值,因此 newData 为假时会返回空数组。最后

flat()

取消深度

那其实就是新数据旧数据一起传给

lsset()

    lsset(key, value) {
        localStorage.setItem(
            key,
            JSON.stringify(value)
        );
    }

转化成 JSON 存到本地储存

那就是

achieve()

的作用就是从本地取了相应的旧数据再和新数据一起存入本地

那或许

property.change()

的作用就是根据属性更新储存?

回到

doTalent()



property.get()

传入

TLT

,那就还是用的

lsget()

,已经看过了


Array.filter()

设置的过滤条件是,这个 id 对应的 trigger 数量小于最大值

    getTalentCurrentTriggerCount(talentId) {
        return this.#triggerTalents[talentId] || 0;
    }

看来应该是有一个地方会设置 trigger,先跳过

哇哦,接下来会让每一个天赋工作……或许是真正要有游戏逻辑了?


talent.do

在 talent.js

   do(talentId) {
        const { effect, condition, grade, name, description } = this.get(talentId);
        if(condition && !this.#system.check(condition))
            return null;
        return { effect, grade, name, description };
    }


get

是直接获得

talents

属性,这个属性有初始化,嗯……那就跳过

    get(talentId) {
        const talent = this.#talents[talentId];
        if(!talent) throw new Error(`[ERROR] No Talent[${talentId}]`);
        return this.#system.clone(talent);
    }

回到

talent.do



system.check()

返回真才让

talent.do

返回真

class Talent {
    constructor(system) {
        this.#system = system;
    }

    #system;
    #talents;
    #talentPullCount;
    #talentRate;
    #additions;

不知道这个

system

是什么类的,麻了

设个断点看看

在这里插入图片描述

单步走不到那里……算了

如果这个天赋有运行结果,就把它的 trigger + 1,然后拿出

effect



property.effect()

    effect(effects) {
        for(let prop in effects)
            this.change(
                this.hookSpecial(prop),
                Number(effects[prop])
            );
    }



effect

中的每一项都是要提取对应的属性和数值

    hookSpecial(prop) {
        switch(prop) {
            case this.TYPES.RDM:
                return this.#util.listRandom(this.SPECIAL.RDM);
            default: return prop;
        }
    }


hookSpecial()

是特殊属性特殊处理

function listRandom(list) {
    return list[Math.floor(Math.random() * list.length)];
}


listRandom()

是返回列表中随机一个元素

		// SPECIAL
        RDM: 'RDM', // 随机属性 random RDM
    // 特殊类型
    SPECIAL = {
        RDM: [ // 随机属性 random RDM
            this.TYPES.CHR,
            this.TYPES.INT,
            this.TYPES.STR,
            this.TYPES.MNY,
            this.TYPES.SPR,
        ]
    }

随机属性是个列表

那其实

property.effect()

的作用就是把

effect

中的属性-数值 应用到

change()



那其实

doTalent()

就是

talent.do()

找到了

effect

之后应用

回到

next()

。下一行是

doEvent()

    doEvent(eventId) {
        const { effect, next, description, postEvent, grade } = this.#event.do(eventId);
        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];
    }

再看

event.do()

,在 event.js

    do(eventId) {
        const { effect, branch, event: description, postEvent, grade } = this.get(eventId);
        if(branch)
            for(const [cond, next] of branch)
                if(this.#system.check(cond))
                    return { effect, next, description, grade };
        return { effect, postEvent, description, grade };
    }

根据一个 id 就取出这么多东西……我好想悟了,难道逻辑就在于这个

this.get()

然后就是是否有分支,如果有分支,就查看分支的条件,如果满足条件就返回分支指向的下一个事件,否则返回事件的结尾

回到

doEvent()

,如果是

event.do()

返回了下一个事件,那么就对这个事件再来一次

doEvent()

,递归

回到

next()

,接下来判断是否结束

    isEnd() {
        return this.get(this.TYPES.LIF) < 1;
    }
        LIF: "LIF", // 生命 life LIFE

这是根据寿命判断的

接下来再判断是否达成成就,

achievement.achieve()

在 achievement.js 中

    achieve(opportunity) {
        this.list()
            .filter(({isAchieved})=>!isAchieved)
            .filter(({opportunity: o})=>o==opportunity)
            .filter(({id})=>this.check(id, this.#prop))
            .forEach(({id})=>{
                this.#prop.achieve(this.#prop.TYPES.ACHV, id)
                $$event('achievement', this.get(id))
            });
    }
    get(achievementId) {
        const achievement = this.#achievements[achievementId];
        if(!achievement) throw new Error(`[ERROR] No Achievement[${achievementId}]`);
        return this.#system.clone(achievement);
    }

没有完成的

有机会的

id 找得到 属性的

property.js 中的

achieve()

    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()
                )
            )
        )
    }

如果是已完成的成就就添加当前时间,返回

如果是天赋或事件就返回已触发的天赋或事件

其他情况返回

然后已触发的天赋或事件添加上新数据返回

嗯……好吧有点没懂

啊……这就是

next()

的全部内容了……我还是没懂啊


所以感觉最主要的还是这两句

const { effect, condition, grade, name, description } = this.get(talentId);
const { effect, branch, event: description, postEvent, grade } = this.get(eventId);
    get(talentId) {
        const talent = this.#talents[talentId];
        if(!talent) throw new Error(`[ERROR] No Talent[${talentId}]`);
        return this.#system.clone(talent);
    }
    get(eventId) {
        const event = this.#events[eventId];
        if(!event) throw new Error(`[ERROR] No Event[${eventId}]`);
        return this.#system.clone(event);
    }

但是这个

get

拿到的是他们自己的初始化好的属性……所以应该还要看这个东西是怎么初始化的

    initial({talents}) {
        this.#talents = talents;
        const emt = this.#system.function(this.#system.Function.CONDITION).extractMaxTriggers;
        for(const id in talents) {
            const talent = talents[id];
            talent.id= Number(id);
            talent.grade = Number(talent.grade);
            talent.max_triggers = emt(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;
    }
    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;
    }

然后就感觉她这个初始化也不是从头构造的,还有一个输入的值,还要看到底输入了啥

然后就看到位于 lief.js 中的

initial

    async initial(i18nLoad, commonLoad) {
        const [age, talents, events, achievements, characters, specialThanks] = await Promise.all([
            i18nLoad('age'),
            i18nLoad('talents'),
            i18nLoad('events'),
            i18nLoad('achievement'),
            i18nLoad('character'),
            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});
        this.#character.initial({characters});
    }

用于初始化的值是

Promise.all()

来的,其中

i18nLoad

,

commonLoad

是输入的函数,那还要看这个

initial

是谁调用的

在这里插入图片描述

好怪,找不到谁调用了这个

initial


单步调试一下

一开始是运行 index

const core = new Life();
const game = new App();
globalThis.core = core;
globalThis.game = game;

然后 life 的构造函数

class Life {
    constructor() {
        this.#property = new Property(this);
        this.#event = new Event(this);
        this.#talent = new Talent(this);
        this.#achievement = new Achievement(this);
        this.#character = new Character(this);
    }

一系列的

最后是

game.start(query);

然后我一按跳过就到了

initial

的最后了……怪

然后就只能看看变量了

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

好吧,后来又找到了

在这里插入图片描述

诶,但是看不到输入了什么参数,为啥呢……

那如果跳过这个,那就是唯一一个还没看的地方了

在这里插入图片描述

我刚开始看到这个

core

我还以为不是

Life

类 就没管

后面想到,诶,既然是查找引用那就确实是

Life.initial()

		await core.initial(
            dataSet=>Laya.promises.loader.load(`data/${this.#language}/${dataSet}.json`, null, Laya.Loader.JSON),
            dataSet=>Laya.promises.loader.load(`data/${dataSet}.json`, null, Laya.Loader.JSON),
        );

网上搜到的好像都是

Laya.loader.load

没有

Laya.promises.loader.load

的,好神奇

Laya.promises = {
	...//前略
    loader: {
        load: async function (url, progress, type) {
            return new Promise(function (resolve, reject) {
                try {
                    Laya.loader.load(url, Laya.Handler.create(null, ret=>resolve(ret), null, true), progress, type);
                } catch (e) {
                    reject(e);
                }
            });
        }

    }
};

啊……看上去就是个工具,不深究了

好吧,那就回到

Talent.initial()



Event.initial()

const emt = this.#system.function(this.#system.Function.CONDITION).extractMaxTriggers;

这里用到了

system

属性,这个属性是在构造函数中被初始化的

class Talent {
    constructor(system) {
        this.#system = system;
    }



Talent

是在

Life

中构造的

class Life {
    constructor() {
        this.#property = new Property(this);
        this.#event = new Event(this);
        this.#talent = new Talent(this);
        this.#achievement = new Achievement(this);
        this.#character = new Character(this);
    }

所以

Talent



system

就是

Life


那这里调用的就是

Life.Function

class Life {
    Function = {
        CONDITION: 'CONDITION',
        UTIL: 'UTIL',
    }

最后是个字符串

CONDITION

外层是

Life.function

class Life {
    function(type) {
        switch (type) {
            case this.Function.CONDITION: return fCondition;
            case this.Function.UTIL: return util;
        }
    }

调用的是

fCondition

import * as fCondition from '../functions/condition.js';

于是看到 condition.js,其中

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;
}


/AGE\?\[([0-9\,]+)\]/

是正则表达式,

AGE\?\[([0-9\,]+)\]

是内容,

\?

是转义成问号字符,

\[

是转义成括号字符,

([0-9\,]+)

保存

[0-9\,]+

的搜索结果,

+

修饰代表至少一个,

[]

表示范围,范围是 数字 0 到 9 及逗号,那么

([0-9\,]+)

就是至少一个数字 0 到 9 及逗号,整个是表达式的结果是

AGE?[至少一个数字 0 到 9 及逗号]


这是获得的年龄的条件,然后以逗号分隔,返回年龄条件个数……没看懂,跳了

回到

Talent.initial()

,现在

emt

变成了这个函数

输入的

talents

是一个大表,对表中的每一个元素,重新赋

id



grade



max_triggers

属性

接下来看这个天赋应该被其他天赋置换



Event.initial()

,处理一下分支

然后就没有了……

那其实还是要看 talent 和 event 的 json 了

在这里插入图片描述

或许我最后应该找的是怎么解析他们约定好的字符串的地方

在这里插入图片描述

啊,最后就是别人的商业秘密了,混淆了

思考



3.总结

那看来现在唯一能做的就是再捋一捋了

每一年,增加一岁,先检查一次天赋,再检查一次事件,最后判断一次成就

检查天赋时,选择那些没有达到使用次数上限的天赋,判断是否满足天赋的生效条件,如果能生效,应用天赋的效果,给该天赋的使用次数加 1

检查事件时,判断事件是否有分支,没有分支则事件直接生效,有分支则判断是否满足分支事件的生效条件,满足则在本事件生效时也返回一个分支,然后递归检查这个分支事件

好像唯一看懂的代码就这些hhh



版权声明:本文为PriceCheap原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。