import { clone, weightRandom } 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; async initial(loadJSON) { const [age, talents, events, achievements] = await Promise.all([ loadJSON('age'), loadJSON('talents'), loadJSON('events'), loadJSON('achievement'), ]); this.#property.initial({age}); this.#talent.initial({talents}); this.#event.initial({events}); this.#achievement.initial({achievements}); } 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 } = {}) { this.#defaultPropertyPoints = defaultPropertyPoints; this.#talentSelectLimit = talentSelectLimit; this.#propertyAllocateLimit = propertyAllocateLimit; this.#defaultPropertys = defaultPropertys; this.#talent.config(talentConfig); } restart(allocation) { const propertys = clone(this.#defaultPropertys); for(const key in allocation) { propertys[key] = clone(allocation[key]); } this.#triggerTalents = {}; const contents = this.talentReplace(propertys.TLT); this.#property.restart(propertys); this.doTalent() this.#property.restartLastStep(); this.#achievement.achieve( this.#achievement.Opportunity.START, this.#property ) return contents; } getPropertyPoints(selectedTalentIds) { return this.#defaultPropertyPoints + this.#talent.allocationAddition(selectedTalentIds); } 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.#achievement.Opportunity.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.#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) { return weightRandom( events.filter( ([eventId])=>this.#event.check(eventId, this.#property) ) ); } talentRandom() { const times = this.#property.get(this.#property.TYPES.TMS); const achievement = this.#property.get(this.#property.TYPES.CACHV); return this.#talent.talentRandom(this.getLastExtendTalent(), { times, achievement }); } talentExtend(talentId) { this.#property.set(this.#property.TYPES.EXT, talentId); } getLastExtendTalent() { return this.#property.get(this.#property.TYPES.EXT); } getSummary() { this.#achievement.achieve( this.#achievement.Opportunity.SUMMARY, this.#property ) return { AGE: this.#property.get(this.#property.TYPES.HAGE), CHR: this.#property.get(this.#property.TYPES.HCHR), INT: this.#property.get(this.#property.TYPES.HINT), STR: this.#property.get(this.#property.TYPES.HSTR), MNY: this.#property.get(this.#property.TYPES.HMNY), SPR: this.#property.get(this.#property.TYPES.HSPR), SUM: this.#property.get(this.#property.TYPES.SUM), }; } getLastRecord() { return this.#property.getLastRecord(); } exclusive(talents, exclusive) { return this.#talent.exclusive(talents, exclusive); } getAchievements() { const ticks = {}; this.#property .get(this.#property.TYPES.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; }); } getTotal() { const TMS = this.#property.get(this.#property.TYPES.TMS); const CACHV = this.#property.get(this.#property.TYPES.CACHV); const CTLT = this.#property.get(this.#property.TYPES.CTLT); const CEVT = this.#property.get(this.#property.TYPES.CEVT); const totalTalent = this.#talent.count(); const totalEvent = this.#event.count(); return { times: TMS, achievement: CACHV, talentRate: CTLT / totalTalent, eventRate: CEVT / totalEvent, } } get PropertyTypes() { return clone(this.#property.TYPES); } get talentSelectLimit() { return this.#talentSelectLimit; } get propertyAllocateLimit() { return clone(this.#propertyAllocateLimit); } get propertys() { return this.#property.getPropertys(); } get times() { return this.#property?.get(this.#property.TYPES.TMS) || 0; } set times(v) { this.#property?.set(this.#property.TYPES.TMS, v) || 0; this.#achievement.achieve( this.#achievement.Opportunity.END, this.#property ) } } export default Life;