import {ALL_STATS, Skill, Stat, SuccessorOption, Wish} from "./datatypes.ts"; import {getSkills} from "./skills.ts"; export class PlayerProgress { #name: string #stats: Record #talents: Record #isInPenance: boolean; #wish: Wish | null; #exp: number; #blood: number #itemsPurloined: number #skillsLearned: number[] // use the raw ID representation for indexOf #untrimmedSkillsAvailable: Skill[] constructor(asSuccessor: SuccessorOption, withWish: Wish | null) { this.#name = asSuccessor.name; this.#stats = {...asSuccessor.stats}; this.#talents = {...asSuccessor.talents}; this.#isInPenance = asSuccessor.inPenance; this.#wish = withWish; this.#exp = 0; this.#blood = 0; this.#itemsPurloined = 0; this.#skillsLearned = [] this.#untrimmedSkillsAvailable = []; this.refill(); } applyEndOfTurn() { for (let stat of ALL_STATS.values()) { this.add(stat, this.#talents[stat]); } } get name(): string { return this.#name; } get isInPenance(): boolean { return this.#isInPenance; } refill() { this.#blood = 2000; let learnableSkills = []; // TODO: Also include costing info for (let skill of getSkills().getAvailableSkills(this.#isInPenance).values()) { if (this.#canBeAvailable(skill)) { learnableSkills.push(skill); } } this.#untrimmedSkillsAvailable = learnableSkills } hasLearned(skill: Skill) { return this.#skillsLearned.indexOf(skill.id) !== -1; } learnSkill(skill: Skill) { if (this.#skillsLearned.indexOf(skill.id) != -1) { return } this.#skillsLearned.push(skill.id); // remove entries for that skill let skills2 = []; for (let entry of this.#untrimmedSkillsAvailable.values()) { if (entry.id == skill.id) { continue; } skills2.push(entry); } this.#untrimmedSkillsAvailable = skills2; } #canBeAvailable(skill: Skill) { // make sure we haven't learned this skill already if (this.hasLearned(skill)) { return false; } let data = getSkills().get(skill); // make sure the prereqs are met for (let prereq of data.prereqs.values()) { if (!this.hasLearned(prereq)) { return false } } // ok, we're good!! return true; } purloinItem() { this.#itemsPurloined += 1; } getItemsPurloined() { return this.#itemsPurloined } add(stat: Stat, amount: number) { if (amount != Math.floor(amount)) { throw `stat increment must be integer: ${amount}` } this.#stats[stat] += amount; this.#stats[stat] = Math.min(Math.max(this.#stats[stat], -99), 999); } addExperience(amt: number) { this.#exp += amt; } getExperience(): number { return this.#exp } spendExperience(cost: number) { if (this.#exp < cost) { throw `can't spend ${cost}` } this.#exp -= cost; } getStat(stat: Stat): number { return this.#stats[stat] } getTalent(stat: Stat): number { return this.#talents[stat]; } getBlood(): number { return Math.floor(Math.max(this.#blood, 0)); } addBlood(amt: number) { this.#blood += amt; this.#blood = Math.min(this.#blood, 5000) } spendBlood(amt: number) { this.#blood -= amt; } getWish(): Wish | null { return this.#wish } getAvailableSkills(): Skill[] { // Sort by cost, then by name, then trim down to first 6 let skillsAvailable = [...this.#untrimmedSkillsAvailable]; skillsAvailable.sort((a, b) => { let name1 = getSkills().get(a).profile.name; let name2 = getSkills().get(b).profile.name; if (name1 < name2) { return -1; } if (name1 > name2) { return 1; } return 0; }); skillsAvailable.sort((a, b) => { return getSkills().computeCost(a) - getSkills().computeCost(b) }); return skillsAvailable.slice(0, 6) } getLearnedSkills() { let learnedSkills = [] for (let s of this.#skillsLearned.values()) { learnedSkills.push({id: s}) } return learnedSkills; } getStats() { return {...this.#stats} } getTalents() { return {...this.#talents} } } let active: PlayerProgress | null = null; export function initPlayerProgress(asSuccessor: SuccessorOption, withWish: Wish | null){ active = new PlayerProgress(asSuccessor, withWish); } export function getPlayerProgress(): PlayerProgress { if (active == null) { throw `trying to get player progress before it has been initialized` } return active }