Add nethack-rooms-and-corridors-generator; make nethack-level-generator into a folder
This commit is contained in:
		
							parent
							
								
									202d8177be
								
							
						
					
					
						commit
						77106032f1
					
				
					 4 changed files with 412 additions and 44 deletions
				
			
		| 
						 | 
				
			
			@ -1,43 +0,0 @@
 | 
			
		|||
---
 | 
			
		||||
title: "Nethack's Rooms & Corridors Generator"
 | 
			
		||||
date: 2023-02-05T09:08:07-08:00
 | 
			
		||||
draft: true
 | 
			
		||||
series: "Nethack Level Generation"
 | 
			
		||||
categories: Tech
 | 
			
		||||
tags: [Nethack, Programming]
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Rectangles
 | 
			
		||||
 | 
			
		||||
## Placing Rooms
 | 
			
		||||
 | 
			
		||||
[`makerooms`][makerooms_func] uses a "random rect" algorithm to create up to `MAXNROFROOMS` rooms. It iteratively
 | 
			
		||||
generates a random rectancle
 | 
			
		||||
 | 
			
		||||
`create_room`
 | 
			
		||||
 | 
			
		||||
`sort_rooms`
 | 
			
		||||
 | 
			
		||||
## Digging Corridors
 | 
			
		||||
 | 
			
		||||
[`makecorridors`][makecorridors_func]
 | 
			
		||||
 | 
			
		||||
[`join`][join_func]
 | 
			
		||||
 | 
			
		||||
`dig_corridor`
 | 
			
		||||
 | 
			
		||||
## Special Features
 | 
			
		||||
 | 
			
		||||
`make_niches`
 | 
			
		||||
 | 
			
		||||
`do_vault`
 | 
			
		||||
 | 
			
		||||
`create_vault`
 | 
			
		||||
 | 
			
		||||
## Special Rooms
 | 
			
		||||
 | 
			
		||||
[nh36]: https://github.com/NetHack/NetHack/tree/NetHack-3.6
 | 
			
		||||
[mklevc_file]: https://github.com/NetHack/NetHack/blob/NetHack-3.6/src/mklev.c
 | 
			
		||||
[makerooms_func]: https://github.com/NetHack/NetHack/blob/59b117c655731bdf1f8b92c57bdb786119927f3a/src/mklev.c#L223
 | 
			
		||||
[makecorridors_func]: https://github.com/NetHack/NetHack/blob/59b117c655731bdf1f8b92c57bdb786119927f3a/src/mklev.c#L319
 | 
			
		||||
[join_func]: https://github.com/NetHack/NetHack/blob/59b117c655731bdf1f8b92c57bdb786119927f3a/src/mklev.c#L244
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
---
 | 
			
		||||
title: "Nethack's Rooms & Corridors Generator"
 | 
			
		||||
date: 2023-02-05T09:08:07-08:00
 | 
			
		||||
draft: true
 | 
			
		||||
series: "Nethack Level Generation"
 | 
			
		||||
categories: Tech
 | 
			
		||||
tags: [Nethack, Programming]
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
This post is one of a series of posts about Nethack's level generation
 | 
			
		||||
algorithms. You might want to start [at the beginning][series].
 | 
			
		||||
 | 
			
		||||
## Rectangles
 | 
			
		||||
 | 
			
		||||
Nethack builds a list of random rectangles each time it generates new level. The
 | 
			
		||||
list is initialized with a single rect the size of the map, 80 by 21. As new
 | 
			
		||||
rectangles are needed, larger ones are broken down into smaller ones by the
 | 
			
		||||
`split_rects` function in such a way that no rectangle overlaps any other. Up to
 | 
			
		||||
50 rectangeles can be generated this way, placed randomly around the map, and
 | 
			
		||||
each one is no smaller than 4 by 3.
 | 
			
		||||
 | 
			
		||||
## Placing Rooms
 | 
			
		||||
 | 
			
		||||
[`makerooms`][makerooms_func] uses the rectangle list above to create up to 50
 | 
			
		||||
rooms. If enough rooms have been generated, it will also try to generate a
 | 
			
		||||
[vault](#vaults).
 | 
			
		||||
 | 
			
		||||
In each turn of the loop, `makerooms` calls [`create_room`][create_room_func],
 | 
			
		||||
which tries to create a room.
 | 
			
		||||
 | 
			
		||||
`sort_rooms`
 | 
			
		||||
 | 
			
		||||
## Digging Corridors
 | 
			
		||||
 | 
			
		||||
After all the rooms have been placed, the level generator places corridors. It
 | 
			
		||||
tries to connect as many rooms to each other as possible, up to a maximum number
 | 
			
		||||
of doors.
 | 
			
		||||
 | 
			
		||||
[`makecorridors`][makecorridors_func]
 | 
			
		||||
 | 
			
		||||
[`join`][join_func]
 | 
			
		||||
 | 
			
		||||
`dig_corridor`
 | 
			
		||||
 | 
			
		||||
## Niches
 | 
			
		||||
 | 
			
		||||
Niches are small, one tile rooms behind locked, hidden doors. The code refers to
 | 
			
		||||
these as niches, though I've most often heard players refer to them as closets.
 | 
			
		||||
 | 
			
		||||
`make_niches`
 | 
			
		||||
 | 
			
		||||
## Vaults
 | 
			
		||||
 | 
			
		||||
`do_vault`
 | 
			
		||||
 | 
			
		||||
`create_vault`
 | 
			
		||||
 | 
			
		||||
## Special Rooms
 | 
			
		||||
 | 
			
		||||
[series]: {{< ref "/series/nethack-level-generation" >}}
 | 
			
		||||
[makerooms_func]: https://github.com/NetHack/NetHack/blob/59b117c655731bdf1f8b92c57bdb786119927f3a/src/mklev.c#L223
 | 
			
		||||
[create_room_func]: https://github.com/NetHack/NetHack/blob/59b117c655731bdf1f8b92c57bdb786119927f3a/src/sp_lev.c#L1127
 | 
			
		||||
[makecorridors_func]: https://github.com/NetHack/NetHack/blob/59b117c655731bdf1f8b92c57bdb786119927f3a/src/mklev.c#L319
 | 
			
		||||
[join_func]: https://github.com/NetHack/NetHack/blob/59b117c655731bdf1f8b92c57bdb786119927f3a/src/mklev.c#L244
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,347 @@
 | 
			
		|||
"use strict";
 | 
			
		||||
 | 
			
		||||
/// @see ROWNO
 | 
			
		||||
const NUMBER_OF_ROWS = 21;
 | 
			
		||||
/// @see COLNO
 | 
			
		||||
const NUMBER_OF_COLS = 80;
 | 
			
		||||
/// @see XLIM
 | 
			
		||||
const RECT_WIDTH_LIMIT = 4;
 | 
			
		||||
/// @see YLIM
 | 
			
		||||
const RECT_HEIGHT_LIMIT = 3;
 | 
			
		||||
/// @see MAXRECT
 | 
			
		||||
const MAX_NUMBER_OF_RECTS = 50;
 | 
			
		||||
 | 
			
		||||
export class Rect {
 | 
			
		||||
    lowX = 0;
 | 
			
		||||
    lowY = 0;
 | 
			
		||||
    highX = 0;
 | 
			
		||||
    highY = 0;
 | 
			
		||||
 | 
			
		||||
    constructor(lx = 0, ly = 0, hx = 0, hy = 0) {
 | 
			
		||||
        if (lx) { this.lowX = lx; }
 | 
			
		||||
        if (ly) { this.lowY = ly; }
 | 
			
		||||
        if (hx) { this.highX = hx; }
 | 
			
		||||
        if (hy) { this.highY = hy; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A re-implementation of NetHack's rect.c, hewing as close to the implementation as possible.
 | 
			
		||||
 */
 | 
			
		||||
export class Rects {
 | 
			
		||||
    #rects = [];
 | 
			
		||||
    #numberOfRects = 0;
 | 
			
		||||
 | 
			
		||||
    #maxNumbersOfRects;
 | 
			
		||||
 | 
			
		||||
    constructor(numberOfSlots = MAX_NUMBER_OF_RECTS) {
 | 
			
		||||
        this.#maxNumbersOfRects = numberOfSlots;
 | 
			
		||||
        this.initialize(new Rect(0, 0, NUMBER_OF_ROWS, NUMBER_OF_COLS));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get numberOfRects() {
 | 
			
		||||
        return this.#numberOfRects;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @see init_rect
 | 
			
		||||
    initialize(baseRect) {
 | 
			
		||||
        this.#rects = new Array(this.#maxNumbersOfRects);
 | 
			
		||||
        if (baseRect) {
 | 
			
		||||
            this.#rects[0] = baseRect;
 | 
			
		||||
            this.#numberOfRects = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @see get_rect
 | 
			
		||||
    get(rect) {
 | 
			
		||||
        for (let i = 0; i < this.#numberOfRects; i++) {
 | 
			
		||||
            let storedRect = this.#rects[i];
 | 
			
		||||
            if (   rect.lowX >= storedRect.lowX
 | 
			
		||||
                && rect.lowY >= storedRect.lowY
 | 
			
		||||
                && rect.highX <= storedRect.highX
 | 
			
		||||
                && rect.highY <= storedRect.highY)
 | 
			
		||||
            {
 | 
			
		||||
                return storedRect;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @see get_rect_ind
 | 
			
		||||
    getIndex(rect) {
 | 
			
		||||
        for (let i = 0; i < this.#numberOfRects; i++) {
 | 
			
		||||
            let storedRect = this.#rects[i];
 | 
			
		||||
            if (   rect.lowx === storedRect.lowX
 | 
			
		||||
                && rect.lowY === storedRect.lowY
 | 
			
		||||
                && rect.highX === storedRect.highX
 | 
			
		||||
                && rect.highY === storedRect.highY)
 | 
			
		||||
            {
 | 
			
		||||
                return i;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @see add_rect
 | 
			
		||||
    add(rect) {
 | 
			
		||||
        if (this.#numberOfRects > this.#maxNumbersOfRects) {
 | 
			
		||||
            console.error("Exceeded maximum number of rects");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.get(rect)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.#rects[this.#numberOfRects] = rect;
 | 
			
		||||
        this.#numberOfRects++;
 | 
			
		||||
 | 
			
		||||
        console.debug(`Added rect (n = ${this.#numberOfRects})`, rect);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @see remove_rect
 | 
			
		||||
    remove(rect) {
 | 
			
		||||
        let indexOfRect = this.getIndex(rect);
 | 
			
		||||
 | 
			
		||||
        if (indexOfRect < 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.#rects[indexOfRect] = this.#rects[--this.#numberOfRects];
 | 
			
		||||
 | 
			
		||||
        console.debug(`Removed rect (n = ${this.#numberOfRects})`, rect);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @see rnd_rect
 | 
			
		||||
    random() {
 | 
			
		||||
        assert(this.#numberOfRects > 0);
 | 
			
		||||
 | 
			
		||||
        const numberOfRects = this.#numberOfRects;
 | 
			
		||||
        return numberOfRects > 0 ? this.#rects[randomInt(numberOfRects)] : undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @see intersect
 | 
			
		||||
    intersect(rectA, rectB) {
 | 
			
		||||
        if (   rectB.lowX > rectA.highX
 | 
			
		||||
            || rectB.lowY > rectA.highY
 | 
			
		||||
            || rectB.highX < rectA.lowX
 | 
			
		||||
            || rectB.highY < rectA.lowY)
 | 
			
		||||
        {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let intersectingRect = new Rect(
 | 
			
		||||
            rectB.lowX > rectA.lowX ? rectB.lowX : rectA.lowX,
 | 
			
		||||
            rectB.lowY > rectA.lowY ? rectB.lowY : rectA.lowY,
 | 
			
		||||
            rectB.highX > rectA.highX ? rectA.highX : rectB.highX,
 | 
			
		||||
            rectB.highY > rectA.highY ? rectA.highY : rectB.highY
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (   intersectingRect.lowX > intersectingRect.highX
 | 
			
		||||
            || intersectingRect.lowY > intersectingRect.highY)
 | 
			
		||||
        {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return intersectingRect;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @see split_rects
 | 
			
		||||
    split(rectA, rectB) {
 | 
			
		||||
        let outputRect;
 | 
			
		||||
 | 
			
		||||
        let oldRect = rectA;
 | 
			
		||||
        this.remove(rectA);
 | 
			
		||||
 | 
			
		||||
        for (let i = this.#numberOfRects - 1; i >= 0; i--) {
 | 
			
		||||
            let storedRect = this.#rects[i];
 | 
			
		||||
            let intersectingRect = this.intersect(storedRect, rectB);
 | 
			
		||||
            if (intersectingRect) {
 | 
			
		||||
                outputRect = this.split(storedRect);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const rectHeightLimitTimes2 = 2 * RECT_HEIGHT_LIMIT;
 | 
			
		||||
        const rectHeightLimitPlus1 = RECT_HEIGHT_LIMIT + 1;
 | 
			
		||||
 | 
			
		||||
        if (rectB.lowY - oldRect.lowY - 1 > (oldRect.highY < (NUMBER_OF_ROWS - 1) ? rectHeightLimitTimes2 : rectHeightLimitPlus1) + 4) {
 | 
			
		||||
            outputRect = oldRect;
 | 
			
		||||
            outputRect.highY = rectB.lowY - 1;
 | 
			
		||||
            this.add(outputRect);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (rectB.lowY - oldRect.lowX - 1 > (oldRect.highX < NUMBER_OF_COLS - 1 ? rectHeightLimitTimes2 : rectHeightLimitPlus1) + 4) {
 | 
			
		||||
            outputRect = oldRect;
 | 
			
		||||
            outputRect.highX = rectB.lowX - 2;
 | 
			
		||||
            this.add(outputRect);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (oldRect.highY - rectB.highY - 1 > (oldRect.lowY > 0 ? rectHeightLimitTimes2  : rectHeightLimitPlus1) + 4) {
 | 
			
		||||
            outputRect = oldRect;
 | 
			
		||||
            outputRect.lowY = rectB.highY + 2;
 | 
			
		||||
            this.add(outputRect);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (oldRect.highX - rectB.highX - 1 > (oldRect.lowX > 0 ? rectHeightLimitTimes2 : rectHeightLimitPlus1) + 4) {
 | 
			
		||||
            outputRect = oldRect;
 | 
			
		||||
            outputRect.lowX = rectB.highX + 2;
 | 
			
		||||
            this.add(outputRect);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SpecialLevel {
 | 
			
		||||
    #rects;
 | 
			
		||||
    #rooms = [];
 | 
			
		||||
    // TODO: What is this?
 | 
			
		||||
    #smeq = [];
 | 
			
		||||
    #numberOfRooms = 0;
 | 
			
		||||
 | 
			
		||||
    constructor(rects) {
 | 
			
		||||
        this.#rects = rects;
 | 
			
		||||
        this.#numberOfRooms = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createRoom(x, y, width, height, xAlignment, yAlignment, roomType, isLit) {
 | 
			
		||||
        // TODO: roomType
 | 
			
		||||
        let isVault = false;
 | 
			
		||||
 | 
			
		||||
        // TODO: isLit
 | 
			
		||||
 | 
			
		||||
        let xAbs;
 | 
			
		||||
        let yAbs;
 | 
			
		||||
        let xTmp;
 | 
			
		||||
        let yTmp;
 | 
			
		||||
        let widthTmp;
 | 
			
		||||
        let heightTmp;
 | 
			
		||||
 | 
			
		||||
        let tryCount = 0;
 | 
			
		||||
        let rectA;
 | 
			
		||||
        let rectB;
 | 
			
		||||
        
 | 
			
		||||
        do {
 | 
			
		||||
            xTmp = x;
 | 
			
		||||
            yTmp = y;
 | 
			
		||||
            heightTmp = height;
 | 
			
		||||
            widthTmp = width;
 | 
			
		||||
 | 
			
		||||
            let xAlignmentTmp = xAlignment;
 | 
			
		||||
            let yAlignmentTmp = yAlignment;
 | 
			
		||||
 | 
			
		||||
            if (   (xTmp === null
 | 
			
		||||
                    && yTmp === null
 | 
			
		||||
                    && heightTmp === null
 | 
			
		||||
                    && widthTmp === null
 | 
			
		||||
                    && xAlignmentTmp === null
 | 
			
		||||
                    && yAlignmentTmp === null)
 | 
			
		||||
                || isVault)
 | 
			
		||||
            {
 | 
			
		||||
                rectA = this.#rects.random();
 | 
			
		||||
                if (!rectA) {
 | 
			
		||||
                    console.error("No more rects...");
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let lx = rectA.lowX;
 | 
			
		||||
                let ly = rectA.lowY;
 | 
			
		||||
                let hx = rectA.highX;
 | 
			
		||||
                let hy = rectA.highY;
 | 
			
		||||
 | 
			
		||||
                let dx;
 | 
			
		||||
                let dy;
 | 
			
		||||
                if (isVault) {
 | 
			
		||||
                    dx = 1;
 | 
			
		||||
                    dy = 1;
 | 
			
		||||
                } else {
 | 
			
		||||
                    dx = 2 + randomInt((hx - lx > 28) ? 12 : 8);
 | 
			
		||||
                    dy = 2 + randomInt(4);
 | 
			
		||||
                    if (dx * dy > 50) {
 | 
			
		||||
                        dy = 50 / dx;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let xBorder;
 | 
			
		||||
                if (lx > 0 && hx < NUMBER_OF_COLS - 1) {
 | 
			
		||||
                    xBorder = 2 * RECT_WIDTH_LIMIT;
 | 
			
		||||
                } else {
 | 
			
		||||
                    xBorder = RECT_WIDTH_LIMIT + 1;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let yBorder;
 | 
			
		||||
                if (ly > 0 && hy < NUMBER_OF_ROWS - 1) {
 | 
			
		||||
                    yBorder = 2 * RECT_WIDTH_LIMIT;
 | 
			
		||||
                } else {
 | 
			
		||||
                    yBorder = RECT_WIDTH_LIMIT + 1;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (hx - lx < dx + 3 + xBorder || hy - ly < dy + 3 + yBorder) {
 | 
			
		||||
                    rectA = null;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let xAbs = lx 
 | 
			
		||||
                           + (lx > 0 ? RECT_WIDTH_LIMIT : 3) 
 | 
			
		||||
                           + randomInt(hx - (lx > 0 ? lx : 3) - dx - xBorder + 1);
 | 
			
		||||
                let yAbs = ly 
 | 
			
		||||
                           + (ly > 0 ? RECT_HEIGHT_LIMIT : 2) 
 | 
			
		||||
                           + randomInt(hy - (ly > 0 ? ly : 2) - dy - yBorder + 1);
 | 
			
		||||
                if (ly === 0 && hy >= (NUMBER_OF_ROWS - 1) && (!this.#numberOfRooms || !randomInt(this.#numberOfRooms)) && (yAbs + dy > NUMBER_OF_ROWS / 2)) {
 | 
			
		||||
                    yAbs = randomIntPlusOffset(3, 2);
 | 
			
		||||
                    if (this.#numberOfRooms < 4 && dy > 1) {
 | 
			
		||||
                        dy--;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!this.checkRoom(xAbs, dx, yAbs, dy, isVault)) {
 | 
			
		||||
                    rectA = null;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let widthTmp = dx + 1;
 | 
			
		||||
                let heightTmp = dy + 1;
 | 
			
		||||
                rectB = new Rect(xAbs - 1, yAbs - 1, xAbs + widthTmp, yAbs + heightTmp);
 | 
			
		||||
            } else {
 | 
			
		||||
                console.error("Not implemented yet!");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } while (++tryCount < 100 && !rectA);
 | 
			
		||||
 | 
			
		||||
        if (!rectA) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.#rects.split(rectA, rectB);
 | 
			
		||||
 | 
			
		||||
        if (!isVault) {
 | 
			
		||||
            this.#smeq[this.#numberOfRooms] = this.#numberOfRooms;
 | 
			
		||||
            this.addRoom(xAbs, yAbs, xAbs + widthTmp - 1, yAbs + heightTmp - 1, isLit, roomType, false);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.#rooms[this.#numberOfRooms].lx = xAbs;
 | 
			
		||||
            this.#rooms[this.#numberOfRooms].ly = yAbs;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    checkRoom(x, dx, y, dy, isVault) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addRoom(x, y, width, height, isLit, roomType, isVault) {
 | 
			
		||||
        let room = this.#rooms[this.#numberOfRooms];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function randomInt(n) {
 | 
			
		||||
  const max = Math.floor(n);
 | 
			
		||||
  return Math.floor(Math.random() * max);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function randomIntPlusOffset(n, offset) {
 | 
			
		||||
    return randomInt(n) + offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const randomRect = p => {
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ mean it's easy to understand.
 | 
			
		|||
 | 
			
		||||
I'm basing this entire series on the [Nethack 3.6][nh36] branch.
 | 
			
		||||
 | 
			
		||||
## Basics
 | 
			
		||||
### Basics
 | 
			
		||||
 | 
			
		||||
It's written in C, using C89 style function declarations. You see a lot of functions defined like this, with the types
 | 
			
		||||
of arguments defined below the function declaration:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue