212 lines
5.8 KiB
TypeScript
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;
|
|
}
|
|
}
|