fledgling/src/playerprogress.ts
2025-02-08 23:51:15 -08:00

191 lines
4.4 KiB
TypeScript

import {ALL_STATS, Skill, Stat, SuccessorOption, Wish} from "./datatypes.ts";
import {getSkills} from "./skills.ts";
export class PlayerProgress {
#name: string
#stats: Record<Stat, number>
#talents: Record<Stat, number>
#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
}