fledgling/src/scorer.ts

150 lines
4.6 KiB
TypeScript

import { VNScene } from "./vnscene.ts";
import { getPlayerProgress } from "./playerprogress.ts";
import { getSkills } from "./skills.ts";
import { Ending, SCORING_CATEGORIES, ScoringCategory } from "./datatypes.ts";
import {
sceneBat,
sceneCharm,
sceneLore,
sceneParty,
sceneStare,
sceneStealth,
} from "./endings.ts";
import { generateWishes, getWishes, isWishCompleted } from "./wishes.ts";
import { generateSuccessors } from "./successors.ts";
class Scorer {
constructor() {}
pickEnding(): Ending {
let learnedSkills = getPlayerProgress().getLearnedSkills();
let scores: Record<string, number> = {};
let itemsPurloined = getPlayerProgress().getItemsPurloined();
let vampiricSkills = 0;
let mortalServants = itemsPurloined / 10;
for (const skill of learnedSkills.values()) {
let data = getSkills().get(skill);
for (let [category, number] of Object.entries(data.governing.scoring)) {
scores[category] = (scores[category] ?? 0) + number;
}
mortalServants += data.governing.mortalServantValue;
vampiricSkills += 1;
}
mortalServants = Math.max(Math.floor(mortalServants), 0);
// NOTE: This approach isn't efficient but it's easy to understand
// and it allows me to arbitrate ties however I want
let runningScores: Record<string, number> = { ...scores };
const isMax = (cat: ScoringCategory, min: number) => {
let score = runningScores[cat] ?? 0;
runningScores[cat] = 0; // each category, once checked, can't disqualify any other category
if (score < min) {
return false;
}
for (let cat of SCORING_CATEGORIES.values()) {
if (runningScores[cat] > score) {
return false;
}
}
return true;
};
let scene: VNScene;
let rank: string;
let domicile: string;
let reignSentence: string;
let penance: boolean = false;
let successorVerb: string = "Appoint a Successor";
// Let the player
let wish = getPlayerProgress().getWish();
if (wish != null) {
let data = getWishes().get(wish);
if (isWishCompleted(wish)) {
scene = data.onVictory;
rank = data.profile.name;
domicile = data.profile.domicile;
reignSentence = data.profile.reignSentence;
} else {
scene = data.onFailure;
rank = data.profile.failureName;
domicile = data.profile.failureDomicile;
reignSentence = data.profile.failureReignSentence;
penance = true;
successorVerb = data.profile.failureSuccessorVerb;
}
}
// TODO: Award different ranks depending on second-to-top skill
// TODO: Award different domiciles based on overall score
// TODO: Force the rank to match the wish if one existed
else if (isMax("stare", 3)) {
scene = sceneStare;
rank = "Hypno-Chiropteran";
domicile = "Village of Brainwashed Mortals";
reignSentence = "You rule with a fair but unflinching gaze.";
} else if (isMax("lore", 3)) {
scene = sceneLore;
rank = "Loremaster";
domicile = "Vineyard";
reignSentence = "You're well on the path to ultimate knowledge.";
} else if (isMax("charm", 2)) {
scene = sceneCharm;
rank = "Seducer";
domicile = "Guest House";
reignSentence = "You get to sink your fangs into anyone you want.";
} else if (isMax("party", 1)) {
scene = sceneParty;
rank = "Party Animal";
domicile = "Nightclub";
reignSentence = "Everyone thinks you're too cool to disobey.";
} else if (isMax("stealth", 0)) {
scene = sceneStealth;
rank = "Invisible";
domicile = "Townhouse";
reignSentence = "People don't see you but they do most of what you want.";
}
// if (isMax("bat")) {
else {
scene = sceneBat;
rank = "Bat";
domicile = "Cave";
reignSentence =
"Your skreeking verdicts are irresistible to your subjects.";
}
// TODO: Analytics tracker
let analytics = {
itemsPurloined,
vampiricSkills,
mortalServants,
};
let successorOptions = generateSuccessors(0, penance); // TODO: generate nImprovements from mortalServants and the player's bsae improvements
let wishOptions = generateWishes(penance);
let progenerateVerb = penance ? "Repent" : "Progenerate";
return {
scene,
personal: {
rank,
domicile,
reignSentence,
successorVerb,
progenerateVerb,
},
analytics,
successorOptions,
wishOptions,
};
}
}
let active = new Scorer();
export function getScorer(): Scorer {
return active;
}