fledgling/src/newmap.ts
2025-02-09 22:37:33 -08:00

212 lines
5.8 KiB
TypeScript

import {Resource, Skill, Stat} from "./datatypes.ts";
import {Grid, Point, Size} from "./engine/datatypes.ts";
import {choose} from "./utils.ts";
import {VNScene} from "./vnscene.ts";
export type Province = "a" | "b" | "c";
export type Check = "1" | "2";
export type Progress = Stat | Resource
export type Pickup = Progress; // TODO: Items
export type NewMapInput = {
id: string,
data: {
architecture: string,
provinces: string,
}
pickups: {
"*"?: string,
stat: {primary: Stat, secondary: Stat},
"!"?: string,
},
provinces: Record<Province, string>,
checks: Record<Check, CheckData>,
}
export type CheckData = {
label: string,
options: CheckDataOption[],
}
export type CheckDataOption = {
skills: () => Skill[],
locked: string,
unlockable: string,
unlockScene: VNScene,
}
export enum Architecture { Wall, Floor }
export class LoadedNewMap {
#id: string
#size: Size
#entrance: Point | null
#architecture: Grid<Architecture>
#pickups: Grid<Pickup | null>
#provinces: Grid<string | null>
#checks: Grid<CheckData | null>
#revealed: Grid<boolean>
constructor(id: string, size: Size) {
this.#id = id;
this.#size = size;
this.#entrance = null;
this.#architecture = new Grid<Architecture>(size, () => Architecture.Wall);
this.#pickups = new Grid<Pickup | null>(size, () => null);
this.#provinces = new Grid<string | null>(size, () => null);
this.#checks = new Grid<CheckData | null>(size, () => null);
this.#revealed = new Grid<boolean>(size, () => false);
}
set entrance(point: Point) {
this.#entrance = point;
}
get entrance(): Point {
if (this.#entrance == null) {
throw `${this.#id}: this.#entrance was never initialized`
}
return this.#entrance;
}
get size(): Size {
return this.#size;
}
get(point: Point): CellView {
return new CellView(this, point)
}
setArchitecture(point: Point, value: Architecture) {
this.#architecture.set(point, value);
}
getArchitecture(point: Point): Architecture {
return this.#architecture.get(point);
}
setPickup(point: Point, value: Pickup | null) {
this.#pickups.set(point, value);
}
getPickup(point: Point): Pickup | null {
return this.#pickups.get(point);
}
setProvince(point: Point, value: string | null) {
this.#provinces.set(point, value);
}
getProvince(point: Point): string | null {
return this.#provinces.get(point);
}
setCheck(point: Point, value: CheckData | null) {
this.#checks.set(point, value);
}
getCheck(point: Point): CheckData | null {
return this.#checks.get(point);
}
setRevealed(point: Point, value: boolean) {
this.#revealed.set(point, value)
}
getRevealed(point: Point): boolean {
return this.#revealed.get(point);
}
}
export class CellView {
#map: LoadedNewMap
#point: Point
constructor(map: LoadedNewMap, point: Point) {
this.#map = map;
this.#point = point;
}
set architecture(value: Architecture) { this.#map.setArchitecture(this.#point, value) }
get architecture(): Architecture { return this.#map.getArchitecture(this.#point) }
set pickup(value: Pickup | null) { this.#map.setPickup(this.#point, value) }
get pickup(): Pickup | null { return this.#map.getPickup(this.#point) }
set province(value: string | null) { this.#map.setProvince(this.#point, value) }
get province(): string | null { return this.#map.getProvince(this.#point) }
set check(value: CheckData | null) { this.#map.setCheck(this.#point, value) }
get check(): CheckData | null { return this.#map.getCheck(this.#point) }
set revealed(value: boolean) { this.#map.setRevealed(this.#point, value) }
get revealed(): boolean { return this.#map.getRevealed(this.#point) }
copyFrom(cell: CellView) {
this.architecture = cell.architecture;
this.pickup = cell.pickup;
this.province = cell.province;
this.check = cell.check;
this.revealed = cell.revealed;
}
}
export type NewMap = () => LoadedNewMap;
export function compileNewMap(input: NewMapInput): NewMap {
let {architecture: architectureInput, provinces: provincesInput} = input.data;
let architecture = Grid.createGridFromMultilineString(architectureInput);
let provinces = Grid.createGridFromMultilineString(provincesInput);
let size = architecture.size;
if (!size.equals(provinces.size)) {
throw `${input.id}: malformed, wrong province size (${provinces.size})`;
}
return () => {
let map = new LoadedNewMap(input.id, size);
for (let y = 0; y < size.h; y++) {
for (let x = 0; x < size.w; x++) {
let xy = new Point(x, y);
let cell = map.get(xy);
// set up the wall
let arch = architecture.get(xy);
cell.architecture = Architecture.Floor;
if (arch == "#") {
cell.architecture = Architecture.Wall;
} else if (arch == "@") {
map.entrance = xy;
} else if (arch == " " || arch == "-") {
}
// player resources: pickups
else if (arch == ".") {
let stat = choose([
input.pickups.stat.primary,
input.pickups.stat.primary,
input.pickups.stat.secondary
])
cell.pickup = choose<Progress>([stat, stat, stat, "EXP"]);
} else if (arch == "*") {
// TODO: Common item
} else if (arch == "!") {
// TODO: Artifact
}
// stat checks
else if (input.checks.hasOwnProperty(arch)) {
cell.check = input.checks[arch as Check];
} else {
throw `${input.id}: unrecognized architecture cell: ${arch}`
}
// set province
let provinceId = provinces.get(xy);
if (input.provinces.hasOwnProperty(provinceId)) {
cell.province = input.provinces[provinceId as Province];
}
}
}
return map;
}
}