Room generation looks good

This commit is contained in:
2023-07-25 13:43:42 +10:00
parent 8cd48f6be3
commit dbaa394460
7 changed files with 187 additions and 904 deletions

View File

@@ -1,163 +0,0 @@
import standard;
import node;
import random;
//variables
var gridPositionX: int = 1;
var gridPositionY: int = 1;
var direction: int = null; //BUGFIX: animation not looping properly
var stepAI: int = 0;
//polyfills - animating different cycles on one image
var walkAnimationCounter: int = 0;
fn faceDown(node: opaque) {
if (direction == 0) return;
direction = 0;
node.setNodeRect(0, 0, 32, 32);
node.setNodeFrames(2);
walkAnimationCounter = 12;
}
fn faceUp(node: opaque) {
if (direction == 1) return;
direction = 1;
node.setNodeRect(32 * 2, 0, 32, 32);
node.setNodeFrames(2);
walkAnimationCounter = 12;
}
fn faceRight(node: opaque) {
if (direction == 2) return;
direction = 2;
node.setNodeRect(32 * 4, 0, 32, 32);
node.setNodeFrames(2);
walkAnimationCounter = 12;
}
fn faceLeft(node: opaque) {
if (direction == 3) return;
direction = 3;
node.setNodeRect(32 * 6, 0, 32, 32);
node.setNodeFrames(2);
walkAnimationCounter = 12;
}
//accessors & mutators
fn setGridPosition(node: opaque, x: int, y: int) {
gridPositionX = x;
gridPositionY = y;
node.setNodePositionX(floor( gridPositionX * node.getNodeRectW() / node.getNodeScaleX() ));
node.setNodePositionY(floor( gridPositionY * node.getNodeRectH() / node.getNodeScaleY() ));
}
fn getGridPositionX(node: opaque) {
return gridPositionX;
}
fn getGridPositionY(node: opaque) {
return gridPositionY;
}
//lifecycle functions
fn onLoad(node: opaque) {
node.loadNodeTexture("sprites:/drone.png");
node.faceDown();
node.setNodeScaleX(CAMERA_SCALE_X);
node.setNodeScaleY(CAMERA_SCALE_Y);
}
//fn onUpdate(node: opaque, delta: int) {
// //
//}
fn onStep(node: opaque) {
if (++walkAnimationCounter >= 12) {
node.incrementCurrentNodeFrame();
walkAnimationCounter = 0;
}
//move in realspace
var distX = gridPositionX * TILE_PIXEL_WIDTH - node.getNodePositionX();
var distY = gridPositionY * TILE_PIXEL_HEIGHT - node.getNodePositionY();
node.setNodeMotionX( normalize(distX) );
node.setNodeMotionY( normalize(distY) );
}
fn onFree(node: opaque) {
node.freeNodeTexture();
}
fn onDraw(node: opaque) {
var posX: int = node.getNodeWorldPositionX();
var posY: int = node.getNodeWorldPositionY();
var scaleX: float = node.getNodeWorldScaleX();
var scaleY: float = node.getNodeWorldScaleY();
//this offset is because the sprite cell for lejana is twice as big as the sprite cell for the floor tiles
var originOffsetX: int = node.getNodeRectW() * int scaleX / 4;
var originOffsetY: int = node.getNodeRectH() * int scaleY / 2;
node.drawNode(
floor(posX * scaleX) - originOffsetX + globalCameraX,
floor(posY * scaleY) - originOffsetY + globalCameraY,
floor(node.getNodeRectW() * scaleX),
floor(node.getNodeRectH() * scaleY)
);
}
//gameplay functions
fn runAI(node: opaque, rng: opaque) {
if (stepAI++ >= 1) {
stepAI = 0;
var dir = rng.generateRandomNumber() % 4;
var moveX = 0;
var moveY = 0;
if (dir == 0) {
moveY += 1;
node.faceDown();
}
if (dir == 1) {
moveY -= 1;
node.faceUp();
}
if (dir == 2) {
moveX += 1;
node.faceRight();
}
if (dir == 3) {
moveX -= 1;
node.faceLeft();
}
if (node.getParentNode().callNodeFn("getWalkableAt", gridPositionX + moveX, gridPositionY + moveY)) {
gridPositionX += moveX;
gridPositionY += moveY;
}
}
}
//polyfills - move these to standard
fn normalize(x): int {
if (x > 0) {
return 1;
}
if (x < 0) {
return -1;
}
return 0;
}

View File

@@ -1,258 +0,0 @@
import standard;
import node;
var gridPositionX: int = 1; //position on the game grid
var gridPositionY: int = 1;
var inputX: int = 0; //cache the keyboard input
var inputY: int = 0;
var direction: int = null; //BUGFIX: animation not looping properly
var enableMovementCounter: int = 60 * 3; //BUGFIX: freeze while drones reach their starting spot
//polyfills - animating different cycles on one image
var walkAnimationCounter: int = 0;
var attackAnimationCounter: int = 0;
//utils for facing different directions (idling)
fn faceDown(node: opaque) {
if (direction == 0) return;
direction = 0;
node.setNodeRect(0, 0, 32, 32);
node.setNodeFrames(4);
walkAnimationCounter = 12;
}
fn faceUp(node: opaque) {
if (direction == 1) return;
direction = 1;
node.setNodeRect(32 * 4, 0, 32, 32);
node.setNodeFrames(4);
walkAnimationCounter = 12;
}
fn faceRight(node: opaque) {
if (direction == 2) return;
direction = 2;
node.setNodeRect(32 * 8, 0, 32, 32);
node.setNodeFrames(4);
walkAnimationCounter = 12;
}
fn faceLeft(node: opaque) {
if (direction == 3) return;
direction = 3;
node.setNodeRect(32 * 12, 0, 32, 32);
node.setNodeFrames(4);
walkAnimationCounter = 12;
}
//accessors & mutators
fn setGridPosition(node: opaque, x: int, y: int) {
gridPositionX = x;
gridPositionY = y;
node.setNodePositionX(floor( gridPositionX * node.getNodeRectW() / node.getNodeScaleX() ));
node.setNodePositionY(floor( gridPositionY * node.getNodeRectH() / node.getNodeScaleY() ));
}
fn getGridPositionX(node: opaque) {
return gridPositionX;
}
fn getGridPositionY(node: opaque) {
return gridPositionY;
}
//lifecycle functions
fn onLoad(node: opaque) {
node.loadNodeTexture("sprites:/lejana.png"); //NOTE: all of this script is mapped to this sprite sheet
node.faceDown();
node.setNodeScaleX(CAMERA_SCALE_X);
node.setNodeScaleY(CAMERA_SCALE_Y);
}
//fn onUpdate(node: opaque, delta: int) {
// //
//}
fn onStep(node: opaque) {
//initial freeze
if (enableMovementCounter > 0) {
enableMovementCounter--;
return;
}
//animation - start idling
if (attackAnimationCounter == 0) {
//move to standing state
if (node.getNodeRectY() != 0) {
node.setNodeRect(direction * 32 * 4, 0, 32, 32);
node.setNodeFrames(4);
walkAnimationCounter = 12;
}
}
//animation - start attacking
if (--attackAnimationCounter >= 0) {
//move to attacking state
if (node.getNodeRectY() != 32) {
node.setNodeRect(direction * 32 * 4, 32, 32, 32);
node.setNodeFrames(3);
}
if ((attackAnimationCounter+1) % 4 == 0) { //+1 for a bugfix
node.incrementCurrentNodeFrame();
}
//skip out
return;
}
//facing
if (inputY > 0) {
node.faceDown();
}
else if (inputY < 0) {
node.faceUp();
}
else if (inputX > 0) {
node.faceRight();
}
else if (inputX < 0) {
node.faceLeft();
}
//BUGFIX for smooth animation fo
var lesser = 0;
if ((inputX != 0 || inputY != 0) && attackAnimationCounter == 0) {
lesser = 6;
}
//actually animate
if (--walkAnimationCounter <= lesser) {
node.incrementCurrentNodeFrame();
walkAnimationCounter = 12;
}
var parent = node.getParentNode(); //check for collisions from the parent
if (parent.callNodeFn("getWalkableAt", gridPositionX + inputX, gridPositionY + inputY) && abs(inputX) != abs(inputY)) {
//calc movement
gridPositionX += inputX;
gridPositionY += inputY;
parent.callNodeFn("runAI");
}
//move in realspace
var distX = gridPositionX * TILE_PIXEL_WIDTH - node.getNodePositionX();
var distY = gridPositionY * TILE_PIXEL_HEIGHT - node.getNodePositionY();
node.setNodeMotionX( normalize(distX) );
node.setNodeMotionY( normalize(distY) );
//reset input
inputX = 0;
inputY = 0;
}
fn onFree(node: opaque) {
node.freeNodeTexture();
}
fn onDraw(node: opaque) {
var posX: int = node.getNodeWorldPositionX();
var posY: int = node.getNodeWorldPositionY();
var scaleX: float = node.getNodeWorldScaleX();
var scaleY: float = node.getNodeWorldScaleY();
//this offset is because the sprite cell for lejana is twice as big as the sprite cell for the floor tiles
var originOffsetX: int = player.getNodeRectW() / 2;
var originOffsetY: int = player.getNodeRectH();
node.drawNode(
floor(posX * scaleX) - originOffsetX + globalCameraX,
floor(posY * scaleY) - originOffsetY + globalCameraY,
floor(node.getNodeRectW() * scaleX),
floor(node.getNodeRectH() * scaleY)
);
}
//event functions
fn onKeyDown(node: opaque, event: string) {
//initial freeze
if (enableMovementCounter > 0) {
return;
}
//enable attack
if (event == "character_attack" && inputX == 0 && inputY == 0) {
attackAnimationCounter = 12;
return;
}
//if moving, don't take any more input
if (node.getNodeMotionX() != 0 || node.getNodeMotionY() != 0) {
return;
}
if (event == "character_up") {
inputY -= 1;
return;
}
if (event == "character_down") {
inputY += 1;
return;
}
if (event == "character_left") {
inputX -= 1;
return;
}
if (event == "character_right") {
inputX += 1;
return;
}
}
fn onKeyUp(node: opaque, event: string) {
if (event == "character_up" && inputY < 0) {
inputY = 0;
return;
}
if (event == "character_down" && inputY > 0) {
inputY = 0;
return;
}
if (event == "character_left" && inputX < 0) {
inputX = 0;
return;
}
if (event == "character_right" && inputX > 0) {
inputX = 0;
return;
}
}
//polyfills - move these to standard
fn normalize(x): int {
if (x > 0) {
return 1;
}
if (x < 0) {
return -1;
}
return 0;
}

View File

@@ -1,161 +0,0 @@
import standard;
import node;
import random;
//persistent members
var tilemap: opaque = null;
var stepCounter: opaque = null;
var player: opaque = null;
var enemies: [opaque] = null;
var collisionDataCache: [bool] = null;
var rng: opaque = null; //TODO: does this have an opaque tag?
//lifecycle functions
fn onInit(node: opaque) {
//load the map stuff
tilemap = node.loadChild("scripts:/gameplay/tilemap.toy");
node.generateScene(clock().hash(), MAP_GRID_WIDTH, MAP_GRID_HEIGHT);
//load and initialize the UI (depends on map stuff)
stepCounter = node.loadChild("scripts:/gameplay/step-counter.toy");
stepCounter.setNodeRect(0, 0, CAMERA_SCREEN_W, 32);
stepCounter.callNodeFn("setMaxSteps", 100);
stepCounter.setNodeLayer(100);
//offset the scene node, so it shows the map outside separate from the UI
node.setNodePositionY(16);
}
fn onFree(node: opaque) {
//cleanup
if (rng != null) {
rng.freeRandomGenerator();
}
}
// fn onUpdate(node: opaque, delta: int) {
// print "delta: " + string delta;
// }
//fn onStep(node: opaque) {
// //
//}
fn onDraw(node: opaque) {
//use the onDraw to sort - skip too many steps
node.sortChildrenNode(depthComparator);
var scaleX: float = player.getNodeWorldScaleX();
var scaleY: float = player.getNodeWorldScaleY();
//this offset is because the sprite cell for lejana is twice as big as the sprite cell for the floor tiles
var originOffsetX: int = player.getNodeRectW() / 2;
var originOffsetY: int = player.getNodeRectH() / 2;
//use the room scene's onDraw to set the global camera position
globalCameraX = floor((CAMERA_SCREEN_W / 2) - (player.getNodeWorldPositionX() * scaleX)) - originOffsetX;
globalCameraY = floor((CAMERA_SCREEN_H / 2) - (player.getNodeWorldPositionY() * scaleY)) - originOffsetY;
}
//gameplay functions
fn generateScene(node: opaque, seed: int, width: int, height: int) {
//reset the array
enemies = [];
if (rng != null) {
rng.freeRandomGenerator();
}
rng = createRandomGenerator(seed);
//generate map
tilemap.callNodeFn("generateFromRng", rng, width, height);
collisionDataCache = tilemap.callNodeFn("getCollisionData");
//place player here, so drop dropping can avoid the player
player = node.loadChild("scripts:/gameplay/lejana.toy");
player.callNodeFn("setGridPosition", 1, 1);
//generate drones
var droneCount: int const = rng.generateRandomNumber() % 2 + 20;
for (var i = 0; i < droneCount; i++) {
var d: opaque = node.loadChild("scripts:/gameplay/drone.toy");
d.initNode();
enemies.push(d);
var x = 0;
var y = 0;
//while generated spot is a collision, or too close to the player
while (x == 0 && y == 0 || node.getWalkableAt(x, y) == false || x < width / 6 * 2 && y < height / 6 * 2) {
x = rng.generateRandomNumber() % width;
y = rng.generateRandomNumber() % height;
}
d.callNodeFn("setGridPosition", x, y);
d.setNodePositionX(MAP_GRID_WIDTH * TILE_PIXEL_WIDTH);
d.setNodePositionY(MAP_GRID_HEIGHT * TILE_PIXEL_HEIGHT);
}
}
fn getWalkableAt(node: opaque, x: int, y: int) {
if (collisionDataCache == null || x == null || y == null) {
return false;
}
//entities
if (x == player.callNodeFn("getGridPositionX") && y == player.callNodeFn("getGridPositionY")) {
return false;
}
for (var i = 0; i < enemies.length(); i++) {
if (x == enemies[i].callNodeFn("getGridPositionX") && y == enemies[i].callNodeFn("getGridPositionY")) {
return false;
}
}
//outside the grid
if (y * MAP_GRID_WIDTH + x < 0 || y * MAP_GRID_WIDTH + x >= collisionDataCache.length()) {
return false;
}
//default
return collisionDataCache[y * MAP_GRID_WIDTH + x];
}
fn runAI(node: opaque) {
for (var i = 0; i < enemies.length(); i++) {
enemies[i].callNodeFn("runAI", rng);
}
stepCounter.callNodeFn("alterRemainingSteps", -1);
}
//utils - polyfills
fn loadChild(parent: opaque, fname: string) {
//TODO: add this to the API proper
var child: opaque = loadNode(fname);
parent.pushNode(child);
return child;
}
fn depthComparator(lhs: opaque, rhs: opaque) { //for sorting by depth
var lhsPositionY = lhs.getNodeWorldPositionY();
var rhsPositionY = rhs.getNodeWorldPositionY();
if (lhsPositionY == null || rhsPositionY == null) { //BUGFIX: children without that function
return true;
}
if (lhsPositionY == rhsPositionY) { //BUGFIX: prevent z-fighting
var lhsPositionX = lhs.getNodeWorldPositionX();
var rhsPositionX = rhs.getNodeWorldPositionX();
return lhsPositionX < rhsPositionX;
}
return lhsPositionY < rhsPositionY;
}

View File

@@ -1,68 +0,0 @@
import node;
//members
var maxSteps: int = 0;
var remainingSteps: int = 0;
//accessors
fn getMaxSteps(node: opaque) {
return maxSteps;
}
fn getRemainingSteps(node: opaque) {
return remainingSteps;
}
//mutators
fn setMaxSteps(node: opaque, steps: int) {
maxSteps = steps;
remainingSteps = steps;
}
fn setRemainingSteps(node: opaque, steps: int) {
remainingSteps = steps;
}
//utility mutators
fn alterRemainingSteps(node: opaque, increment: int) {
remainingSteps += increment;
if (remainingSteps > maxSteps) {
remainingSteps = maxSteps;
}
if (remainingSteps < 0) {
remainingSteps = 0;
}
return remainingSteps;
}
//utils - polyfills
fn loadChild(parent: opaque, fname: string) {
var child: opaque = loadNode(fname);
parent.pushNode(child);
child.setNodeLayer(parent.getNodeLayer());
return child;
}
//lifecycle functions
fn onLoad(node: opaque) {
node.loadNodeTexture("sprites:/stepcounter.png");
node
.loadChild("scripts:/gameplay/text.toy")
.setNodeText("fonts:/alphbeta.ttf", 32, "Water", 0, 60, 240, 255); //rgba
}
fn onFree(node: opaque) {
node.freeNodeTexture();
}
fn onDraw(node: opaque) {
if (remainingSteps > 0 && maxSteps > 0) {
var tmp = float remainingSteps / maxSteps * node.getNodeRectW();
var h = node.getNodeRectH();
node.drawNode(node.getNodePositionX(), node.getNodePositionY(), int tmp, h);
}
}

View File

@@ -1,18 +0,0 @@
import node;
//this is a child of the step counter, which simply renders text
fn onDraw(node: opaque) {
var posX: int = node.getNodePositionX();
var posY: int = node.getNodePositionY();
var scaleX: float = node.getNodeScaleX();
var scaleY: float = node.getNodeScaleY();
node.drawNode(floor(posX * scaleX), floor(posY * scaleY));
}
fn onFree(node: opaque) {
node.freeNodeTexture();
}
//TODO: polyfill?

View File

@@ -1,187 +0,0 @@
import standard;
import node;
//constants mapped to the given file "tileset.png"
var tileset: [string : [int]] const = [
"empty": [-1. -1, -1],
"pillar": [0, 0, 0],
"floor-0": [0, 1, 1],
"floor-1": [1, 1, 1],
"floor-2": [2, 1, 1],
"floor-3": [3, 1, 1],
"wall-t": [0, 2, 0],
"wall-b": [1, 2, 0],
"wall-l": [2, 2, 0],
"wall-r": [3, 2, 0],
"corner-tl": [0, 3, 0],
"corner-tr": [1, 3, 0],
"corner-bl": [2, 3, 0],
"corner-br": [3, 3, 0],
"edge-tl": [0, 4, 0],
"edge-tr": [1, 4, 0],
"edge-bl": [2, 4, 0],
"edge-br": [3, 4, 0]
];
//raw string data
var rawmap: [[string]] = null;
//baked and usable blobs of data
var tilemap: [int] = null; // [x0, y0, x1, y1, ...]
var collisions: [bool] = null;
//metadata about the above blobs
var tilemapGridWidth: int = 0;
var tilemapGridHeight: int = 0;
//fast accessors
fn getTilemapData(node: opaque) {
return tilemap;
}
fn getCollisionData(node: opaque) {
return collisions;
}
//lifecycle functions
fn onLoad(node: opaque) {
node.loadNodeTexture("sprites:/tileset.png");
node.setNodeScaleX(CAMERA_SCALE_X);
node.setNodeScaleY(CAMERA_SCALE_Y);
}
fn onDraw(node: opaque) {
assert tilemap, "tilemap is null";
var posX: int const = node.getNodeWorldPositionX();
var posY: int const = node.getNodeWorldPositionY();
//draw everything at twice the original size
var scaleX: float const = node.getNodeWorldScaleX();
var scaleY: float const = node.getNodeWorldScaleY();
//draw the tilemap
for (var j = 0; j < tilemapGridHeight; j++) {
for (var i = 0; i < tilemapGridWidth; i++) {
//set the rect of the node on the tilesheet - the "tilemap" var is a single blob of data
node.setNodeRect(
tilemap[j * tilemapGridWidth * 2 + i * 2] * TILE_PIXEL_WIDTH,
tilemap[j * tilemapGridWidth * 2 + i * 2 + 1] * TILE_PIXEL_HEIGHT,
TILE_PIXEL_WIDTH, TILE_PIXEL_HEIGHT
);
//draw to the screen
node.drawNode(
floor((i * TILE_PIXEL_WIDTH + posX) * scaleX) + globalCameraX,
floor((j * TILE_PIXEL_HEIGHT + posY) * scaleY) + globalCameraY,
floor(TILE_PIXEL_WIDTH * scaleX),
floor(TILE_PIXEL_HEIGHT * scaleY)
);
}
}
}
//utils functions for map generation
fn generateFromRng(node: opaque, rng: opaque, width: int, height: int) {
//use debug room for now
rawmap = generateRawTilemapDebugRoom(rng, width, height);
tilemap = bakeTilemap(rawmap, width, height);
collisions = bakeCollisionMap(rawmap, width, height);
tilemapGridWidth = width;
tilemapGridHeight = height;
//debugging
print tilemap;
print collisions;
}
//"raw" tilemaps are not usable, they are just a 2d array of strings
fn generateRawTilemapDebugRoom(rng: opaque, width: int, height: int): [[string]] const {
import random;
//generate a grid filled with only pillar tiles, as a starting point
var result: [[string]] = [];
var row: [string] = [];
for (var j: int = 0; j < height; j++) {
row.push("pillar");
}
for (var i: int = 0; i < width; i++) {
result.push(row);
}
//generate the walkable tiles of the grid - floor tiles
for (var j: int = 1; j < height - 1; j++) {
for (var i: int = 1; i < width - 1; i++) {
//select a random floor tile (graphical effect only)
var x: int = rng.generateRandomNumber() % 4;
var t: string = "floor-" + string x;
result[i][j] = t;
}
}
//draw top and bottom walls
for (var i: int = 1; i < width - 1; i++) {
result[i][0] = "wall-t";
result[i][height - 1] = "wall-b";
}
//draw the left and right walls
for (var j: int = 1; j < height - 1; j++) {
result[0][j] = "wall-l";
result[width-1][j] = "wall-r";
}
//draw the corners
result[0][0] = "corner-tl";
result[width - 1][0] = "corner-tr";
result[0][height - 1] = "corner-bl";
result[width - 1][height - 1] = "corner-br";
//debugging
result[4][4] = "pillar";
result[width - 5][4] = "pillar";
result[4][height - 5] = "pillar";
result[width - 5][height - 5] = "pillar";
//return the raw result of strings
return result;
}
//"baking" converts raw tilemaps into usable blobs of data
fn bakeTilemap(tilemap: [[string]] const, width: int, height: int): [int] const {
var result: [int] = [];
//extract the positions from the tileset
for (var j: int = 0; j < height; j++) {
for (var i: int = 0; i < width; i++) {
result.push(tileset[ tilemap[i][j] ][0]);
result.push(tileset[ tilemap[i][j] ][1]);
}
}
return result;
}
//this bakes the collision map as a separate object
fn bakeCollisionMap(tilemap: [[string]] const, width: int, height: int): [bool] const {
var result: [bool] = [];
//extract the walkable state
for (var j: int = 0; j < height; j++) {
for (var i: int = 0; i < width; i++) {
result.push(tileset[ tilemap[i][j] ][2] != 0);
}
}
return result;
}

View File

@@ -24,8 +24,8 @@ var tileset: [string : [int]] const = [
"temple-edge-tl": [0, 4, 0],
"temple-edge-tr": [1, 4, 0],
"temple-edge-bl": [2, 4, 0],
"temple-edge-br": [3, 4, 0]
"temple-edge-bl": [3, 4, 0], //would be great if these were the right way around...
"temple-edge-br": [2, 4, 0]
];
var themes: [string] const = [
@@ -104,7 +104,8 @@ fn generateTilemapData(rng: opaque) {
//etch the corridors
etchCorridors(roomData, corridorData, rng);
//TODO: etch the walls with a filter, based on the room's themes
//etch the walls with marching squares, based on the room's themes
etchWalls(roomData);
//save the metadata for later retrieval
metadata = roomData;
@@ -153,52 +154,6 @@ fn etchRoom(rng: opaque, metadata: [string: any]) {
tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 2] = tileset[theme + "-floor-" + string floorIndex][2];
}
}
// //create the string constants here, so refstring can do it's job
// var T_WALL: [int] const = tileset[ theme + "-wall-t" ];
// var B_WALL: [int] const = tileset[ theme + "-wall-b" ];
// var L_WALL: [int] const = tileset[ theme + "-wall-l" ];
// var R_WALL: [int] const = tileset[ theme + "-wall-r" ];
// //etch the walls of each room
// for (var i: int = x; i < x + w; i++) {
// tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 0] = T_WALL[0];
// tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 1] = T_WALL[1];
// tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 2] = T_WALL[2];
// tilemap[(h + y - 1) * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 0] = B_WALL[0];
// tilemap[(h + y - 1) * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 1] = B_WALL[1];
// tilemap[(h + y - 1) * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 2] = B_WALL[2];
// }
// for (var j: int = y; j < y + h; j++) {
// tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 0] = L_WALL[0];
// tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 1] = L_WALL[1];
// tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 2] = L_WALL[2];
// tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + (x + w - 1) * 3 + 0] = R_WALL[0];
// tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + (x + w - 1) * 3 + 1] = R_WALL[1];
// tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + (x + w - 1) * 3 + 2] = R_WALL[2];
// }
// //etch the corners
// tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 0] = tileset[ theme + "-corner-tl" ][0];
// tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 1] = tileset[ theme + "-corner-tl" ][1];
// tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 2] = tileset[ theme + "-corner-tl" ][2];
// tilemap[(h + y - 1) * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 0] = tileset[ theme + "-corner-bl" ][0];
// tilemap[(h + y - 1) * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 1] = tileset[ theme + "-corner-bl" ][1];
// tilemap[(h + y - 1) * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 2] = tileset[ theme + "-corner-bl" ][2];
// tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + (x + w - 1) * 3 + 0] = tileset[ theme + "-corner-tr" ][0];
// tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + (x + w - 1) * 3 + 1] = tileset[ theme + "-corner-tr" ][1];
// tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + (x + w - 1) * 3 + 2] = tileset[ theme + "-corner-tr" ][2];
// tilemap[(h + y - 1) * CELL_WIDTH * CELL_COUNT_X * 3 + (x + w - 1) * 3 + 0] = tileset[ theme + "-corner-br" ][0];
// tilemap[(h + y - 1) * CELL_WIDTH * CELL_COUNT_X * 3 + (x + w - 1) * 3 + 1] = tileset[ theme + "-corner-br" ][1];
// tilemap[(h + y - 1) * CELL_WIDTH * CELL_COUNT_X * 3 + (x + w - 1) * 3 + 2] = tileset[ theme + "-corner-br" ][2];
}
fn generateCorridorData(rng: opaque) {
@@ -419,6 +374,189 @@ fn etchLine(x: int, y: int, xLength: int, yLength: int, theme: string, rng: opaq
}
}
fn etchWalls(roomData) {
//zero'd signals
var signals: [string] = [];
for (var j: int = 0; j < CELL_COUNT_Y * CELL_HEIGHT; j++) {
for (var i: int = 0; i < CELL_COUNT_X * CELL_WIDTH; i++) {
signals.push(""); //empty
}
}
//determine the walls' layout from the tilemap
for (var i: int = 0; i < MAP_GRID_WIDTH; i++) {
for (var j: int = 0; j < MAP_GRID_HEIGHT; j++) {
signals[j * MAP_GRID_WIDTH + i] = parseTilemapAt(i, j);
}
}
//etch the walls into the tilemap, based on the room theme
for (var i: int = 0; i < MAP_GRID_WIDTH; i++) {
for (var j: int = 0; j < MAP_GRID_HEIGHT; j++) {
if (signals[j * MAP_GRID_WIDTH + i] == "") {
continue;
}
var theme = roomData[floor(i / CELL_WIDTH)][floor(j / CELL_HEIGHT)]["theme"];
tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 0] = tileset[theme + "-" + signals[j * MAP_GRID_WIDTH + i]][0];
tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 1] = tileset[theme + "-" + signals[j * MAP_GRID_WIDTH + i]][1];
tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 2] = tileset[theme + "-" + signals[j * MAP_GRID_WIDTH + i]][2];
}
}
}
/*
//lets say snapshot looks like this
snapshot:
0, 0, 1
0, 0, 1
0, 0, 1
result should be "wall-l",
//lets say filter looks like this
filter:
1 1 1 1 1
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 1 1 1
find the position within the filter, that gives the result "wall-l"
//lets say result looks like this
"edge-br" "wall-b" "edge-bl"
"wall-r" "pillar" "wall-l"
"edge-tr" "wall-t" "edge-tl"
let's try this...
*/
/* 9x9
var marchingFilter: [int] const = [
1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 1, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1, 1, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1
];
var marchingFilterResult: [string] const = [
"", "", "", "", "", "", "", "", "",
"", "edge-tr", "wall-b", "wall-b", "wall-b", "wall-b", "wall-b", "edge-tl", "",
"", "wall-r", "corner-tl", "wall-t", "wall-t", "wall-t", "corner-tr", "wall-l", "",
"", "wall-r", "wall-l", "", "", "", "wall-r", "wall-l", "",
"", "wall-r", "wall-l", "", "", "", "wall-r", "wall-l", "",
"", "wall-r", "wall-l", "", "", "", "wall-r", "wall-l", "",
"", "wall-r", "corner-bl", "wall-b", "wall-b", "wall-b", "corner-br", "wall-l", "",
"", "edge-br", "wall-t", "wall-t", "wall-t", "wall-t", "wall-t", "edge-bl", "",
"", "", "", "", "", "", "", "", ""
];
*/
//this is a nightmare
var marchingFilter: [int] const = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1,
1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
var marchingFilterResult: [string] const = [
"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" ,
"" ,"" ,"edge-tl" ,"wall-b" ,"wall-b" ,"wall-b" ,"wall-b" ,"wall-b" ,"wall-b" ,"edge-tr" ,"" ,"" ,
"" ,"edge-tl" ,"corner-br" ,"" ,"corner-tl" ,"wall-t" ,"wall-t" ,"corner-tr" ,"" ,"corner-bl" ,"edge-tr" ,"" ,
"" ,"wall-r" ,"" ,"" ,"wall-l" ,"" ,"" ,"wall-r" ,"" ,"" ,"wall-l" ,"" ,
"" ,"wall-r" ,"corner-tl" ,"wall-t" ,"edge-br" ,"" ,"" ,"edge-bl" ,"wall-t" ,"corner-tr" ,"wall-l" ,"" ,
"" ,"wall-r" ,"wall-l" ,"" ,"" ,"" ,"" ,"" ,"" ,"wall-r" ,"wall-l" ,"" ,
"" ,"wall-r" ,"wall-l" ,"" ,"" ,"" ,"" ,"" ,"" ,"wall-r" ,"wall-l" ,"" ,
"" ,"wall-r" ,"corner-bl" ,"wall-b" ,"edge-tr" ,"" ,"" ,"edge-tl" ,"wall-b" ,"corner-br" ,"wall-l" ,"" ,
"" ,"wall-r" ,"" ,"" ,"wall-l" ,"" ,"" ,"wall-r" ,"" ,"" ,"wall-l" ,"" ,
"" ,"edge-bl" ,"corner-tr" ,"" ,"corner-bl" ,"wall-b" ,"wall-b" ,"corner-br" ,"" ,"corner-tl" ,"edge-br" ,"" ,
"" ,"" ,"edge-bl" ,"wall-t" ,"wall-t" ,"wall-t" ,"wall-t" ,"wall-t" ,"wall-t" ,"edge-br" ,"" ,"" ,
"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" ,""
];
//if walkable is below, then return top, etc.
fn parseTilemapAt(x: int, y: int) {
//parse based on walkability, for now
if (tilemap[y * CELL_WIDTH * CELL_COUNT_X * 3 + x * 3 + 2] != 0) {
return ""; //empty
}
//generate the snapshot of the current position
var snapshot = generateSnapshotAt(x, y);
//find the snapshot's position within the filter
for (var i: int = 0; i < 12 -2; i++) {
for (var j: int = 0; j < 12 -2; j++) {
if (
marchingFilter[(j + 0) * 12 + (i + 0)] == snapshot[0 * 3 + 0] &&
marchingFilter[(j + 0) * 12 + (i + 1)] == snapshot[0 * 3 + 1] &&
marchingFilter[(j + 0) * 12 + (i + 2)] == snapshot[0 * 3 + 2] &&
marchingFilter[(j + 1) * 12 + (i + 0)] == snapshot[1 * 3 + 0] &&
marchingFilter[(j + 1) * 12 + (i + 1)] == snapshot[1 * 3 + 1] &&
marchingFilter[(j + 1) * 12 + (i + 2)] == snapshot[1 * 3 + 2] &&
marchingFilter[(j + 2) * 12 + (i + 0)] == snapshot[2 * 3 + 0] &&
marchingFilter[(j + 2) * 12 + (i + 1)] == snapshot[2 * 3 + 1] &&
marchingFilter[(j + 2) * 12 + (i + 2)] == snapshot[2 * 3 + 2]
)
{
return marchingFilterResult[(j+1) * 12 + (i+1)];
}
}
}
//anything else, just plop down a pillar
fn nonZero(key: int, value: int) {
return value != 0;
}
if (snapshot.some(nonZero)) {
return "pillar";
}
return "";
}
fn generateSnapshotAt(x: int, y: int) {
var result: [int] = [];
for (var j: int = -1; j < 2; j++) {
for (var i: int = -1; i < 2; i++) {
if (x + i < 0 || y + j < 0 || x + i >= MAP_GRID_WIDTH || y + j >= MAP_GRID_HEIGHT) {
result.push(0);
}
else {
result.push(tilemap[(y+j) * CELL_WIDTH * CELL_COUNT_X * 3 + (x+i) * 3 + 2] != 0 ? 1 : 0); //walkable
}
}
}
return result;
}
//polyfill
fn sign(x) {
if (x > 0) return 1;