import { Character, Element, Gender, Realm, RealmMajor } from '../types/domain' import { Rng } from '../core/rng' import { ELEMENT_LIST, ROOT_GRADES } from '../data/elements' import { MAJORS } from '../data/realms' import { TRAIT_POOL, TRAITS } from '../data/traits' export function rollRoots(rng: Rng, parents?: { m?: Character; f?: Character }): { grade: number; primary: Element; secondary: Element[] } { let grade: number if (parents && parents.m && parents.f) { const mix = (parents.m.roots.grade + parents.f.roots.grade) / 2 const roll = rng.next() if (roll < 0.3) grade = Math.round(mix) else if (roll < 0.8) grade = Math.round(mix) + 1 else grade = Math.round(mix) - 1 if (rng.chance(0.15)) grade = Math.min(5, grade + 2) } else { const total = Object.entries(ROOT_GRADES).reduce((s, [k, v]) => s + v.drawWeight, 0) let r = rng.next() * total grade = 1 for (const [k, v] of Object.entries(ROOT_GRADES)) { r -= v.drawWeight if (r <= 0) { grade = Number(k) break } } } grade = Math.max(0, Math.min(5, grade)) const primaryCandidate = parents && (parents.m || parents.f) ? [parents.m!.roots.primary, parents.f!.roots.primary] : ELEMENT_LIST const primary = rng.pick(primaryCandidate) const secondaryCount = grade >= 3 ? rng.int(1, 2) : grade === 2 ? rng.int(0, 1) : 0 const rest = ELEMENT_LIST.filter((e) => e !== primary) const secondary = rng.shuffle(rest).slice(0, secondaryCount) return { grade, primary, secondary } } export function rollPersonality(rng: Rng): string[] { const n = rng.chance(0.5) ? 2 : 1 return rng.shuffle([...TRAIT_POOL]).slice(0, n) } export function rollAttributes(rng: Rng, base: number, variance: number): number { const v = Math.round(base + rng.between(-variance, variance)) return Math.max(1, Math.min(10, v)) } export function calcLifespan(major: RealmMajor, physique: number): number { const base = MAJORS[major].lifespan return Math.round(base * (0.9 + physique * 0.02)) } export function newCharacter( rng: Rng, opts: { name: string gender: Gender generation: number bornYear: number age: number mother?: Character father?: Character realm?: Realm isHead?: boolean isFounder?: boolean fortuneBase?: number } ): Character { const realm: Realm = opts.realm ?? { major: 'mortal', minor: 0 } const roots = rollRoots(rng, opts.father || opts.mother ? { m: opts.father, f: opts.mother } : undefined) const levelBonus = MAJORS[realm.major as RealmMajor].minorLayers > 0 ? realm.minor * 0.4 : 0 const perception = rollAttributes(rng, 4.5 + roots.grade * 0.8 + levelBonus * 0.25, 1.8) const physique = rollAttributes(rng, 4 + roots.grade * 0.5 + levelBonus * 0.3, 1.8) const mind = rollAttributes(rng, 4.5 + roots.grade * 0.3, 1.8) const charm = rollAttributes(rng, 4.5, 2.2) const fortune = rollAttributes(rng, (opts.fortuneBase ?? 5) + roots.grade * 0.4, 2.2) return { id: '', name: opts.name, gender: opts.gender, generation: opts.generation, children: [], bornYear: opts.bornYear, age: opts.age, realm, realmProgress: 0, roots, perception, physique, mind, charm, fortune, traits: rollPersonality(rng), state: 'idle', health: 100, alive: true, isHead: opts.isHead, isFounder: opts.isFounder } } export function traitBonuses(character: Character): { exp: number; breakBonus: number; windBonus: number; charmBonus: number; priceMult: number } { let exp = 0 let breakBonus = 0 let windBonus = 0 let charmBonus = 0 let priceMult = 0 for (const t of character.traits) { const def = TRAITS[t] if (!def) continue exp += def.expBonus ?? 0 breakBonus += def.breakBonus ?? 0 windBonus += def.windBonus ?? 0 charmBonus += def.charmBonus ?? 0 priceMult += def.priceMult ?? 0 } return { exp: 1 + exp, breakBonus, windBonus, charmBonus, priceMult } }