From a6120944b9d923e18fe4ea6474403ca83e80895c Mon Sep 17 00:00:00 2001 From: Roger Ostrander Date: Sat, 15 Jul 2017 15:28:38 -0400 Subject: [PATCH] Added type definitions for rot-js --- types/rot-js/index.d.ts | 722 ++++++++++++++++++++++ types/rot-js/rot-js-tests.ts | 1119 ++++++++++++++++++++++++++++++++++ types/rot-js/tsconfig.json | 23 + types/rot-js/tslint.json | 1 + 4 files changed, 1865 insertions(+) create mode 100644 types/rot-js/index.d.ts create mode 100644 types/rot-js/rot-js-tests.ts create mode 100644 types/rot-js/tsconfig.json create mode 100644 types/rot-js/tslint.json diff --git a/types/rot-js/index.d.ts b/types/rot-js/index.d.ts new file mode 100644 index 0000000000..32a75dbc9f --- /dev/null +++ b/types/rot-js/index.d.ts @@ -0,0 +1,722 @@ +// Type definitions for rot-js 0.6 +// Project: https://github.com/ondras/rot.js +// Definitions by: Roger Ostrander +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +// Extensions (thanks, https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html ) +declare global { + interface Array { + random(): T; + randomize(): T[]; + } + + interface Number { + mod(n: number): number; + } + + // Added to support `String.format` + interface FormatFunc { + (...args: any[]): string; + map: { [key: string]: string }; + } + + interface StringConstructor { + format: FormatFunc; + } + + interface String { + capitalize(): string; + lpad(character?: string, count?: number): string; + rpad(character?: string, count?: number): string; + + format: FormatFunc; + } +} + +export function isSupported(): boolean; + +// Docs explicitly show examples of overriding these, but TS2 (and ES6) +// say that module-level vars are constant, so here we are: +export const DEFAULT_WIDTH: number; +export const DEFAULT_HEIGHT: number; + +/** Directional constants. Ordering is important! */ +export const DIRS: { + "4": Array<[number, number]>; + "6": Array<[number, number]>; + "8": Array<[number, number]>; +}; + +/** Cancel key. */ +export const VK_CANCEL: number; +/** Help key. */ +export const VK_HELP: number; +/** Backspace key. */ +export const VK_BACK_SPACE: number; +/** Tab key. */ +export const VK_TAB: number; +/** 5 key on Numpad when NumLock is unlocked. Or on Mac, clear key which is positioned at NumLock key. */ +export const VK_CLEAR: number; +/** Return/enter key on the main keyboard. */ +export const VK_RETURN: number; +/** Reserved, but not used. */ +export const VK_ENTER: number; +/** Shift key. */ +export const VK_SHIFT: number; +/** Control key. */ +export const VK_CONTROL: number; +/** Alt (Option on Mac) key. */ +export const VK_ALT: number; +/** Pause key. */ +export const VK_PAUSE: number; +/** Caps lock. */ +export const VK_CAPS_LOCK: number; +/** Escape key. */ +export const VK_ESCAPE: number; +/** Space bar. */ +export const VK_SPACE: number; +/** Page Up key. */ +export const VK_PAGE_UP: number; +/** Page Down key. */ +export const VK_PAGE_DOWN: number; +/** End key. */ +export const VK_END: number; +/** Home key. */ +export const VK_HOME: number; +/** Left arrow. */ +export const VK_LEFT: number; +/** Up arrow. */ +export const VK_UP: number; +/** Right arrow. */ +export const VK_RIGHT: number; +/** Down arrow. */ +export const VK_DOWN: number; +/** Print Screen key. */ +export const VK_PRINTSCREEN: number; +/** Ins(ert) key. */ +export const VK_INSERT: number; +/** Del(ete) key. */ +export const VK_DELETE: number; +/***/ +export const VK_0: number; +/***/ +export const VK_1: number; +/***/ +export const VK_2: number; +/***/ +export const VK_3: number; +/***/ +export const VK_4: number; +/***/ +export const VK_5: number; +/***/ +export const VK_6: number; +/***/ +export const VK_7: number; +/***/ +export const VK_8: number; +/***/ +export const VK_9: number; +/** Colon (:) key. Requires Gecko 15.0 */ +export const VK_COLON: number; +/** Semicolon (;) key. */ +export const VK_SEMICOLON: number; +/** Less-than (<) key. Requires Gecko 15.0 */ +export const VK_LESS_THAN: number; +/** Equals (=) key. */ +export const VK_EQUALS: number; +/** Greater-than (>) key. Requires Gecko 15.0 */ +export const VK_GREATER_THAN: number; +/** Question mark (?) key. Requires Gecko 15.0 */ +export const VK_QUESTION_MARK: number; +/** Atmark (@) key. Requires Gecko 15.0 */ +export const VK_AT: number; +/***/ +export const VK_A: number; +/***/ +export const VK_B: number; +/***/ +export const VK_C: number; +/***/ +export const VK_D: number; +/***/ +export const VK_E: number; +/***/ +export const VK_F: number; +/***/ +export const VK_G: number; +/***/ +export const VK_H: number; +/***/ +export const VK_I: number; +/***/ +export const VK_J: number; +/***/ +export const VK_K: number; +/***/ +export const VK_L: number; +/***/ +export const VK_M: number; +/***/ +export const VK_N: number; +/***/ +export const VK_O: number; +/***/ +export const VK_P: number; +/***/ +export const VK_Q: number; +/***/ +export const VK_R: number; +/***/ +export const VK_S: number; +/***/ +export const VK_T: number; +/***/ +export const VK_U: number; +/***/ +export const VK_V: number; +/***/ +export const VK_W: number; +/***/ +export const VK_X: number; +/***/ +export const VK_Y: number; +/***/ +export const VK_Z: number; +/***/ +export const VK_CONTEXT_MENU: number; +/** 0 on the numeric keypad. */ +export const VK_NUMPAD0: number; +/** 1 on the numeric keypad. */ +export const VK_NUMPAD1: number; +/** 2 on the numeric keypad. */ +export const VK_NUMPAD2: number; +/** 3 on the numeric keypad. */ +export const VK_NUMPAD3: number; +/** 4 on the numeric keypad. */ +export const VK_NUMPAD4: number; +/** 5 on the numeric keypad. */ +export const VK_NUMPAD5: number; +/** 6 on the numeric keypad. */ +export const VK_NUMPAD6: number; +/** 7 on the numeric keypad. */ +export const VK_NUMPAD7: number; +/** 8 on the numeric keypad. */ +export const VK_NUMPAD8: number; +/** 9 on the numeric keypad. */ +export const VK_NUMPAD9: number; +/** * on the numeric keypad. */ +export const VK_MULTIPLY: number; +/** + on the numeric keypad. */ +export const VK_ADD: number; +/***/ +export const VK_SEPARATOR: number; +/** - on the numeric keypad. */ +export const VK_SUBTRACT: number; +/** Decimal point on the numeric keypad. */ +export const VK_DECIMAL: number; +/** / on the numeric keypad. */ +export const VK_DIVIDE: number; +/** F1 key. */ +export const VK_F1: number; +/** F2 key. */ +export const VK_F2: number; +/** F3 key. */ +export const VK_F3: number; +/** F4 key. */ +export const VK_F4: number; +/** F5 key. */ +export const VK_F5: number; +/** F6 key. */ +export const VK_F6: number; +/** F7 key. */ +export const VK_F7: number; +/** F8 key. */ +export const VK_F8: number; +/** F9 key. */ +export const VK_F9: number; +/** F10 key. */ +export const VK_F10: number; +/** F11 key. */ +export const VK_F11: number; +/** F12 key. */ +export const VK_F12: number; +/** F13 key. */ +export const VK_F13: number; +/** F14 key. */ +export const VK_F14: number; +/** F15 key. */ +export const VK_F15: number; +/** F16 key. */ +export const VK_F16: number; +/** F17 key. */ +export const VK_F17: number; +/** F18 key. */ +export const VK_F18: number; +/** F19 key. */ +export const VK_F19: number; +/** F20 key. */ +export const VK_F20: number; +/** F21 key. */ +export const VK_F21: number; +/** F22 key. */ +export const VK_F22: number; +/** F23 key. */ +export const VK_F23: number; +/** F24 key. */ +export const VK_F24: number; +/** Num Lock key. */ +export const VK_NUM_LOCK: number; +/** Scroll Lock key. */ +export const VK_SCROLL_LOCK: number; +/** Circumflex (^) key. Requires Gecko 15.0 */ +export const VK_CIRCUMFLEX: number; +/** Exclamation (!) key. Requires Gecko 15.0 */ +export const VK_EXCLAMATION: number; +/** Double quote () key. Requires Gecko 15.0 */ +export const VK_DOUBLE_QUOTE: number; +/** Hash (#) key. Requires Gecko 15.0 */ +export const VK_HASH: number; +/** Dollar sign ($) key. Requires Gecko 15.0 */ +export const VK_DOLLAR: number; +/** Percent (%) key. Requires Gecko 15.0 */ +export const VK_PERCENT: number; +/** Ampersand (&) key. Requires Gecko 15.0 */ +export const VK_AMPERSAND: number; +/** Underscore (_) key. Requires Gecko 15.0 */ +export const VK_UNDERSCORE: number; +/** Open parenthesis (() key. Requires Gecko 15.0 */ +export const VK_OPEN_PAREN: number; +/** Close parenthesis ()) key. Requires Gecko 15.0 */ +export const VK_CLOSE_PAREN: number; +/* Asterisk (*) key. Requires Gecko 15.0 */ +export const VK_ASTERISK: number; +/** Plus (+) key. Requires Gecko 15.0 */ +export const VK_PLUS: number; +/** Pipe (|) key. Requires Gecko 15.0 */ +export const VK_PIPE: number; +/** Hyphen-US/docs/Minus (-) key. Requires Gecko 15.0 */ +export const VK_HYPHEN_MINUS: number; +/** Open curly bracket ({) key. Requires Gecko 15.0 */ +export const VK_OPEN_CURLY_BRACKET: number; +/** Close curly bracket (}) key. Requires Gecko 15.0 */ +export const VK_CLOSE_CURLY_BRACKET: number; +/** Tilde (~) key. Requires Gecko 15.0 */ +export const VK_TILDE: number; +/** Comma (,) key. */ +export const VK_COMMA: number; +/** Period (.) key. */ +export const VK_PERIOD: number; +/** Slash (/) key. */ +export const VK_SLASH: number; +/** Back tick (`) key. */ +export const VK_BACK_QUOTE: number; +/** Open square bracket ([) key. */ +export const VK_OPEN_BRACKET: number; +/** Back slash (\) key. */ +export const VK_BACK_SLASH: number; +/** Close square bracket (]) key. */ +export const VK_CLOSE_BRACKET: number; +/** Quote (''') key. */ +export const VK_QUOTE: number; +/** Meta key on Linux, Command key on Mac. */ +export const VK_META: number; +/** AltGr key on Linux. Requires Gecko 15.0 */ +export const VK_ALTGR: number; +/** Windows logo key on Windows. Or Super or Hyper key on Linux. Requires Gecko 15.0 */ +export const VK_WIN: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_KANA: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_HANGUL: number; +/** 英数 key on Japanese Mac keyboard. Requires Gecko 15.0 */ +export const VK_EISU: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_JUNJA: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_FINAL: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_HANJA: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_KANJI: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_CONVERT: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_NONCONVERT: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_ACCEPT: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_MODECHANGE: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_SELECT: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_PRINT: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_EXECUTE: number; +/** Linux support for this keycode was added in Gecko 4.0. */ +export const VK_SLEEP: number; + +// Added to support `measure` +export interface Size { + width: number; + height: number; +} + +export namespace Text { + const RE_COLORS: RegExp; + + /* token types */ + const TYPE_BG: number; + const TYPE_FG: number; + const TYPE_NEWLINE: number; + const TYPE_TEXT: number; + + function measure(str: string, maxWidth: number): Size; + + // Added to support `tokenize` + interface Token { + type: number; + value?: string; + } + + function tokenize(str: string, maxWidth: number): Token[]; +} + +// Duplication is so that we can support chaining like ROT does, +// but also use ROT.RNG like a normal namespace +export interface RNGable { + getSeed(): number; + setSeed(seed: number): RNGable; + getUniform(): number; + getUniformInt(lowerBound: number, upperBound: number): number; + getNormal(mean?: number, stddev?: number): number; + getPercentage(): number; + getWeightedValue(data: { T: number }): T; + getState(): [number, number, number, number]; + setState(state: [number, number, number, number]): RNGable; + clone(): RNGable; +} + +export namespace RNG { + function getSeed(): number; + function setSeed(seed: number): RNGable; + function getUniform(): number; + function getUniformInt(lowerBound: number, upperBound: number): number; + function getNormal(mean?: number, stddev?: number): number; + function getPercentage(): number; + // Can't do the following because index has to explicitly be strings + // or numbers, see https://github.com/Microsoft/TypeScript/issues/5683 + // function getWeightedValue(data: {[key: T]: number}): T; + // Provide a type-safe version for strings, at least: + function getWeightedValue(data: { [key: string]: number }): string; + // And everything else: + function getWeightedValue(data: any): any; + function getState(): [number, number, number, number]; + function setState(state: [number, number, number, number]): RNGable; + function clone(): RNGable; +} + +export interface DisplayOptions { + width?: number; + height?: number; + transpose?: boolean; + fontSize?: number; + fontFamily?: string; + fontStyle?: string; + fg?: string; + bg?: string; + spacing?: number; + border?: number; + layout?: string; + forceSquareRatio?: boolean; + tileWidth?: number; + tileHeight?: number; + tileMap?: any; + tileSet?: any; + tileColorize?: boolean; + termColor?: string; +} + +export class Display { + DEBUG: DigCallback; + + constructor(options?: DisplayOptions); + clear(): void; + computeSize(availWidth: number, availHeight: number): [number, number]; + computeFontSize(availWidth: number, availHeight: number): number; + draw(x: number, y: number, character: string | string[], fg?: string, bg?: string): void; + drawText(x: number, y: number, text: string, maxWidth?: number): number; + eventToPosition(e: UIEvent): [number, number] | number; + getContainer(): Node; + getOptions(): DisplayOptions; + setOptions(options: DisplayOptions): Display; +} + +// Map callbacks +export type DigCallback = (x: number, y: number, cellValue: number) => any; +export type DoorCallback = (x: number, y: number) => any; +export type CanBeDugCallback = (x: number, y: number) => boolean; +export type IsWallCallback = (x: number, y: number) => boolean; + +// Map +export class Map { + constructor(width: number, height: number); + create(callback?: DigCallback): Map; +} + +export interface CellularOptions { + born?: number[]; + survive?: number[]; + topology?: number; + // Docs say this exists, even though it's never used: + connected?: boolean; +} + +export interface DiggerOptions { + roomWidth?: [number, number]; + roomHeight?: [number, number]; + corridorLength?: [number, number]; + dugPercentage?: number; + timeLimit?: number; +} + +export interface UniformOptions { + roomWidth?: [number, number]; + roomHeight?: [number, number]; + roomDugPercentage?: number; + timeLimit?: number; +} + +export interface RogueOptions { + cellWidth?: number; + cellHeight?: number; + roomWidth?: [number, number]; + roomHeight?: [number, number]; +} + +export interface RoomOptions { + roomWidth?: [number, number]; + roomHeight?: [number, number]; +} + +export interface CorridorOptions { + corridorLength: [number, number]; +} +export type FeatureOptions = RoomOptions | CorridorOptions; + +export namespace Map { + class Arena extends Map { } + class DividedMaze extends Map { } + class IceyMaze extends Map { + constructor(width?: number, height?: number, regularity?: number); + } + class EllerMaze extends Map { } + class Cellular extends Map { + constructor(width?: number, height?: number, options?: CellularOptions); + randomize(probability: number): this; + setOptions(options: CellularOptions): void; + set(x: number, y: number, value: any): void; + serviceCallback(callback: DigCallback): void; + connect(callback: DigCallback, value?: number, connectionCallback?: DoorCallback): void; + } + class Dungeon extends Map { + getRooms(): Map.Feature.Room[]; + getCorridors(): Map.Feature.Corridor[]; + } + + class Digger extends Dungeon { + constructor(width?: number, height?: number, options?: DiggerOptions); + } + + class Uniform extends Dungeon { + constructor(width?: number, height?: number, options?: UniformOptions); + } + + class Rogue extends Map { + constructor(width?: number, height?: number, options?: RogueOptions); + } + + class Feature { + isValid(canBeDugCallback: CanBeDugCallback): boolean; + create(digCallback: DigCallback): void; + debug(): void; + static createRandomAt(x: number, y: number, dx: number, dy: number, options?: FeatureOptions): Feature; + } + + namespace Feature { + class Room { + constructor(x1: number, y1: number, x2: number, y2: number, doorX?: number, doorY?: number); + + static createRandom(availWidth: number, availHeight: number, options?: RoomOptions): Room; + static createRandomAt(x: number, y: number, dx: number, dy: number, options?: RoomOptions): Room; + static createRandomCenter(cx: number, cy: number, options?: RoomOptions): Room; + + create(digCallback: DigCallback): void; + debug(): void; + + addDoor(x: number, y: number): this; + getDoors(callback: DoorCallback): this; + clearDoors(): this; + addDoors(isWallCallback: IsWallCallback): this; + isValid(isWallCallback: IsWallCallback, canBeDugCallback: CanBeDugCallback): boolean; + getCenter(): [number, number]; + + getLeft(): number; + getRight(): number; + getTop(): number; + getBottom(): number; + } + + class Corridor { + constructor(startX: number, startY: number, endX: number, endY: number); + + static createRandomAt(x: number, y: number, dx: number, dy: number, options?: CorridorOptions): Corridor; + + create(digCallback: DigCallback): boolean; + debug(): void; + + isValid(isWallCallback: IsWallCallback, canBeDugCallback: CanBeDugCallback): boolean; + createPriorityWalls(priorityWallCallback: DoorCallback): void; + } + } +} + +// FOV callbacks +export type LightPassesCallback = (x: number, y: number) => boolean; +export type FOVCallback = (x: number, y: number, R: number, visibility: number) => any; + +export interface FOVOptions { + topology?: number; +} + +export class FOV { + constructor(lightPassesCallback: LightPassesCallback, options?: FOVOptions); + compute(x: number, y: number, R: number, callback: FOVCallback): void; +} +export namespace FOV { + class DiscreteShadowcasting extends FOV { } + class PreciseShadowcasting extends FOV { } + + class RecursiveShadowcasting extends FOV { + compute180(x: number, y: number, R: number, dir: number, callback: FOVCallback): void; + compute90(x: number, y: number, R: number, dir: number, callback: FOVCallback): void; + } + namespace RecursiveShadowcasting { + const OCTANTS: Array<[number, number, number, number]>; + } +} + +export type ColorArray = [number, number, number]; + +export namespace Color { + function fromString(str: string): [number, number, number]; + function add(color1: [number, number, number], ...colors2: Array<[number, number, number]>): [number, number, number]; + function add_(color1: [number, number, number], ...colors2: Array<[number, number, number]>): [number, number, number]; + function multiply(color1: [number, number, number], ...colors2: Array<[number, number, number]>): [number, number, number]; + function multiply_(color1: [number, number, number], ...colors2: Array<[number, number, number]>): [number, number, number]; + function interpolate(color1: [number, number, number], color2: [number, number, number], factor: number): [number, number, number]; + function interpolateHSL(color1: [number, number, number], color2: [number, number, number], factor: number): [number, number, number]; + function randomize(color: [number, number, number], diff: number | [number, number, number]): [number, number, number]; + function rgb2hsl(color: [number, number, number]): [number, number, number]; + function hsl2rgb(color: [number, number, number]): [number, number, number]; + function toRGB(color: [number, number, number]): string; + function toHex(color: [number, number, number]): string; +} + +// Lighting callbacks +export type ReflectivityCallback = (x: number, y: number) => number; +export type LightingCallback = (x: number, y: number, color: [number, number, number]) => any; + +export interface LightingOptions { + passes?: number; + emissionThreshold?: number; + range?: number; +} + +export class Lighting { + constructor(reflectivityCallback: ReflectivityCallback, options?: LightingOptions); + + setOptions(options: LightingOptions): this; + setFOV(fov: FOV): this; + setLight(x: number, y: number, color: string | [number, number, number]): this; + clearLights(): void; + reset(): this; + compute(lightingCallback: LightingCallback): this; +} + +// Path callbacks +export type PassableCallback = (x: number, y: number) => boolean; +export type PathCallback = (x: number, y: number) => any; + +export interface PathOptions { + topology?: number; +} + +export class Path { + constructor(toX: number, toY: number, passableCallback: PassableCallback, options?: PathOptions); + + compute(fromX: number, fromY: number, callback: PathCallback): void; +} + +export namespace Path { + class Dijkstra extends Path { } + class AStar extends Path { } +} + +export interface Noise { + get(x: number, y: number): number; +} +export namespace Noise { + class Simplex implements Noise { + constructor(gradients?: number); + get(xin: number, yin: number): number; + } +} + +export class EventQueue { + getTime(): number; + clear(): void; + add(event: T, time: number): void; + get(): T; + getEventTime(event: T): number; + remove(event: T): boolean; + remove(index: number): boolean; +} + +export class Scheduler { + getTime(): number; + add(item: any, repeat: boolean, time?: number): Scheduler; + getTimeOf(item: any): number; + clear(): Scheduler; + remove(item: any): any; + next(): any; +} + +export namespace Scheduler { + class Simple extends Scheduler { } + class Speed extends Scheduler { } + class Action extends Scheduler { + setDuration(time: number): Action; + } +} + +export class Engine { + constructor(scheduler: Scheduler); + start(): Engine; + lock(): Engine; + unlock(): Engine; +} + +export interface StringGeneratorOptions { + words?: boolean; + order?: number; + prior?: number; +} + +export class StringGenerator { + constructor(options?: StringGeneratorOptions); + clear(): void; + generate(): string; + observe(str: string): void; + getStats(): string; +} diff --git a/types/rot-js/rot-js-tests.ts b/types/rot-js/rot-js-tests.ts new file mode 100644 index 0000000000..bef1de16d3 --- /dev/null +++ b/types/rot-js/rot-js-tests.ts @@ -0,0 +1,1119 @@ +import * as ROT from 'rot-js'; + +// All the documentation uses a function called `SHOW` to do results; stubbing it out here +// so that I can copy/paste directly. +function SHOW(...something: any[]): any { + console.log(something); +} + +let w = 80; +let h = 40; +let display: ROT.Display; + +// Introduction / Browser Support + +SHOW( + ROT.isSupported() +); + +// JS Enhancements / Array + +SHOW( + ["apples", "oranges", "zombies"].random(), + ["apples", "oranges", "zombies"].randomize() +); + +// JS Enhancements / String + +SHOW( + "hello world".capitalize(), + "hello %s, this is %s".format("world", "sparta"), + "7".lpad("0", 3), + "123".rpad(".", 6) +); + +// JS Enhancements / Number + +SHOW( + (15) % 7, + (-15) % 7, + (15).mod(7), + (-15).mod(7) +); + +// JS Enhancements / (Object | Function) not included, as TS does that for us + +// JS Enhancements / String formatting +SHOW( + String.format("%s %s", "hello", "world"), + "%s %s".format("hello", "world") +); +// ---- + +let myObj = { + foo() { return "bar"; } +}; +String.format.map['f'] = "foo"; + +SHOW("%f".format(myObj)); +// ---- + +class Item { + constructor(private name: string) { } + + a() { + let first = self.name.charAt(0); + return (first.match(/[aeiouy]/i) ? "an" : "a") + " " + this.name; + } + + the() { + return `the ${this.name}`; + } +} + +String.format.map['a'] = "a"; +String.format.map['the'] = "the"; + +let apple = new Item("apple"); +let banana = new Item("banana"); +let template = "You eat %a. %The was delicious."; + +SHOW(template.format(apple, apple)); +SHOW(template.format(banana, banana)); +// ---- + +class Animal { + constructor(private name: string) { } + adjective(x: string) { + return `${x} ${this.name}`; + } +} + +String.format.map['adjective'] = "adjective"; + +let cat = new Animal("cat"); +template = "You see a %{adjective,black}."; + +SHOW(template.format(cat)); + +// Keyboard Handling not included, as nothing ROT specific in there + +// RNG / Generating random values + +SHOW( + ROT.RNG.getUniform(), + ROT.RNG.getNormal(0, 10), + ROT.RNG.getPercentage() +); +// ---- + +let canvas = document.createElement("canvas"); +canvas.width = 500; +canvas.height = 200; +SHOW(canvas); + +let ctx = canvas.getContext("2d"); +ctx!.fillStyle = "#fff"; +ctx!.fillRect(0, 0, canvas.width, canvas.height); +ctx!.fillStyle = "#f00"; + +let data = new Array(); +for (let i = 0; i < 40000; i++) { /* generate histogram */ + let num = Math.round(ROT.RNG.getNormal(250, 100)); + data[num] = (data[num] || 0) + 1; +} + +for (let i = 0; i < data.length; i++) { /* plot histogram */ + ctx!.fillRect(i, canvas.height - data[i], 1, data[i]); +} + +// RNG / Working with RNG state +let state = ROT.RNG.getState(); +SHOW(ROT.RNG.getUniform()); + +ROT.RNG.setState(state); +SHOW(ROT.RNG.getUniform()); +// ---- + +let seed = ROT.RNG.getSeed(); + +ROT.RNG.setSeed(12345); +SHOW( + ROT.RNG.getUniform(), + ROT.RNG.getUniform() +); + +ROT.RNG.setSeed(12345); +SHOW( + ROT.RNG.getUniform(), + ROT.RNG.getUniform() +); + +// RNG / Cloning a RNG + +let clone = ROT.RNG.clone(); + +SHOW(ROT.RNG.getUniform()); +SHOW(clone.getUniform()); + +ROT.RNG.setSeed(123); +SHOW(ROT.RNG.getUniform()); +SHOW(clone.getUniform()); + +// RNG / Picking a weighted value +let monsters = { + orc: 3, + ogre: 1, + rat: 5 +}; + +for (let i = 0; i < 20; i++) { + SHOW(ROT.RNG.getWeightedValue(monsters)); +} + +// Console Display / Creating a display + +display = new ROT.Display({ width: 20, height: 5 }); +SHOW(display.getContainer()); /* do not forget to append to page! */ + +// Console Display / Configuring the display +display = new ROT.Display({ width: 20, height: 5 }); +SHOW(display.getContainer()); +display.setOptions({ + width: 30, + fontSize: 8, + fontStyle: "bold", + bg: "#a00" +}); + +// Console Display / Drawing individual characters + +display = new ROT.Display({ width: 40, height: 9 }); +SHOW(display.getContainer()); + +display.draw(5, 4, "@"); +display.draw(15, 4, "%", "#0f0"); /* foreground color */ +display.draw(25, 4, "#", "#f00", "#009"); /* and background color */ + +// Console Display / Drawing strings + +display = new ROT.Display({ width: 40, height: 20 }); +SHOW(display.getContainer()); + +display.drawText(5, 2, "Hello world"); + +/* last argument specifies maximum length */ +display.drawText(20, 5, "This line of text is very long.", 16); + +/* lines are broken at word boundaries; lines are trimmed */ +let words = ["lorem", "ipsum", "dolor", "sit", "amet"]; +let long: string[] = []; +for (let i = 0; i < 30; i++) { long.push(words.random()); } +let longer = long.join(" "); + +display.drawText(1, 10, longer, 38); + +// Console Display / Specifying foreground/background color in strings + +display = new ROT.Display({ width: 40, height: 5 }); +SHOW(display.getContainer()); + +let str = "Goodbye %c{red}cr%b{blue}u%b{}el %c{}world"; +display.drawText(5, 2, str); + +// Console Display / Forced square aspect ratio +let options: ROT.DisplayOptions = { + width: 20, + height: 8, + fontSize: 18, + forceSquareRatio: true +}; +display = new ROT.Display(options); +SHOW(display.getContainer()); + +str = "Using a regular grid\n@....%b{blue}#%b{}##.%b{red}.%b{}.$$$"; +display.drawText(2, 2, str); + +// Console display / graphical tiles + +let tileSet = document.createElement("img"); +tileSet.src = "tiles.png"; + +options = { + layout: "tile", + bg: "transparent", + tileWidth: 64, + tileHeight: 64, + tileSet, + tileMap: { + "@": [0, 0], + "#": [0, 64], + a: [64, 0], + "!": [64, 64] + }, + width: 3, + height: 3 +}; +display = new ROT.Display(options); +SHOW(display.getContainer()); + +tileSet.onload = () => { + display.draw(1, 1, "@"); + display.draw(0, 0, "#"); + display.draw(0, 1, "#"); + display.draw(1, 0, "#"); + display.draw(0, 2, "#"); + display.draw(2, 2, "a"); + display.draw(2, 0, "!"); + display.draw(2, 1, "!"); +}; + +// Console display / graphical tiles / Multiple tiles at one place +tileSet = document.createElement("img"); +tileSet.src = "tiles.png"; + +options = { + layout: "tile", + bg: "transparent", + tileWidth: 64, + tileHeight: 64, + tileSet, + tileMap: { + "@": [0, 0], + "#": [0, 64] + }, + width: 1, + height: 1 +}; +display = new ROT.Display(options); +SHOW(display.getContainer()); + +tileSet.onload = () => { + display.draw(0, 0, ["#", "@"]); +}; + +// Console display / graphical tiles / Colorizing tiles + +tileSet = document.createElement("img"); +tileSet.src = "tiles.png"; + +options = { + layout: "tile", + tileWidth: 64, + tileHeight: 64, + tileSet, + tileMap: { + "@": [0, 0], + "#": [0, 64], + a: [64, 0], + "!": [64, 64] + }, + width: 3, + height: 2, + tileColorize: true +}; +display = new ROT.Display(options); +SHOW(display.getContainer()); + +tileSet.onload = () => { + display.draw(0, 0, "@", "transparent"); + display.draw(1, 0, "@", "green", "red"); + display.draw(2, 0, "@", "rgba(30, 200, 30, 0.5)", "red"); + display.draw(0, 1, "#", "transparent"); + display.draw(1, 1, "#", "white"); + display.draw(2, 1, "#", "transparent", "rgba(250, 250, 0, 0.5)"); +}; + +// Map creation +let map = new ROT.Map.Arena(3, 3); +let userCallback = (x: number, y: number, value: number) => { + SHOW("Value %s generated at [%s,%s]".format(value, x, y)); +}; +map.create(userCallback); +map = new ROT.Map.Arena(10, 5); + +let display1 = new ROT.Display({ width: 10, height: 5, fontSize: 18 }); +SHOW(display1.getContainer()); + +map.create((x, y, wall) => { + display1.draw(x, y, wall ? "#" : "."); +}); + +/* debugging with small font */ +let display2 = new ROT.Display({ width: 10, height: 5, fontSize: 8 }); +SHOW(display2.getContainer()); +map.create(display2.DEBUG); + +// Map creation / Maze / DividedMaze +let dm = new ROT.Map.DividedMaze(w, h); + +for (let i = 0; i < 4; i++) { + display = new ROT.Display({ width: w, height: h, fontSize: 6 }); + SHOW(display.getContainer()); + + dm.create(display.DEBUG); +} + +// Map creation / Maze / Icey's Maze + +for (let i = 0; i < 4; i++) { + display = new ROT.Display({ width: w, height: h, fontSize: 6 }); + SHOW(display.getContainer()); + + let maze = new ROT.Map.IceyMaze(w, h, 4 * i); + maze.create(display.DEBUG); +} + +// Map creation / Maze / Eller's Perfect Maze + +let em = new ROT.Map.EllerMaze(w, h); + +for (let i = 0; i < 4; i++) { + display = new ROT.Display({ width: w, height: h, fontSize: 6 }); + SHOW(display.getContainer()); + + em.create(display.DEBUG); +} + +// Map creation / Cellular +let cellular = new ROT.Map.Cellular(w, h); + +/* cells with 1/2 probability */ +cellular.randomize(0.5); + +/* generate and show four generations */ +for (let i = 0; i < 4; i++) { + display = new ROT.Display({ width: w, height: h, fontSize: 4 }); + SHOW(display.getContainer()); + cellular.create(display.DEBUG); +} +// ---- + +w = 100; h = 60; +display = new ROT.Display({ width: w, height: h, fontSize: 6 }); +SHOW(display.getContainer()); + +/* custom born/survive rules */ +cellular = new ROT.Map.Cellular(w, h, { + born: [4, 5, 6, 7, 8], + survive: [2, 3, 4, 5] +}); + +cellular.randomize(0.9); + +/* generate fifty iterations, show the last one */ +for (let i = 49; i >= 0; i--) { + cellular.create(i ? undefined : display.DEBUG); +} +// ---- + +/* create a connected map where the player can reach all non-wall sections */ +cellular = new ROT.Map.Cellular(w, h, { connected: true }); + +/* cells with 1/2 probability */ +cellular.randomize(0.5); + +/* make a few generations */ +for (let i = 0; i < 4; i++) cellular.create(); + +/* display only the final map */ +display = new ROT.Display({ width: w, height: h, fontSize: 4 }); +SHOW(display.getContainer()); +cellular.create(display.DEBUG); + +/* now connect the maze */ +display = new ROT.Display({ width: w, height: h, fontSize: 4 }); +SHOW(display.getContainer()); +cellular.connect(display.DEBUG); +// ---- + +/* create a connected map where the player can reach all non-wall sections */ +cellular = new ROT.Map.Cellular(w, h, { connected: true }); + +/* cells with 1/2 probability */ +cellular.randomize(0.5); + +/* make a few generations */ +for (let i = 0; i < 4; i++) cellular.create(); + +/* display only the final map */ +display = new ROT.Display({ width: w, height: h, fontSize: 4 }); +SHOW(display.getContainer()); +cellular.create(display.DEBUG); + +/* now connect the maze */ +display = new ROT.Display({ width: w, height: h, fontSize: 4 }); +SHOW(display.getContainer()); +cellular.connect(display.DEBUG, 1); +// ---- + +/* create a connected map where the player can reach all non-wall sections */ +cellular = new ROT.Map.Cellular(w, h, { connected: true }); + +/* cells with 1/2 probability */ +cellular.randomize(0.5); + +/* make a few generations */ +for (let i = 0; i < 4; i++) cellular.create(); + +/* display only the final map */ +display = new ROT.Display({ width: w, height: h, fontSize: 4 }); +SHOW(display.getContainer()); +cellular.create(display.DEBUG); + +/* now connect the maze */ +display = new ROT.Display({ width: w, height: h, fontSize: 4 }); +SHOW(display.getContainer()); +cellular.connect(display.DEBUG, 0, (from, to) => { + SHOW("Connection was made " + from + " to " + to); +}); + +// Map creation / Dungeon + +ROT.RNG.setSeed(1234); +let dungeon = new ROT.Map.Digger(); +display = new ROT.Display({ fontSize: 8 }); +SHOW(display.getContainer()); +dungeon.create(display.DEBUG); + +let drawDoor = (x: number, y: number) => { + display.draw(x, y, "", "", "red"); +}; + +let rooms = dungeon.getRooms(); +for (let i = 0; i < rooms.length; i++) { + let room = rooms[i]; + SHOW("Room #%s: [%s, %s] => [%s, %s]".format( + (i + 1), + room.getLeft(), room.getTop(), + room.getRight(), room.getBottom() + )); + + room.getDoors(drawDoor); +} + +// Map creation / Dungeon / Digger +dungeon = new ROT.Map.Digger(w, h); + +for (let i = 0; i < 4; i++) { + display = new ROT.Display({ width: w, height: h, fontSize: 6 }); + SHOW(display.getContainer()); + dungeon.create(display.DEBUG); +} + +// Map creation / Dungeon / Uniform + +let uniform = new ROT.Map.Uniform(w, h); + +for (let i = 0; i < 4; i++) { + display = new ROT.Display({ width: w, height: h, fontSize: 6 }); + SHOW(display.getContainer()); + uniform.create(display.DEBUG); +} + +// Map creation / Dungeon / Rogue + +let rogue = new ROT.Map.Rogue(w, h); +display = new ROT.Display({ width: w, height: h, fontSize: 6 }); +SHOW(display.getContainer()); +rogue.create(display.DEBUG); + +// FOV computation / Precise shadowcasting + +ROT.RNG.setSeed(12345); +// Not supported in ES6/TS2 +// ROT.DEFAULT_WIDTH = 80; +// ROT.DEFAULT_HEIGHT = 30; + +display = new ROT.Display({ fontSize: 2 }); +SHOW(display.getContainer()); + +/* create a map */ +let data_uniform: any = {}; +new ROT.Map.Uniform().create((x: number, y: number, type: number) => { + data_uniform[x + "," + y] = type; + display.DEBUG(x, y, type); +}); + +/* input callback */ +let lightPasses = (x: number, y: number) => { + let key = x + "," + y; + if (key in data_uniform) { return (data_uniform[key] === 0); } + return false; +}; + +let fov = new ROT.FOV.PreciseShadowcasting(lightPasses); + +/* output callback */ +fov.compute(50, 22, 10, (x, y, r, visibility) => { + let ch = (r ? "" : "@"); + let color = (data_uniform[x + "," + y] ? "#aa0" : "#660"); + display.draw(x, y, ch, "#fff", color); +}); + +// FOV computation / Recursive shadowcasting + +ROT.RNG.setSeed(12345); +const DIR_NORTH = 0; +const DIR_WEST = 6; + +display = new ROT.Display({ fontSize: 12 }); +SHOW(display.getContainer()); + +/* create a map */ +data_uniform = {}; +new ROT.Map.Uniform().create((x: number, y: number, type: number) => { + data_uniform[x + "," + y] = type; + display.DEBUG(x, y, type); +}); + +/* input callback */ +lightPasses = (x: number, y: number) => { + let key = x + "," + y; + if (key in data_uniform) { return (data_uniform[key] === 0); } + return false; +}; + +let fov2 = new ROT.FOV.RecursiveShadowcasting(lightPasses); + +/* output callback for mob with bad vision */ +fov2.compute90(50, 22, 10, DIR_WEST, (x: number, y: number, r: number, visibility: number) => { + let ch = (r ? "1" : "@"); + let color = (data_uniform[x + "," + y] ? "#aa0" : "#660"); + display.draw(x, y, ch, "#fff", color); +}); + +/* output callback for second mob with better vision */ +fov2.compute180(57, 14, 10, DIR_NORTH, (x: number, y: number, r: number, visibility: number) => { + let ch = (r ? "2" : "@"); + let color = (data_uniform[x + "," + y] ? "#aa0" : "#660"); + display.draw(x, y, ch, "#fff", color); +}); + +/* output callback for third mob with supernatural vision */ +fov2.compute(65, 5, 10, (x: number, y: number, r: number, visibility: number) => { + let ch = (r ? "3" : "@"); + let color = (data_uniform[x + "," + y] ? "#aa0" : "#660"); + display.draw(x, y, ch, "#fff", color); +}); + +// Color / Converting string → array +SHOW( + ROT.Color.fromString("rgb(10, 128, 230)"), + ROT.Color.fromString("#faa"), + ROT.Color.fromString("#83fcc4"), + ROT.Color.fromString("goldenrod") +); + +// Color / Converting array → string +SHOW( + ROT.Color.toRGB([10, 128, 230]), + ROT.Color.toHex([10, 128, 230]) +); + +// Color / Converting between RGB and HSL +SHOW( + ROT.Color.rgb2hsl([51, 102, 51]), + ROT.Color.hsl2rgb([0.333, 0.333, 0.3]) +); + +// Color / Adding and mixing colors +SHOW( /* addition = lightening */ + ROT.Color.add([10, 128, 230], [200, 10, 15], [30, 30, 100]), + ROT.Color.add_([10, 128, 230], [200, 10, 15]) +); +SHOW( /* multiplication = darkening */ + ROT.Color.multiply([10, 128, 230], [200, 10, 15]), + ROT.Color.multiply_([10, 128, 230], [200, 10, 15]) +); + +// Color / Interpolating between two colors +SHOW( + /* computed in RGB space */ + ROT.Color.interpolate([10, 128, 230], [30, 255, 255], 0.3), + + /* computed in HSL space */ + ROT.Color.interpolateHSL([10, 128, 230], [30, 255, 255], 0.3) +); + +// Color / Creating random letiants +SHOW( + ROT.Color.randomize([100, 128, 230], [30, 10, 20]), + ROT.Color.randomize([100, 128, 230], [30, 10, 20]), + ROT.Color.randomize([100, 128, 230], [30, 10, 20]) +); + +// Color / Lighting +ROT.RNG.setSeed(12345); + +let mapData: any = {}; +let lightData: { [key: string]: [number, number, number] } = {}; + +/* build a map */ +cellular = new ROT.Map.Cellular().randomize(0.5); +let createCallback = (x: number, y: number, value: number) => { + mapData[x + "," + y] = value; +}; +for (let i = 0; i < 4; i++) { + cellular.create(createCallback); +} + +/* prepare a FOV algorithm */ +lightPasses = (x: number, y: number) => { + return (mapData[x + "," + y] === 1); +}; +fov = new ROT.FOV.PreciseShadowcasting(lightPasses, { topology: 4 }); + +/* prepare a lighting algorithm */ +let reflectivity = (x: number, y: number) => { + return (mapData[x + "," + y] === 1 ? 0.3 : 0); +}; +let lighting = new ROT.Lighting(reflectivity, { range: 12, passes: 2 }); +lighting.setFOV(fov); +lighting.setLight(12, 12, [240, 240, 30]); +lighting.setLight(20, 20, [240, 60, 60]); +lighting.setLight(45, 25, [200, 200, 200]); + +let lightingCallback = (x: number, y: number, color: [number, number, number]) => { + lightData[x + "," + y] = color; +}; +lighting.compute(lightingCallback); + +/* draw the resulting mix of mapData and lightData */ +display = new ROT.Display({ fontSize: 8 }); +SHOW(display.getContainer()); + +/* all cells are lit by ambient light; some are also lit by light sources */ +let ambientLight: ROT.ColorArray = [100, 100, 100]; +for (let id in mapData) { + let parts = id.split(","); + let x = parseInt(parts[0], 10); + let y = parseInt(parts[1], 10); + + let baseColor: ROT.ColorArray = (mapData[id] ? [100, 100, 100] : [50, 50, 50]); + let light = ambientLight; + + if (id in lightData) { /* add light from our computation */ + light = ROT.Color.add(light, lightData[id]); + } + + let finalColor = ROT.Color.multiply(baseColor, light); + display.draw(x, y, "@", "white", ROT.Color.toRGB(finalColor)); +} + +// Pathfinding / Dijkstra's algorithm + +ROT.RNG.setSeed(12345); +display = new ROT.Display({ width: w, height: h, fontSize: 6 }); +SHOW(display.getContainer()); + +/* generate map and store its data */ +let uni_data: any = {}; +let uni_map = new ROT.Map.Uniform(w, h); +uni_map.create((x, y, value) => { + uni_data[x + "," + y] = value; + display.DEBUG(x, y, value); +}); + +/* input callback informs about map structure */ +let passableCallback = (x: number, y: number) => { + return (uni_data[x + "," + y] === 0); +}; + +/* prepare path to given coords */ +let dijkstra = new ROT.Path.Dijkstra(98, 38, passableCallback); + +/* compute from given coords #1 */ +dijkstra.compute(8, 45, (x: number, y: number) => { + display.draw(x, y, "", "", "#800"); +}); + +/* compute from given coords #2 */ +dijkstra.compute(130, 8, (x: number, y: number) => { + display.draw(x, y, "", "", "#800"); +}); + +/* highlight */ +display.draw(8, 45, "", "", "#3f3"); +display.draw(130, 8, "", "", "#3f3"); +display.draw(98, 38, "", "", "#f33"); + +// Pathfinding / A* algorithm + +ROT.RNG.setSeed(12345); +display = new ROT.Display({ width: w, height: h, fontSize: 6 }); +SHOW(display.getContainer()); + +/* generate map and store its data */ +uni_map = new ROT.Map.Uniform(w, h); +uni_map.create((x: number, y: number, value: number) => { + uni_data[x + "," + y] = value; + display.DEBUG(x, y, value); +}); + +/* input callback informs about map structure */ +passableCallback = (x: number, y: number) => { + return (uni_data[x + "," + y] === 0); +}; + +/* prepare path to given coords */ +let astar = new ROT.Path.AStar(98, 38, passableCallback); + +/* compute from given coords #1 */ +astar.compute(8, 45, (x: number, y: number) => { + display.draw(x, y, "", "", "#800"); +}); + +/* compute from given coords #2 */ +astar.compute(130, 8, (x: number, y: number) => { + display.draw(x, y, "", "", "#800"); +}); + +/* highlight */ +display.draw(8, 45, "", "", "#3f3"); +display.draw(130, 8, "", "", "#3f3"); +display.draw(98, 38, "", "", "#f33"); + +// Noise Generation +let noise = new ROT.Noise.Simplex(); + +display = new ROT.Display({ width: w, height: h, fontSize: 3 }); +SHOW(display.getContainer()); + +for (let j = 0; j < h; j++) { + for (let i = 0; i < w; i++) { + let val = noise.get(i / 20, j / 20) * 255; + + let r = ~~(val > 0 ? val : 0); + let g = ~~(val < 0 ? -val : 0); + display.draw(i, j, "", "", "rgb(" + r + "," + g + ",0)"); + } +} +// ---- +noise = new ROT.Noise.Simplex(); + +display = new ROT.Display({ width: w, height: h, fontSize: 12, layout: "hex" }); +SHOW(display.getContainer()); + +for (let j = 0; j < h; j++) { + for (let i = j % 2; i < w; i += 2) { + let val = noise.get(i / 60, j / 60) * 255; + + let r = ~~(val > 0 ? val : 0); + let g = ~~(val < 0 ? -val : 0); + display.draw(i, j, "", "", "rgb(" + r + "," + g + ",0)"); + } +} + +// Timing & scheduling / event queue + +let queue = new ROT.EventQueue(); + +queue.add("event 1", 100); /* queued after 100 time units */ +queue.add("event 2", 10); /* queued after 10 time units */ +queue.add("event 3", 50); /* queued after 50 time units */ + +queue.remove("event 2"); +SHOW( + queue.get(), + queue.get(), + queue.getTime() +); + +// Timing & scheduling / Simple scheduler + +let scheduler = new ROT.Scheduler.Simple(); + +/* generate some actors */ +for (let i = 0; i < 4; i++) { + scheduler.add(i + 1, true); /* true = recurring actor */ +} + +/* simulate several turns */ +let turns: any = []; +for (let i = 0; i < 20; i++) { + let current = scheduler.next(); + turns.push(current); +} + +SHOW("\nGenerated order of actors:"); +SHOW(turns.join(" ") + " ..."); + +// Timing & scheduling / Speed scheduler + +scheduler = new ROT.Scheduler.Speed(); + +class Actor { + constructor(public speed: number, public number: number) { } + + getSpeed() { return this.speed; } +} + +/* generate some actors */ +for (let i = 0; i < 4; i++) { + let actor = new Actor(ROT.RNG.getPercentage(), i + 1); + scheduler.add(actor, true); + SHOW("Object #%s has speed %s.".format(actor.number, actor.speed)); +} + +/* simulate several turns */ +turns = []; +for (let i = 0; i < 40; i++) { + let current = scheduler.next(); + turns.push(current.number); +} + +SHOW("\nGenerated order of turns:"); +SHOW(turns.join(" ") + " ..."); + +// Timing & scheduling / Action-duration scheduler + +let act_scheduler = new ROT.Scheduler.Action(); + +/* generate some actors */ +for (let i = 0; i < 4; i++) { + act_scheduler.add(i + 1, true, i); /* last argument - initial delay */ +} + +/* simulate several turns */ +template = "Actor %s performing action for %s time units (current time: %s)"; +for (let i = 0; i < 20; i++) { + let current = act_scheduler.next(); + + let actionDuration = Math.ceil(ROT.RNG.getUniform() * 20); + act_scheduler.setDuration(actionDuration); + + let padded = actionDuration.toString().lpad("0"); + SHOW(template.format(current, padded, act_scheduler.getTime())); +} + +// Timing & scheduling / Engine / Asynchronous game engine +scheduler = new ROT.Scheduler.Simple(); +let engine = new ROT.Engine(scheduler); +let output: any = []; + +class Actor1 { + constructor(public lives: number) { } + + act() { + output.push("."); + this.lives--; + if (!this.lives) { + scheduler.remove(actor1); + engine.lock(); /* pause execution */ + setTimeout(unlock, 500); /* wait for 500ms */ + } + } +} + +/* sample actor: pauses the execution when dead */ +let actor1 = new Actor1(3); +scheduler.add(actor1, true); + +class Actor2 { + act() { + output.push("@"); + } +} + +let unlock = () => { /* called asynchronously */ + let actor2 = new Actor2(); + + output = []; + scheduler.add(actor2, false); /* add second (non-repeating) actor */ + engine.unlock(); /* continue execution */ + SHOW(output.join("")); +}; + +engine.start(); +SHOW(output.join("")); + +// Timing & scheduling / Engine / Promises + +scheduler = new ROT.Scheduler.Simple(); +engine = new ROT.Engine(scheduler); +output = []; + +/* sample actor: pauses the execution when dead */ +class Actor3 { + constructor(public lives: number) { } + + act() { + let done: any = null; + let promise = { + then(cb: any) { done = cb; } + }; + + output.push("."); + SHOW(output.join("")); + this.lives--; + + /* if alive, wait for 500ms for next turn */ + if (this.lives) { + setTimeout(() => { done(); }, 500); + } + + return promise; + } +} +let actor3 = new Actor3(3); + +scheduler.add(actor3, true); + +engine.start(); + +// String generator + +let sg = new ROT.StringGenerator(); + +let req = new XMLHttpRequest(); +req.open("get", "java.txt", true); +req.send(); + +req.onreadystatechange = () => { + if (req.readyState !== 4) { return; } + + let lines = req.responseText.split("\n"); + while (lines.length) { + let line = lines.pop()!.trim(); + if (!line) { continue; } + sg.observe(line); + } + + for (let i = 0; i < 20; i++) { SHOW(sg.generate()); } +}; + +// Hex support / Displaying hexes +display = new ROT.Display({ width: 8, height: 5 }); +SHOW(display.getContainer()); + +for (let y = 0; y < 5; y++) { + for (let x = y % 2; x < 8; x += 2) { + display.draw(x, y, "•"); + } +} +// ---- + +display = new ROT.Display({ width: 8, height: 5, layout: "hex" }); +SHOW(display.getContainer()); + +for (let y = 0; y < 5; y++) { + for (let x = y % 2; x < 8; x += 2) { + let bg = ["#333", "#666", "#999", "#ccc", "#fff"].random(); + display.draw(x, y, "•", "#000", bg); + } +} +// ---- + +display = new ROT.Display({ + width: 10, height: 4, spacing: 2.5, + layout: "hex", transpose: true +}); +SHOW(display.getContainer()); + +for (let y = 0; y < 4; y++) { + for (let x = y % 2; x < 10; x += 2) { + let bg = ["#333", "#666", "#999", "#ccc", "#fff"].random(); + display.draw(x, y, x + "," + y, "#000", bg); + } +} + +// Hex support / Cellular dungeon generator + +display = new ROT.Display({ width: w, height: h, fontSize: 10, layout: "hex" }); +SHOW(display.getContainer()); + +/* hexagonal map and rules */ +let cell_map = new ROT.Map.Cellular(w, h, { + topology: 6, + born: [4, 5, 6], + survive: [3, 4, 5, 6] +}); + +/* initialize with irregularly random values */ +for (let i = 0; i < w; i++) { + for (let j = 0; j < h; j++) { + let dx = i / w - 0.5; + let dy = j / h - 0.5; + let dist = Math.pow(dx * dx + dy * dy, 0.3); + if (ROT.RNG.getUniform() < dist) { cell_map.set(i, j, 1); } + } +} + +/* generate four iterations, show the last one */ +for (let i = 4; i >= 0; i--) { + cell_map.create(i ? undefined : display.DEBUG); +} + +// Hex support / Pathfinding + +ROT.RNG.setSeed(12345); +display = new ROT.Display({ width: w, height: h, fontSize: 6, layout: "hex" }); +SHOW(display.getContainer()); + +/* generate map and store its data */ +let cell_data: any = {}; +cell_map = new ROT.Map.Cellular(w, h, { + topology: 6, + born: [4, 5, 6], + survive: [3, 4, 5, 6] +}); +cell_map.randomize(0.48); +cell_map.create(); /* two iterations */ +cell_map.create((x: number, y: number, value: number) => { + cell_data[x + "," + y] = value; + display.DEBUG(x, y, value); +}); + +/* input callback informs about map structure */ +passableCallback = (x: number, y: number) => { + return (cell_data[x + "," + y] === 0); +}; + +/* prepare path to given coords */ +dijkstra = new ROT.Path.Dijkstra(120, 64, passableCallback, { topology: 6 }); + +/* compute from given coords */ +dijkstra.compute(30, 16, (x: number, y: number) => { + display.draw(x, y, "", "", "#800"); +}); + +/* highlight */ +display.draw(30, 16, "", "", "#3f3"); +display.draw(120, 64, "", "", "#f33"); + +// Hex support / Field of view + +ROT.RNG.setSeed(12345); + +display = new ROT.Display({ fontSize: 12, layout: "hex" }); +SHOW(display.getContainer()); + +/* generate map and store its data */ +cell_data = {}; +cell_map = new ROT.Map.Cellular(undefined, undefined, { + topology: 6, + born: [4, 5, 6], + survive: [3, 4, 5, 6] +}); +cell_map.randomize(0.4); +cell_map.create((x: number, y: number, value: number) => { + cell_data[x + "," + y] = value; + display.DEBUG(x, y, value); +}); + +/* input callback */ +lightPasses = (x: number, y: number) => { + let key = x + "," + y; + if (key in cell_data) { return (cell_data[key] === 0); } + return false; +}; + +fov = new ROT.FOV.PreciseShadowcasting(lightPasses, { topology: 6 }); + +/* output callback */ +fov.compute(20, 14, 6, (x, y, r, vis) => { + let ch = (r ? "" : "@"); + let color = (cell_data[x + "," + y] ? "#aa0" : "#660"); + display.draw(x, y, ch, "#fff", color); +}); diff --git a/types/rot-js/tsconfig.json b/types/rot-js/tsconfig.json new file mode 100644 index 0000000000..fb42352fb7 --- /dev/null +++ b/types/rot-js/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "rot-js-tests.ts" + ] +} diff --git a/types/rot-js/tslint.json b/types/rot-js/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/rot-js/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" }