import {getPartLocation, withCamera} from "./layout.ts"; import {AlignX, Point, Rect, Size} from "./engine/datatypes.ts"; import {DrawPile} from "./drawpile.ts"; import {D} from "./engine/public.ts"; import {BG_INSET, FG_BOLD, FG_TEXT} from "./colors.ts"; import {addButton} from "./button.ts"; import { getSkills, } from "./skills.ts"; import {getPlayerProgress} from "./playerprogress.ts"; import {Skill, SkillData} from "./datatypes.ts"; export class SkillsModal { #drawpile: DrawPile; #shown: boolean; #skillSelection: Skill | null; constructor() { this.#drawpile = new DrawPile(); this.#shown = false; this.#skillSelection = null; } get #size(): Size { // Instead of calculating this here, compute it from outside // as it has to be the same for every bottom modal return getPartLocation("BottomModal").size } get isShown(): boolean { return this.#shown; } setShown(shown: boolean) { this.#shown = shown } update() { withCamera("BottomModal", () => this.#update()) } draw() { withCamera("BottomModal", () => this.#draw()) } #update() { this.#drawpile.clear(); let size = this.#size this.#drawpile.add(0, () => { D.fillRect(new Point(-4, -4), size.add(new Size(8, 8)), BG_INSET) }) // draw skills let availableSkills = getPlayerProgress().getAvailableSkills(); this.#fixSkillSelection(availableSkills); let y = 0; for (let skill of availableSkills) { let data = getSkills().get(skill); let cost = getSkills().computeCost(skill); let y_ = y; let selected = this.#skillSelection?.id == skill.id; let skillRect = new Rect(new Point(0, y_), new Size(160 + 4, 16)); let enabled = true; this.#drawpile.addClickable( 0, (hover) => { // two column layout let [bg, fg] = [BG_INSET, FG_TEXT]; if (selected || hover) { [bg, fg] = [FG_BOLD, BG_INSET]; } D.fillRect(skillRect.top, skillRect.size, bg); D.drawText(data.profile.name, new Point(4, y_), fg); D.drawText("" + cost, new Point(160 - 4, y_), fg, {alignX: AlignX.Right}); }, skillRect, enabled, () => { this.#skillSelection = skill; } ) y += 16; } // add skill description let selection = this.#skillSelection; if (selection != null) { let data = getSkills().get(selection); let cost = getSkills().computeCost(selection); let size = this.#size; let remainingWidth = size.w - 160; this.#drawpile.add(0, () => { D.fillRect(new Point(160, 0), new Size(remainingWidth, 96), FG_BOLD) D.drawText(createFullDescription(data), new Point(164, 0), BG_INSET, {forceWidth: remainingWidth - 8}); }); // add learn button let drawButtonRect = new Rect(new Point(160, 96), new Size(remainingWidth, 32)) let canAfford = getPlayerProgress().getExperience() >= cost; let caption = `Learn ${data.profile.name}` if (!canAfford) { caption = `Can't Afford`; } addButton(this.#drawpile, caption, drawButtonRect, canAfford, () => { getPlayerProgress().spendExperience(cost); getPlayerProgress().learnSkill(selection); }) } // add close button let closeRect = new Rect(new Point(0, 96), new Size(160, 32)) addButton(this.#drawpile, "Back", closeRect, true, () => { this.setShown(false); }) this.#drawpile.executeOnClick(); } #fixSkillSelection(skills: Skill[]) { // if we have selected a skill that is really available, // that's fine for (let s of skills.values()) { if (s.id == this.#skillSelection?.id) { return; } } // select the first skill if one exists if (skills.length == 0) { this.#skillSelection = null; } else { this.#skillSelection = skills[0]; } } #draw() { this.#drawpile.draw(); } } let active = new SkillsModal(); export function getSkillsModal(): SkillsModal { return active; } function createFullDescription(data: SkillData) { return data.profile.description + "\n\n" + data.governing.note }