From 98d0d605e731b20b7a28e355b85111a8ea43f899 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Mon, 3 Jul 2023 16:37:10 +1000 Subject: [PATCH] Implemented Rogue-style room generation... badly. 10,000 ways not to make a lightbulb, I guess. --- .../drone.toy | 0 .../lejana.toy | 0 .../scene.toy | 6 +- .../step-counter.toy | 0 .../{gameplay => gameplay-prototype}/text.toy | 0 .../tilemap.toy | 4 +- assets/scripts/init.toy | 13 +- assets/scripts/scene.toy | 66 ++++++ assets/scripts/tilemap/generator.toy | 188 ++++++++++++++++++ assets/scripts/tilemap/rendernode.toy | 62 ++++++ 10 files changed, 327 insertions(+), 12 deletions(-) rename assets/scripts/{gameplay => gameplay-prototype}/drone.toy (100%) rename assets/scripts/{gameplay => gameplay-prototype}/lejana.toy (100%) rename assets/scripts/{gameplay => gameplay-prototype}/scene.toy (98%) rename assets/scripts/{gameplay => gameplay-prototype}/step-counter.toy (100%) rename assets/scripts/{gameplay => gameplay-prototype}/text.toy (100%) rename assets/scripts/{gameplay => gameplay-prototype}/tilemap.toy (99%) create mode 100644 assets/scripts/scene.toy create mode 100644 assets/scripts/tilemap/generator.toy create mode 100644 assets/scripts/tilemap/rendernode.toy diff --git a/assets/scripts/gameplay/drone.toy b/assets/scripts/gameplay-prototype/drone.toy similarity index 100% rename from assets/scripts/gameplay/drone.toy rename to assets/scripts/gameplay-prototype/drone.toy diff --git a/assets/scripts/gameplay/lejana.toy b/assets/scripts/gameplay-prototype/lejana.toy similarity index 100% rename from assets/scripts/gameplay/lejana.toy rename to assets/scripts/gameplay-prototype/lejana.toy diff --git a/assets/scripts/gameplay/scene.toy b/assets/scripts/gameplay-prototype/scene.toy similarity index 98% rename from assets/scripts/gameplay/scene.toy rename to assets/scripts/gameplay-prototype/scene.toy index 7b63e1b..d19a65d 100644 --- a/assets/scripts/gameplay/scene.toy +++ b/assets/scripts/gameplay-prototype/scene.toy @@ -36,9 +36,9 @@ fn onFree(node: opaque) { } } -//fn onUpdate(node: opaque, delta: int) { -// // -//} +// fn onUpdate(node: opaque, delta: int) { +// print "delta: " + string delta; +// } //fn onStep(node: opaque) { // // diff --git a/assets/scripts/gameplay/step-counter.toy b/assets/scripts/gameplay-prototype/step-counter.toy similarity index 100% rename from assets/scripts/gameplay/step-counter.toy rename to assets/scripts/gameplay-prototype/step-counter.toy diff --git a/assets/scripts/gameplay/text.toy b/assets/scripts/gameplay-prototype/text.toy similarity index 100% rename from assets/scripts/gameplay/text.toy rename to assets/scripts/gameplay-prototype/text.toy diff --git a/assets/scripts/gameplay/tilemap.toy b/assets/scripts/gameplay-prototype/tilemap.toy similarity index 99% rename from assets/scripts/gameplay/tilemap.toy rename to assets/scripts/gameplay-prototype/tilemap.toy index 8f92adb..cfc4878 100644 --- a/assets/scripts/gameplay/tilemap.toy +++ b/assets/scripts/gameplay-prototype/tilemap.toy @@ -3,8 +3,10 @@ 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], diff --git a/assets/scripts/init.toy b/assets/scripts/init.toy index 69884eb..a2cf745 100644 --- a/assets/scripts/init.toy +++ b/assets/scripts/init.toy @@ -2,14 +2,11 @@ var TILE_PIXEL_WIDTH: int const = 16; var TILE_PIXEL_HEIGHT: int const = 16; -var MAP_GRID_WIDTH: int const = 16; -var MAP_GRID_HEIGHT: int const = 16; +var CAMERA_SCREEN_W: int const = 1080; +var CAMERA_SCREEN_H: int const = 720; -var CAMERA_SCREEN_W: int const = 800; -var CAMERA_SCREEN_H: int const = 600; - -var CAMERA_SCALE_X: float const = 2.0; -var CAMERA_SCALE_Y: float const = 2.0; +var CAMERA_SCALE_X: float const = 1.0; +var CAMERA_SCALE_Y: float const = 1.0; //this is a very odd pattern... var globalCameraX: int = 0; @@ -53,5 +50,5 @@ var globalCameraY: int = 0; initWindow("Skylands", CAMERA_SCREEN_W, CAMERA_SCREEN_H, false); //TODO: custom FPS setting //kick off the logic of the scene graph - loadRootNode("scripts:/gameplay/scene.toy"); + loadRootNode("scripts:/scene.toy"); } diff --git a/assets/scripts/scene.toy b/assets/scripts/scene.toy new file mode 100644 index 0000000..e1d05d2 --- /dev/null +++ b/assets/scripts/scene.toy @@ -0,0 +1,66 @@ +import standard; +import random; +import node; + + +//persistent members of the scene node +var rng: opaque = null; + +//map data (interleaved) +var tilemap: [int] = null; + + +//lifetime functions +fn onInit(node: opaque) { + //init the rng with a seed (random) + if (rng != null) { + rng.freeRandomGenerator(); + } + rng = createRandomGenerator(clock().hash()); + + //generate the level, filling out the data + generateLevel(rng); + + //generate the child node to render the map + var child: opaque = node.loadChild("scripts:/tilemap/rendernode.toy"); + + child.callNodeFn("setTilemap", tilemap); +} + +fn onFree(node: opaque) { + if (rng != null) { + rng.freeRandomGenerator(); + } +} + + +//scene loading +fn generateLevel(rng: opaque) { + import runner; + + //load and run the generator script + var generatorScript: opaque = loadScript("scripts:/tilemap/generator.toy"); + generatorScript.runScript(); + + //call the functions + generatorScript.callScriptFn("generateTilemapData", rng); + + //get the resulting blobs of data for processing + tilemap = generatorScript.getScriptVar("tilemap"); + + //free + generatorScript.freeScript(); +} + + + + + + +//polyfills +fn loadChild(parent: opaque, fname: string) { + //TODO: add this to the API proper + var child: opaque = loadNode(fname); + parent.pushNode(child); + return child; +} \ No newline at end of file diff --git a/assets/scripts/tilemap/generator.toy b/assets/scripts/tilemap/generator.toy new file mode 100644 index 0000000..060325b --- /dev/null +++ b/assets/scripts/tilemap/generator.toy @@ -0,0 +1,188 @@ +import standard; +import random; + +//constants mapped to the given atlas file "tileset.png" +var tileset: [string : [int]] const = [ + "empty": [-1, -1, -1], + + "temple-pillar": [0, 0, 0], + + "temple-floor-0": [0, 1, 1], + "temple-floor-1": [1, 1, 1], + "temple-floor-2": [2, 1, 1], + "temple-floor-3": [3, 1, 1], + + "temple-wall-t": [0, 2, 0], + "temple-wall-b": [1, 2, 0], + "temple-wall-l": [2, 2, 0], + "temple-wall-r": [3, 2, 0], + + "temple-corner-tl": [0, 3, 0], + "temple-corner-tr": [1, 3, 0], + "temple-corner-bl": [2, 3, 0], + "temple-corner-br": [3, 3, 0], + + "temple-edge-tl": [0, 4, 0], + "temple-edge-tr": [1, 4, 0], + "temple-edge-bl": [2, 4, 0], + "temple-edge-br": [3, 4, 0] +]; + +var themes: [string] const = [ + "temple" +]; + + +//constants for generating maps +var ROOM_MIN_WIDTH: int const = 4; //minimum safe value 4 +var ROOM_MIN_HEIGHT: int const = 4; + +var ROOM_MAX_WIDTH: int const = 12; +var ROOM_MAX_HEIGHT: int const = 12; + +var CELL_WIDTH: int const = 16; //minimum safe value ROOM_MAX_* + 4 +var CELL_HEIGHT: int const = 16; + +var CELL_COUNT_X: int const = 3; +var CELL_COUNT_Y: int const = 3; + + +//raw string data +var tilemap: [int] = null; + + +//public functions +fn generateTilemapData(rng: opaque) { + //generate a grid filled with only empty tiles, as a starting point + tilemap = []; + + for (var j: int = 0; j < CELL_COUNT_Y * CELL_HEIGHT; j++) { + for (var i: int = 0; i < CELL_COUNT_X * CELL_WIDTH; i++) { + tilemap.push(-1); //x + tilemap.push(-1); //y + tilemap.push(-1); //collision + } + } + + //generate the metadata of each room + var data = []; + + for (var i: int = 0; i < CELL_COUNT_X; i++) { + + var inner = []; //inner decl + + for (var j: int = 0; j < CELL_COUNT_Y; j++) { + var metadata = generateRoomMetadata(rng, + i * CELL_WIDTH + 1, + j * CELL_HEIGHT + 1, + CELL_WIDTH - 2, + CELL_HEIGHT - 2 + ); + + inner.push(metadata); //BUG: see Toy #70 + } + + data.push(inner); + } + + //etch each tile string into the tilemap + for (var j: int = 0; j < CELL_COUNT_Y; j++) { + for (var i: int = 0; i < CELL_COUNT_X; i++) { + etchRoom(rng, data[i][j]); + } + } + + //TODO: etch corridors +} + +fn generateRoomMetadata(rng: opaque, left: int, top: int, width: int, height: int) { + //I expected this line to break, but I must've fixed calls within indexes at some point + var theme: string = themes[rng.generateRandomNumber() % themes.length()]; + + var x: int = rng.generateRandomNumber() % (ROOM_MAX_WIDTH - width) + left; + var y: int = rng.generateRandomNumber() % (ROOM_MAX_HEIGHT - height) + top; + var w: int = rng.generateRandomNumber() % (ROOM_MAX_WIDTH - ROOM_MIN_WIDTH) + ROOM_MIN_WIDTH; + var h: int = rng.generateRandomNumber() % (ROOM_MAX_HEIGHT - ROOM_MIN_HEIGHT) + ROOM_MIN_HEIGHT; + + var doorX: int = x + 1 + rng.generateRandomNumber() % (w - 2); + var doorY: int = y + 1 + rng.generateRandomNumber() % (h - 2); + + var metadata: [string: any] = [ + "theme" : theme, + "x" : x, + "y" : y, + "w" : w, + "h" : h, + "doorX": doorX, + "doorY": doorY + ]; + + return metadata; +} + +fn etchRoom(rng: opaque, metadata: [string: any]) { + //NOTE: using unrolled assignments in this function for speed reasons + + var theme: string = metadata["theme"]; + var x: int = metadata["x"]; + var y: int = metadata["y"]; + var w: int = metadata["w"]; + var h: int = metadata["h"]; + + //etch the floor-space (1-tile margin) + for (var j: int = y + 1; j < y + h - 1; j++) { + for (var i: int = x + 1; i < x + w - 1; i++) { + var floorIndex = rng.generateRandomNumber() % 4; //NOTE: there might not always be only 4 floor sprites + tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 0] = tileset[theme + "-floor-" + string floorIndex][0]; + tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 1] = tileset[theme + "-floor-" + string floorIndex][1]; + 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]; +} + diff --git a/assets/scripts/tilemap/rendernode.toy b/assets/scripts/tilemap/rendernode.toy new file mode 100644 index 0000000..2c83c68 --- /dev/null +++ b/assets/scripts/tilemap/rendernode.toy @@ -0,0 +1,62 @@ +//constants for generating maps +var ROOM_MIN_WIDTH: int const = 4; //minimum safe value 4 +var ROOM_MIN_HEIGHT: int const = 4; + +var ROOM_MAX_WIDTH: int const = 12; +var ROOM_MAX_HEIGHT: int const = 12; + +var CELL_WIDTH: int const = 16; //minimum safe value ROOM_MAX_* + 4 +var CELL_HEIGHT: int const = 16; + +var CELL_COUNT_X: int const = 3; +var CELL_COUNT_Y: int const = 3; + +var tilemap: [int] = null; + +fn setTilemap(node: opaque, t: [int]) { + tilemap = t; +} + +//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 < CELL_HEIGHT * CELL_COUNT_Y; j++) { + for (var i = 0; i < CELL_WIDTH * CELL_COUNT_X; i++) { + //don't render empty tiles + if (tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3] < 0) { + continue; + } + + //set the rect of the node on the tilesheet - the "tilemap" var is a single blob of data + node.setNodeRect( + tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3] * TILE_PIXEL_WIDTH, + tilemap[j * CELL_WIDTH * CELL_COUNT_X * 3 + i * 3 + 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) + ); + } + } +} \ No newline at end of file