-
-
Save dartlab-user/3e3cbff8054475c47a19 to your computer and use it in GitHub Desktop.
Dungeon on a Canvas
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"origin": "dartlab.org", | |
"url": "http://dartlab.org/#:gistId", | |
"history": [ | |
"1dec995218ffb0e27cd2" | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div style="background-color:#FFFFFF;margin:5px;position:absolute;left:5px;width:900px;height:70px; border: 2px solid black; border-radius: 15px;"> | |
<form style="font-size:10pt;"> | |
<div style="position:absolute;top:20px;width:140px;left:10px;">Map Width | |
<input type="range" min="20" max="200" id="MapWidth" value="45" /> | |
</div> | |
<div style="position:absolute;top:20px;width:130px;left:150px;">Map Height | |
<input type="range" min="20" max="100" id="MapHeight" value="40" /> | |
</div> | |
<div style="position:absolute;top:20px;left:300px;width:150px;">Max Room Width | |
<input type="range" min="5" max="20" id="RoomWidth" value="8" /> | |
</div> | |
<div style="position:absolute;top:20px;left:450px;width:140px;">Max Room Height | |
<input type="range" min="5" max="20" id="RoomHeight" value="8" /> | |
</div> | |
<div style="position:absolute;top:20px;left:600px;width:140px;">Room Count | |
<input type="range" min="1" max="50" id="RoomCount" value="8" /> | |
</div> | |
<div style="position:absolute;top:20px;left:750px;width:140px;">Zoom | |
<input type="range" min="1" max="8" id="ZoomLevel" value="2" /> | |
</div> | |
<div style="position:absolute;top:90px;left:10px;width:140px;"> | |
<input type="button" id="RefreshButton" value="Refresh" style="width:120px;"/> | |
</div> | |
</form> | |
</div> | |
<canvas id="surface" style="color:#000088;position:absolute;top:130px;padding: 3px 5px; border: 3px double silver;background: #008800;"> | |
</canvas> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) 2015, Davy Mitchell. All rights reserved. Use of this source code | |
// is governed by a BSD-style license that can be found in the LICENSE file. | |
import 'dart:html'; | |
import 'dart:async'; | |
import 'dart:math'; | |
InputElement mapWidthRange; | |
InputElement mapHeightRange; | |
InputElement roomwidthRange; | |
InputElement roomheightRange; | |
InputElement roomCountRange; | |
InputElement zoomRange; | |
InputElement refreshButton; | |
CanvasElement visualMap; | |
CanvasRenderingContext2D ctx2d; | |
void main() { | |
mapWidthRange = querySelector('#MapWidth')..onChange.listen(remakeMap); | |
mapHeightRange = querySelector('#MapHeight')..onChange.listen(remakeMap); | |
roomwidthRange = querySelector('#RoomWidth')..onChange.listen(remakeMap); | |
roomheightRange = querySelector('#RoomHeight')..onChange.listen(remakeMap); | |
roomCountRange = querySelector('#RoomCount')..onClick.listen(remakeMap); | |
zoomRange = querySelector('#ZoomLevel')..onClick.listen(remakeMap); | |
refreshButton = querySelector('#RefreshButton')..onClick.listen(remakeMap); | |
Future.wait(LoadImages()).then((_) => makeMap()); | |
} | |
void makeMap() { | |
int mapwidth = mapWidthRange.valueAsNumber.toInt(); | |
int mapheight = mapHeightRange.valueAsNumber.toInt(); | |
int roomwidth = roomwidthRange.valueAsNumber.toInt(); | |
int roomheight = roomheightRange.valueAsNumber.toInt(); | |
int roomcount = roomCountRange.valueAsNumber.toInt(); | |
int zoomrange = zoomRange.valueAsNumber.toInt() * 8; | |
PGMap mymap = buildMap(mapwidth, mapheight, roomwidth, roomheight, roomcount); | |
visualMap = querySelector('#surface'); | |
drawMap(visualMap, mymap, zoomrange); | |
} | |
void remakeMap(e) { | |
makeMap(); | |
} | |
const int CORRIDOR = 1; | |
const int LEVELENTRYPOINT = 10; | |
const int LEVELEXITPOINT = 11; | |
const int ROOM = 3; | |
const int VOID = 0; | |
const int WALLCORNER = 8; | |
const int WALLE = 5; | |
const int WALLN = 7; | |
const int WALLS = 6; | |
const int WALLW = 4; | |
const int TREE = 9; | |
/// Block is the smallest unit of a map. | |
class Block { | |
int base = VOID; | |
Block(this.base); | |
} | |
/// Point Class. | |
class Point { | |
int x = 0; | |
int y = 0; | |
Point(this.x, this.y) {} | |
} | |
// Rectangle - defines a rectangular area on the map. | |
class MapRectangle { | |
int x = 0; | |
int y = 0; | |
int width = 0; | |
int height = 0; | |
int x2 = 0; | |
int y2 = 0; | |
MapRectangle(this.x, this.y, this.width, this.height) { | |
update2ndPoints(); | |
} | |
Point getMidPoint() => (new Point(x + (width / 2).round(), y + (height / 2).round())); | |
void update2ndPoints() { | |
x2 = x + width; | |
y2 = y + height; | |
} | |
} | |
int RND(int maxv) { | |
maxv = max(1, maxv); | |
return new Random().nextInt(maxv); | |
} | |
class PGMap { | |
Map<String, Block> Blocks = new Map<String, Block>(); | |
List<MapRectangle> Rooms = new List<MapRectangle>(); | |
int RoomCount = 0; | |
int CorrCount = 0; | |
int Width = 0; | |
int Height = 0; | |
int MaxRoomWidth = 5; | |
int MaxRoomHeight = 8; | |
PGMap(this.Width, this.Height, int RoomWidth, int RoomHeight) { | |
MaxRoomWidth = RoomWidth; | |
MaxRoomHeight = RoomHeight; | |
if (RoomWidth > Width) { | |
RoomWidth = Width - 6; | |
} | |
if (RoomHeight > Height) { | |
RoomHeight = Height - 6; | |
} | |
} | |
void addCorridor(int x, int y, int d, int l) { | |
if (d == 3) { | |
for (int i = y; i < y + l + 1; i++) setBlock(x, i, CORRIDOR); | |
} else if (d == 1) { | |
for (int i = (y - l) + 1; i < y + 1; i++) setBlock(x, i, CORRIDOR); | |
} else if (d == 2) { | |
for (int i = x; i < (x + l - 1) + 1; i++) setBlock(i, y, CORRIDOR); | |
} else if (d == 4) { | |
for (int i = (x - l) + 1; i < x + 1; i++) setBlock(i, y, CORRIDOR); | |
} | |
CorrCount += 1; | |
} | |
void addRoom(int rx, int ry, int rw, int rh) { | |
for (int i = rx; i < (rw + rx - 1) + 1; i++) { | |
for (int j = ry; j < (rh + ry - 1); j++) setBlock(i, j, ROOM); | |
} | |
Rooms.add(new MapRectangle(rx, ry, rw, rh)); | |
RoomCount += 1; | |
} | |
void addRoomRandom() { | |
int rw = 3 + RND(MaxRoomWidth - 3); | |
int rh = 3 + RND(MaxRoomHeight - 3); | |
int rx = 3 + RND(Width - (rw + 6)); | |
int ry = 3 + RND(Height - (rh + 6)); | |
addRoom(rx, ry, rw, rh); | |
} | |
void addRandomObject(int count, int ObjectID, [bool overwrite = false]) { | |
for (int rc = 0; rc < count; rc++) { | |
int rx = RND(Width); | |
int ry = RND(Height); | |
Block b = getBlock(rx, ry); | |
if (b.base == VOID) { | |
b.base = ObjectID; | |
} | |
} | |
} | |
void buildWall(Block currentTile, Block prevTile, Block nextTile, Block upTile, Block loTile) { | |
if (prevTile.base == VOID && (currentTile.base == ROOM || currentTile.base == CORRIDOR)) prevTile.base = WALLW; | |
if (currentTile.base == VOID) { | |
if (nextTile.base == ROOM || nextTile.base == CORRIDOR) currentTile.base = WALLW; else if (prevTile.base == ROOM || prevTile.base == CORRIDOR) currentTile.base = WALLE; else if (upTile.base == ROOM || upTile.base == CORRIDOR) currentTile.base = WALLS; else if (loTile.base == ROOM || loTile.base == CORRIDOR) currentTile.base = WALLN; | |
} | |
} | |
void buildWallCorners(Block currentTile, Block prevTile, Block nextTile, Block upTile, Block loTile) { | |
if (currentTile.base == VOID) { | |
if (isBlockWall(nextTile) && isBlockWall(loTile)) currentTile.base = WALLCORNER; else if (isBlockWall(nextTile) && isBlockWall(upTile)) currentTile.base = WALLCORNER; else if (isBlockWall(prevTile) && isBlockWall(loTile)) currentTile.base = WALLCORNER; else if (isBlockWall(prevTile) && isBlockWall(upTile)) currentTile.base = WALLCORNER; | |
} | |
} | |
void createCorridors() { | |
int P1, P2; | |
int Start, End; | |
MapRectangle S1, S2; | |
int X, Y; | |
int ln; | |
for (int ci = 0; ci < RoomCount - 1; ci++) { | |
S1 = Rooms[ci]; | |
S2 = Rooms[ci + 1]; | |
P1 = S1.getMidPoint().x; | |
P2 = S2.getMidPoint().x; | |
if (P1 > P2) { | |
Start = P2; | |
End = P1; | |
Y = S2.getMidPoint().y; | |
} else { | |
Start = P1; | |
End = P2; | |
Y = S1.getMidPoint().y; | |
} | |
addCorridor(Start, Y, 2, (End - Start) + 1); | |
ln = End - Start; | |
P1 = S1.getMidPoint().y; | |
P2 = S2.getMidPoint().y; | |
if (S1.getMidPoint().x > S2.getMidPoint().x) X = S1.getMidPoint().x; else X = S2.getMidPoint().x; | |
if (P1 > P2) { | |
Start = P2; | |
End = P1; | |
} else { | |
Start = P1; | |
End = P2; | |
} | |
addCorridor(X, Start, 3, (End - Start) + 1); | |
} | |
} | |
void createMap([int maxrooms = 4]) { | |
for (int i = 0; i < maxrooms; i++) addRoomRandom(); | |
createCorridors(); | |
createWalls(); | |
} | |
void createWalls() { | |
traverseMap(buildWall); | |
traverseMap(buildWallCorners); | |
} | |
void generate() { | |
int base = VOID; | |
for (int x = 0; x < Width; x++) { | |
for (int y = 0; y < Height; y++) { | |
Blocks["$x-$y"] = new Block(base); | |
} | |
} | |
} | |
Block getBlock(x, y) { | |
return Blocks["$x-$y"]; | |
} | |
bool isBlockFree(x, y) { | |
int blockType = getBlock(x, y).base; | |
return (blockType == ROOM || blockType == CORRIDOR); | |
} | |
bool isBlockWall(Block b) { | |
return b.base == WALLN || b.base == WALLS || b.base == WALLE || b.base == WALLW; | |
} | |
int setBlock(x, y, v, [AllowOverwrite = false]) { | |
try { | |
if (!AllowOverwrite && Blocks["$x-$y"].base != VOID) return 1; | |
Blocks["$x-$y"].base = v; | |
} catch (o, e) { | |
return -1; | |
} | |
return 0; | |
} | |
String show() { | |
String mapo = ""; | |
for (int y = 0; y < Height; y++) { | |
for (int x = 0; x < Width; x++) { | |
if (Blocks["$x-$y"].base == VOID) mapo += " "; else if (isBlockWall(Blocks["$x-$y"])) mapo += "█"; else if (Blocks["$x-$y"].base == WALLCORNER) mapo += "₪"; else if (Blocks["$x-$y"].base == CORRIDOR) mapo += "░"; else if (Blocks["$x-$y"].base == ROOM) mapo += "░"; else mapo += "${Blocks["$x-$y"].base}"; | |
} | |
mapo += "\r\n"; | |
} | |
//print(mapo); | |
return mapo; | |
} | |
void traverseMap(processor) { | |
Block currentTile; | |
Block nextTile; | |
Block prevTile; | |
Block upTile; | |
Block loTile; | |
for (int x = 1; x < Width - 2; x++) { | |
for (int y = 1; y < Height - 2; y++) { | |
currentTile = Blocks["$x-$y"]; | |
nextTile = Blocks["${x+1}-$y"]; | |
prevTile = Blocks["${x-1}-$y"]; | |
upTile = Blocks["$x-${y+1}"]; | |
loTile = Blocks["$x-${y-1}"]; | |
processor(currentTile, prevTile, nextTile, upTile, loTile); | |
} | |
} | |
} | |
} | |
ImageElement wall, grass, floor, corner, tree; | |
// Start Load of images and waits for them to complete. | |
LoadImages() { | |
var baseUrl = "https://cdn.rawgit.com/daftspaniel/dart-procgendungeon/master/parttwo/web/img"; | |
wall = new ImageElement(src: "$baseUrl/stone.png"); | |
grass = new ImageElement(src: "$baseUrl/grass.png"); | |
floor = new ImageElement(src: "$baseUrl/floor.png"); | |
corner = new ImageElement(src: "$baseUrl/corner.png"); | |
tree = new ImageElement(src: "$baseUrl/ftree.png"); | |
return [wall.onLoad.first, floor.onLoad.first, grass.onLoad.first, corner.onLoad.first, tree.onLoad.first]; | |
} | |
void drawMap(CanvasElement ca, PGMap Amap, int tileSize) { | |
int width = Amap.Width; | |
int height = Amap.Height; | |
int tw = tileSize; | |
ca.width = width * tw; | |
ca.height = height * tw; | |
CanvasRenderingContext2D ctx = ca.getContext("2d"); | |
ctx.imageSmoothingEnabled = false; | |
for (int y = 0; y < height; y++) { | |
for (int x = 0; x < width; x++) { | |
int c = Amap.getBlock(x, y).base; | |
if (c == WALLN || c == WALLS || c == WALLE || c == WALLW) { | |
ctx.drawImageScaled(wall, x * tw, y * tw, tw, tw); | |
} else if (c == WALLCORNER) { | |
ctx.drawImageScaled(corner, x * tw, y * tw, tw, tw); | |
} else if (c == TREE) { | |
ctx.drawImageScaled(grass, x * tw, y * tw, tw, tw); | |
ctx.drawImageScaled(tree, x * tw, (y * tw) - 4, tw, tw); | |
} else if (c == ROOM || c == CORRIDOR) { | |
ctx.drawImageScaled(floor, x * tw, y * tw, tw, tw); | |
} else { | |
ctx.drawImageScaled(grass, x * tw, y * tw, tw, tw); | |
} | |
} | |
} | |
} | |
PGMap buildMap(int mapwidth, int mapheight, int roomwidth, int roomheight, int roomcount) { | |
PGMap mymap = new PGMap(mapwidth, mapheight, roomwidth, roomheight); | |
mymap | |
..generate() | |
..createMap(roomcount) | |
..addRandomObject(roomcount * 3, TREE); | |
return mymap; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-family:sans-serif;font-size:7pt; | |
background-color:#111111; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment