Let's try some journaling and devlogging.

The idea is a crossover of Viscera Cleanup Detail and any dungeon crawler: you descend into a dungeon/basement/sewers and clean off blood and such, and fight against monsters as well. Certain monsters spread filth, too, and it should add a… strange strategic layer to the game.

Day 1: Sprite selection and maps

The idea had been floating in my head for quite a long time. But at one moment some puzzle pieces snapped in my mind ( 🥁🍽️ ) and I went for a 1-bit texture pack I knew exists. I also discovered two 1-bit icon sets from the same author.

Then I went to my Palette generator and picked the Sin City palette. I actually ended up adding one dark shade of grey to the palette. In Aseprite, I selected the sprites and textures I wanted, and shaded them. I also drew an animated character, and some additional sprites.

Later, at home, I opened ASCII map editor by NoTimeToPlay, the mod of itch.io's Discord server, and drew some corridors, arenas, and starting/lootings rooms.

const pathwayRooms = [
`......####.
####..#--##
/--####---/
##------###
.########..`,

`###########
/---------/
###########`,

`...######
####----/
/----####
######...`,

`......#/#
......#-#
......#-#
......#-#
......#-#
......#-#
#######-#
/-------#
#########`,

`...#/#
...#-#
...#-#
####-#
/----#
######`,

`..#/#..
..#-#..
###-###
/-----/
###-###
..#-#..
..#/#..`,

`..#/#
..#-#
###-#
/---#
###-#
..#-#
..#/#`
];

I also coded a lot of incomprehensible functions to help to make dungeons later.

const flipRoomHorizontally = function(string) {
    let output = '';
    const rows = string.split('\n');
    for (const row of rows) {
        let newRow = '';
        for (let i = 0; i < row.length; i++) {
            newRow += row[row.length - 1 - i];
        }
        output += newRow + '\n';
    }
    return output.slice(0, -1);
};

const flipRoomVertically = function(string) {
    return string.split('\n').reverse().join('\n');
};

const supersampleRoom = function(string) {
    let output = [];
    const lines = string.split('\n');
    for (const row of lines) {
        let newLine = '';
        for (let i = 0; i < row.length; i++) {
            newLine += row[i] + row[i];
        }
        output.push(newLine, newLine);
    }
    console.log(output);
    return output.join('\n');
};

That was the first day of development.

    Day 2: Actually making dungeons

    The idea of the dungeon generator is that it looks at a multi-line string, finds doors that are written as \ symbols, and an algorithm snaps different rooms by their doors, starting with one room at the center of the room. Currently, the algorithm is a bit buggy and I know where, but it still outputs rooms' objects.

    After that, rendering this room was needed. I started with a simple wall texture without variations. I planned to make an auto-tiling algorithm for walls later, and that required doubling the map so that the 4-corners algorithm could spawn visible walls (it cannot draw walls of 1 tile thick).

    After fleshing out the auto-tiling algorithm and fixing issues with a tileset's texture, I've been generating neat rooms with randomized floors:

    Here is the function that told which tile should be drawn — another pile of incomprehensible code:

    const getCorners = function(preSplitMap, x, y) {
        let result = 0;
        if (x === 0 || y === 0 || preSplitMap[y-1][x-1] !== '#' || preSplitMap[y][x-1] !== '#' || preSplitMap[y-1][x] !== '#') {
            result += 0b0001;
        }
        if (x === preSplitMap[y].length - 1 || y === 0 || preSplitMap[y-1][x+1] !== '#' || preSplitMap[y][x+1] !== '#' || preSplitMap[y-1][x] !== '#') {
            result += 0b0010;
        }
        if (x === preSplitMap[y].length - 1 || y === preSplitMap.length  -1 || preSplitMap[y+1][x+1] !== '#' || preSplitMap[y][x+1] !== '#' || preSplitMap[y+1][x] !== '#') {
            result += 0b1000;
        }
        if (x === 0 || y === preSplitMap.length -1 || preSplitMap[y+1][x-1] !== '#' || preSplitMap[y][x-1] !== '#' || preSplitMap[y+1][x] !== '#') {
            result += 0b0100;
        }
        return result;
    };

    And here is the code that turned a list of rooms into a visible map:

    const renderMap = function (map) {
        const floorLayer = ct.tilemaps.create(-100);
        const wallLayer = ct.tilemaps.create(-10);
        for (const room of map.rooms) {
            const randomFrame = ~~ct.random(12);
            const supersampled = supersampleRoom(room.roomTemplate.grid);
            const rows = supersampled.split('\n');
            for (let y = 0; y < rows.length; y++) {
                for (let x = 0; x < rows[y].length; x++) {
                    if (rows[y][x] === '#') {
                        const frame = getCorners(rows, x, y);
                        wallLayer.addTile('4BitWalls', (x + room.x * 2) * 16, (y + room.y * 2) * 16, frame);
                    } else if (rows[y][x] !== '.') {
                        floorLayer.addTile('FloorTextures', (x + room.x * 2) * 16, (y + room.y * 2) * 16, randomFrame);
                    }
                }
            }
        }
        floorLayer.cache();
        wallLayer.cache();
    };

    The texture I ended up using for my walls:

      Day 3: Dungeons. Again. And first mechanics.

      A couple of edits and my monstrosity began spitting out strips of connected rooms:

      The task for today is adding more rooms aside from the critical path and shutting tight open-ended doorways. It would also be cool to transpose rooms; right now they can be flipped only. Transposing combined with flipping allows for 8 variants of placing for each room.

      The room generation was actually done pretty quickly, with minor modifications of the algorithm that put the rooms. Several rules were added:

      • The algorithm targets to create X rooms for the critical path and Y rooms aside from it.
      • The last room should not be a corridor.
      • Rooms aside from the critical path attach to random rooms, not the previously added ones.
      • Rooms aside the critical path must not be corridors.

      For dead ends, each room copied the grid from a template and rewrote it based on its own list of free, unoccupied doors. This modified template is used for rendering out tiles.

      I also managed to introduce a stochastic bug with room placing, where a new room tried attaching to a room with no free doors at all, which should not have been possible while placing a room on a critical path. Turns out, I added a room with just one door to the list of rooms with items. I will probably create a separate group of rooms just for loot and special stuff and will add them for non-critical rooms.

      Transitions between rooms got patched up with some kind of smeared mud:

      Still need to come up on how to make those gaps between rooms clearer.

      After the initial dungeon pass was made, it was time to implement the very basic mechanics and gameplay logic. Unfortunately, I had time for player spawning and exit placement only. But we still have time tomorrow.

        Days 4 & 5

        These were not fruitful, as I could squeeze just an hour or so from these days, but I've added simple animations to my sprites and basic shooting mechanic.

          Write a Reply...