Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
mamayaya1
GitHub Repository: mamayaya1/game
Path: blob/main/projects/cookie-clicker/DungeonGen.js
4626 views
1
/*
2
Orteil's crappy dungeon generation library, 2013
3
Unfinished and buggy, use at your own risk (please credit)
4
http://orteil.dashnet.org
5
6
Rough process (might or might not be what actually happens) :
7
1 make a room in the middle
8
2 pick one of its walls (not corners)
9
3 select a free tile on the other side of that wall
10
4 iteratively expand the selection in one (corridors) or two (rooms) directions, stopping when we meet a wall or when we're above the size threshold
11
5 compute that selection into a room
12
6 add decorations to the room (pillars, water) but only on the center tiles, as to leave free passages (sprinkle destructible decorations anywhere)
13
7 take a random floor tile in the room and repeat step 4, but don't stop at the walls of this room (this creates branching) - repeat about 5 times for interesting shapes
14
8 add those branches to the room
15
9 carve the room into the map, and set the initially selected wall as a door - set the new room's parent to the previous room, and add it to its parent's children
16
10 repeat step 2 with any free wall on the map until the amount of tiles dug is above the desired fill ratio
17
18
Note : I should probably switch the rendering to canvas to allow stuff like occlusion shadows and lights
19
*/
20
21
if (1==1 || undefined==Math.seedrandom)
22
{
23
//seeded random function, courtesy of http://davidbau.com/archives/2010/01/30/random_seeds_coded_hints_and_quintillions.html
24
(function(a,b,c,d,e,f){function k(a){var b,c=a.length,e=this,f=0,g=e.i=e.j=0,h=e.S=[];for(c||(a=[c++]);d>f;)h[f]=f++;for(f=0;d>f;f++)h[f]=h[g=j&g+a[f%c]+(b=h[f])],h[g]=b;(e.g=function(a){for(var b,c=0,f=e.i,g=e.j,h=e.S;a--;)b=h[f=j&f+1],c=c*d+h[j&(h[f]=h[g=j&g+b])+(h[g]=b)];return e.i=f,e.j=g,c})(d)}function l(a,b){var e,c=[],d=(typeof a)[0];if(b&&"o"==d)for(e in a)try{c.push(l(a[e],b-1))}catch(f){}return c.length?c:"s"==d?a:a+"\0"}function m(a,b){for(var d,c=a+"",e=0;c.length>e;)b[j&e]=j&(d^=19*b[j&e])+c.charCodeAt(e++);return o(b)}function n(c){try{return a.crypto.getRandomValues(c=new Uint8Array(d)),o(c)}catch(e){return[+new Date,a,a.navigator.plugins,a.screen,o(b)]}}function o(a){return String.fromCharCode.apply(0,a)}var g=c.pow(d,e),h=c.pow(2,f),i=2*h,j=d-1;c.seedrandom=function(a,f){var j=[],p=m(l(f?[a,o(b)]:0 in arguments?a:n(),3),j),q=new k(j);return m(o(q.S),b),c.random=function(){for(var a=q.g(e),b=g,c=0;h>a;)a=(a+c)*d,b*=d,c=q.g(1);for(;a>=i;)a/=2,b/=2,c>>>=1;return(a+c)/b},p},m(c.random(),b)})(this,[],Math,256,6,52);
25
}
26
27
if (1==1 || undefined==choose) {function choose(arr) {if (arr.length==0) return 0; else return arr[Math.floor(Math.random()*arr.length)];}}
28
29
30
var DungeonGen=function()
31
{
32
var TILE_EMPTY=0;//solid
33
var TILE_LIMIT=-100;//can't build anything here; edges of map
34
var TILE_FLOOR_EDGE=100;
35
var TILE_FLOOR_CENTER=110;
36
var TILE_DOOR=200;
37
var TILE_PILLAR=300;//not just pillars, could be any type of repetitive decoration
38
var TILE_WATER=400;
39
var TILE_WALL=500;
40
var TILE_WALL_CORNER=510;
41
var TILE_ENTRANCE=250;
42
var TILE_EXIT=260;
43
44
var colors=[];
45
colors[TILE_EMPTY]='000';
46
colors[TILE_LIMIT]='900';
47
colors[TILE_FLOOR_EDGE]='ffc';
48
colors[TILE_FLOOR_CENTER]='ff9';
49
colors[TILE_DOOR]='f9f';
50
colors[TILE_PILLAR]='990';
51
colors[TILE_WATER]='99f';
52
colors[TILE_WALL]='960';
53
colors[TILE_WALL_CORNER]='630';
54
colors[TILE_ENTRANCE]='f9f';
55
colors[TILE_EXIT]='f9f';
56
57
var rand=function(a,b){return Math.floor(Math.random()*(b-a+1)+a);}//return random value between a and b
58
59
var Patterns=[];
60
this.Pattern=function(name,func)
61
{
62
this.name=name;
63
this.func=func;
64
Patterns.push(this);
65
}
66
new this.Pattern('Pillars',function(x,y,room)
67
{
68
if ((x+room.x)%2==0 && (y+room.y)%2==0 && Math.random()<0.8) return TILE_PILLAR;
69
return 0;
70
});
71
new this.Pattern('Large pillars',function(x,y,room)
72
{
73
if ((x+room.x)%3<2 && (y+room.y)%3<2 && Math.random()<0.8) return TILE_PILLAR;
74
return 0;
75
});
76
new this.Pattern('Sparse pillars',function(x,y,room)
77
{
78
if ((x+room.x)%3==0 && (y+room.y)%3==0 && Math.random()<0.8) return TILE_PILLAR;
79
return 0;
80
});
81
new this.Pattern('Lines',function(x,y,room)
82
{
83
if (room.x%2==0) if ((x+room.x)%2==0 && Math.random()<0.98) return TILE_PILLAR;
84
if (room.x%2==1) if ((y+room.y)%2==0 && Math.random()<0.98) return TILE_PILLAR;
85
return 0;
86
});
87
88
89
var getRandomPattern=function()
90
{return choose(Patterns);}
91
92
var defaultGenerator=function(me)
93
{
94
me.roomSize=10;
95
me.corridorSize=5;
96
me.fillRatio=1/3;
97
me.corridorRatio=0.2;
98
me.pillarRatio=0.2;
99
me.waterRatio=0;
100
me.branching=4;
101
me.sizeVariance=0.2;
102
103
me.fillRatio=0.1+Math.random()*0.4;
104
me.roomSize=Math.ceil(rand(5,15)*me.fillRatio*2);
105
me.corridorSize=Math.ceil(rand(1,7)*me.fillRatio*2);
106
me.corridorRatio=Math.random()*0.8+0.1;
107
me.pillarRatio=Math.random()*0.5+0.5;
108
me.waterRatio=Math.pow(Math.random(),2);
109
me.branching=Math.floor(Math.random()*6);
110
me.sizeVariance=Math.random();
111
}
112
113
114
this.Map=function(w,h,seed,params)
115
{
116
//create a new map
117
//leave the seed out for a random seed
118
//params is an object that contains custom parameters as defined in defaultGenerator
119
//example : MyMap=new DungeonGen.Map(30,30,MySeed,{waterRatio:0.8}); (80 percent of the rooms will contain water)
120
if (undefined!=seed) this.seed=seed; else {Math.seedrandom();this.seed=Math.random();}
121
Math.seedrandom(this.seed);
122
this.seedState=Math.random;
123
this.w=w||20;
124
this.h=h||20;
125
126
this.roomsAreHidden=0;
127
128
this.rooms=[];
129
this.freeWalls=[];//all walls that would be a good spot for a door
130
this.freeTiles=[];//all passable floor tiles
131
this.doors=[];
132
this.tiles=this.w*this.h;
133
this.tilesDug=0;
134
this.digs=0;//amount of digging steps
135
this.stuck=0;//how many times we ran into a problem; stop digging if we get too many of these
136
137
this.data=[];//fill the map with 0
138
for (var x=0;x<this.w;x++)
139
{
140
this.data[x]=[];
141
for (var y=0;y<this.h;y++)
142
{
143
this.data[x][y]=[TILE_EMPTY,-1,0];//data is stored as [tile system type,room id,tile displayed type] (-1 is no room)
144
if (x==0 || y==0 || x==this.w-1 || y==this.h-1) this.data[x][y]=[TILE_LIMIT,-1,0];
145
}
146
}
147
148
defaultGenerator(this);
149
if (params)
150
{
151
for (var i in params)
152
{
153
this[i]=params[i];
154
}
155
}
156
Math.seedrandom();
157
158
}
159
160
this.Map.prototype.getType=function(x,y){return this.data[x][y][0];}
161
this.Map.prototype.getRoom=function(x,y){if (this.data[x][y][1]!=-1) return this.rooms[this.data[x][y][1]]; else return -1;}
162
this.Map.prototype.getTile=function(x,y){return this.rooms[this.data[x][y][2]];}
163
164
this.Map.prototype.isWall=function(x,y)
165
{
166
var n=0;
167
for (var i in this.freeWalls){if (this.freeWalls[i][0]==x && this.freeWalls[i][1]==y) return n; else n++;}
168
return -1;
169
}
170
this.Map.prototype.isFloor=function(x,y)
171
{
172
var n=0;
173
for (var i in this.freeTiles){if (this.freeTiles[i][0]==x && this.freeTiles[i][1]==y) return n; else n++;}
174
return -1;
175
}
176
this.Map.prototype.removeFreeTile=function(x,y)
177
{
178
this.freeTiles.splice(this.isFloor(x,y),1);
179
}
180
181
this.Map.prototype.fill=function(what)
182
{
183
//fill with something (either a set value, or a function that takes the map, a position X and a position Y as arguments)
184
//NOTE : this also resets the rooms!
185
//example : MyMap.fill(function(m,x,y){return Math.floor((Math.random());});
186
//...will fill the map with 0s and 1s
187
var func=0;
188
if (typeof(what)=='function') func=1;
189
for (var x=0;x<this.w;x++){for (var y=0;y<this.h;y++){
190
if (func) this.data[x][y]=[what(this,x,y),-1,0]; else this.data[x][y]=[what,-1,0];
191
}}
192
this.rooms=[];
193
}
194
195
this.Map.prototype.fillZone=function(X,Y,W,H,what)
196
{
197
//just plain fill a rectangle
198
for (var x=X;x<X+W;x++){for (var y=Y;y<Y+H;y++){
199
this.data[x][y][0]=what;
200
}}
201
}
202
203
this.Map.prototype.getRoomTile=function(room,x,y)
204
{
205
var n=0;
206
for (var i in room.tiles) {if (room.tiles[i].x==x && room.tiles[i].y==y) return n; else n++;}
207
return -1;
208
}
209
210
this.Map.prototype.getFloorTileInRoom=function(room)
211
{
212
var tiles=[];
213
for (var i in room.tiles) {if (room.tiles[i].type==TILE_FLOOR_EDGE || room.tiles[i].type==TILE_FLOOR_CENTER) tiles.push(room.tiles[i]);}
214
return choose(tiles);
215
}
216
217
this.Map.prototype.canPlaceRoom=function(rx,ry,rw,rh)
218
{
219
if (rx<2 || ry<2 || rx+rw>=this.w-1 || ry+rh>=this.h-1) return false;
220
for (var x=rx;x<rx+rw;x++)
221
{
222
for (var y=ry;y<ry+rh;y++)
223
{
224
var tile=this.getType(x,y);
225
var room=this.getRoom(x,y);
226
if (tile==TILE_LIMIT) return false;
227
if (room!=-1) return false;
228
}
229
}
230
return true;
231
}
232
233
this.Map.prototype.setRoomTile=function(room,x,y,tile)
234
{
235
//var mapTile=this.getType(x,y);
236
var oldTile=this.getRoomTile(room,x,y);
237
var oldTileType=oldTile!=-1?room.tiles[oldTile].type:-1;
238
if (oldTile!=-1 && (
239
//(tile!=TILE_FLOOR_EDGE && tile!=TILE_FLOOR_CENTER) ||// && (oldTileType!=TILE_FLOOR_EDGE && oldTileType!=TILE_FLOOR_CENTER)) ||
240
//(tile!=TILE_FLOOR_EDGE && tile!=TILE_FLOOR_CENTER && (oldTileType!=TILE_FLOOR_EDGE && oldTileType!=TILE_FLOOR_CENTER)) ||
241
(tile==TILE_WALL || tile==TILE_WALL_CORNER) ||//don't place a wall over an existing room
242
(tile==TILE_FLOOR_EDGE && oldTileType==TILE_FLOOR_CENTER)//don't place an edge floor over a center floor
243
)) {return false;}
244
else
245
{
246
if (oldTile!=-1) room.tiles.splice(oldTile,1);
247
room.tiles.push({x:x,y:y,type:tile,score:0});
248
if ((tile==TILE_FLOOR_EDGE || tile==TILE_FLOOR_CENTER) && (oldTileType!=TILE_FLOOR_EDGE && oldTileType!=TILE_FLOOR_CENTER)) room.freeTiles++;
249
else if (tile!=TILE_FLOOR_EDGE && tile!=TILE_FLOOR_CENTER && (oldTileType==TILE_FLOOR_EDGE || oldTileType==TILE_FLOOR_CENTER)) room.freeTiles--;
250
return true;
251
}
252
}
253
254
this.Map.prototype.expandRoom=function(room,rx,ry,rw,rh)
255
{
256
var x=0;var y=0;
257
//floor
258
for (var x=rx;x<rx+rw;x++){for (var y=ry;y<ry+rh;y++){
259
this.setRoomTile(room,x,y,TILE_FLOOR_EDGE);
260
}}
261
for (var x=rx+1;x<rx+rw-1;x++){for (var y=ry+1;y<ry+rh-1;y++){
262
this.setRoomTile(room,x,y,TILE_FLOOR_CENTER);
263
}}
264
//walls
265
y=ry-1;
266
for (var x=rx;x<rx+rw;x++){
267
this.setRoomTile(room,x,y,TILE_WALL);
268
}
269
y=ry+rh;
270
for (var x=rx;x<rx+rw;x++){
271
this.setRoomTile(room,x,y,TILE_WALL);
272
}
273
x=rx-1;
274
for (var y=ry;y<ry+rh;y++){
275
this.setRoomTile(room,x,y,TILE_WALL);
276
}
277
x=rx+rw;
278
for (var y=ry;y<ry+rh;y++){
279
this.setRoomTile(room,x,y,TILE_WALL);
280
}
281
//corners
282
x=rx-1;y=ry-1;
283
this.setRoomTile(room,x,y,TILE_WALL_CORNER);
284
x=rx+rw;y=ry-1;
285
this.setRoomTile(room,x,y,TILE_WALL_CORNER);
286
x=rx-1;y=ry+rh;
287
this.setRoomTile(room,x,y,TILE_WALL_CORNER);
288
x=rx+rw;y=ry+rh;
289
this.setRoomTile(room,x,y,TILE_WALL_CORNER);
290
291
//decoration
292
var water=Math.random()<this.waterRatio?1:0;
293
var pattern=Math.random()<this.pillarRatio?getRandomPattern():0;
294
for (var x=rx;x<rx+rw;x++){for (var y=ry;y<ry+rh;y++){
295
if (room.tiles[this.getRoomTile(room,x,y)].type==TILE_FLOOR_CENTER)
296
{
297
var tile=0;
298
if (water!=0) tile=TILE_WATER;
299
if (pattern!=0)
300
{
301
tile=pattern.func(x,y,room)||tile;
302
}
303
if (tile!=0) this.setRoomTile(room,x,y,tile);
304
}
305
}}
306
}
307
308
this.Map.prototype.newRoom=function(x,y,w,h,parent)
309
{
310
//create a new abstract room, ready to be carved
311
var room={};
312
room.id=this.rooms.length;
313
room.w=w;//||rand(2,this.roomSize);
314
room.h=h;//||rand(2,this.roomSize);
315
room.x=x||rand(1,this.w-room.w-1);
316
room.y=y||rand(1,this.h-room.h-1);
317
room.tiles=[];
318
room.freeTiles=0;
319
room.parent=parent?parent:-1;
320
room.children=[];
321
room.gen=0;
322
room.door=0;
323
room.corridor=Math.random()<this.corridorRatio?1:0;
324
room.hidden=this.roomsAreHidden;//if 1, don't draw
325
//if (room.parent!=-1) room.corridor=!room.parent.corridor;//alternate rooms and corridors
326
327
return room;
328
}
329
this.Map.prototype.planRoom=function(room)
330
{
331
var branches=this.branching+1;
332
var forcedExpansions=[];
333
var w=room.w;
334
var h=room.h;
335
while (w>0 && h>0)
336
{
337
if (w>0) {forcedExpansions.push(1,3);w--;}
338
if (h>0) {forcedExpansions.push(2,4);h--;}
339
}
340
341
for (var i=0;i<branches;i++)
342
{
343
var steps=0;
344
var expansions=[];
345
if (!room.corridor)
346
{
347
expansions=[1,2,3,4];
348
steps=this.roomSize;
349
}
350
else
351
{
352
expansions=choose([[1,3],[2,4]]);
353
steps=this.corridorSize;
354
}
355
steps=Math.max(room.w+room.h,Math.ceil(steps*(1-Math.random()*this.sizeVariance)));
356
if (room.tiles.length==0) {var rx=room.x;var ry=room.y;var rw=1;var rh=1;}
357
else {var randomTile=this.getFloorTileInRoom(room);var rx=randomTile.x;var ry=randomTile.y;var rw=1;var rh=1;}
358
for (var ii=0;ii<steps;ii++)
359
{
360
if (expansions.length==0) break;
361
var xd=0;var yd=0;var wd=0;var hd=0;
362
var side=choose(expansions);
363
if (forcedExpansions.length>0) side=forcedExpansions[0];
364
if (side==1) {xd=-1;wd=1;}
365
else if (side==2) {yd=-1;hd=1;}
366
else if (side==3) {wd=1;}
367
else if (side==4) {hd=1;}
368
if (this.canPlaceRoom(rx+xd,ry+yd,rw+wd,rh+hd)) {rx+=xd;ry+=yd;rw+=wd;rh+=hd;} else expansions.splice(expansions.indexOf(side),1);
369
if (forcedExpansions.length>0) forcedExpansions.splice(0,1);
370
}
371
if (rw>1 || rh>1)
372
{
373
this.expandRoom(room,rx,ry,rw,rh);
374
}
375
}
376
}
377
378
379
this.Map.prototype.carve=function(room)
380
{
381
//carve a room into the map
382
for (var i in room.tiles)
383
{
384
var thisTile=room.tiles[i];
385
var x=thisTile.x;var y=thisTile.y;
386
var myType=this.data[x][y][0];
387
var type=thisTile.type;
388
389
if ((type==TILE_WALL || type==TILE_WALL_CORNER) && this.isWall(x,y)!=-1) {this.freeWalls.splice(this.isWall(x,y),1);}
390
391
if (this.data[x][y][1]!=-1 && (type==TILE_WALL || type==TILE_WALL_CORNER)) {}
392
else
393
{
394
if (this.data[x][y][1]==-1) this.tilesDug++;
395
this.data[x][y]=[thisTile.type,room.id,0];
396
if (x>1 && y>1 && x<this.w-2 && y<this.h-2 && type==TILE_WALL) this.freeWalls.push([x,y]);
397
if (type==TILE_FLOOR_EDGE || type==TILE_FLOOR_CENTER) this.freeTiles.push([x,y]);
398
}
399
var pos=[x,y];
400
}
401
this.rooms[room.id]=room;
402
}
403
404
this.Map.prototype.newRandomRoom=function(params)
405
{
406
var success=1;
407
params=params||{};//params is an object such as {corridor:1}
408
var door=choose(this.freeWalls);//select a free wall to use as a door
409
if (!door) {success=0;}
410
else
411
{
412
//this.data[door[0]][door[1]][0]=TILE_LIMIT;//not door
413
var parentRoom=this.getRoom(door[0],door[1]);
414
var sides=[];//select a free side of that door
415
if (this.getType(door[0]-1,door[1])==TILE_EMPTY) sides.push([-1,0]);
416
if (this.getType(door[0]+1,door[1])==TILE_EMPTY) sides.push([1,0]);
417
if (this.getType(door[0],door[1]-1)==TILE_EMPTY) sides.push([0,-1]);
418
if (this.getType(door[0],door[1]+1)==TILE_EMPTY) sides.push([0,1]);
419
var side=choose(sides);
420
if (!side) {success=0;this.freeWalls.splice(this.isWall(door[0],door[1]),1);}
421
else
422
{
423
var room=this.newRoom(door[0]+side[0],door[1]+side[1],0,0,parentRoom);//try a new room from this spot
424
for (var i in params)
425
{
426
room[i]=params[i];
427
}
428
this.planRoom(room);
429
if (room.tiles.length>0 && room.freeTiles>0)//we got a decent room
430
{
431
this.carve(room);
432
this.data[door[0]][door[1]][0]=TILE_DOOR;//place door
433
room.door=[door[0],door[1]];
434
this.data[door[0]][door[1]][1]=room.id;//set ID
435
this.freeWalls.splice(this.isWall(door[0],door[1]),1);//the door isn't a wall anymore
436
this.doors.push([door[0],door[1],room]);
437
//remove free tiles on either side of the door
438
if (this.isFloor(door[0]+side[0],door[1]+side[1])!=-1) this.removeFreeTile(door[0]+side[0],door[1]+side[1]);
439
if (this.isFloor(door[0]-side[0],door[1]-side[1])!=-1) this.removeFreeTile(door[0]-side[0],door[1]-side[1]);
440
room.parent=parentRoom;
441
parentRoom.children.push(room);
442
room.gen=parentRoom.gen+1;
443
}
444
else//not a good spot; remove this tile from the list of walls
445
{
446
this.freeWalls.splice(this.isWall(door[0],door[1]),1);
447
success=0;
448
}
449
}
450
}
451
if (success) return room;
452
else return 0;
453
}
454
455
this.Map.prototype.getRandomSpotInRoom=function(room)
456
{
457
var listOfTiles=[];
458
for (var i in room.tiles)
459
{
460
if ((room.tiles[i].type==TILE_FLOOR_EDGE || room.tiles[i].type==TILE_FLOOR_CENTER) && this.isFloor(room.tiles[i].x,room.tiles[i].y)!=-1)
461
{
462
listOfTiles.push(room.tiles[i]);
463
}
464
}
465
if (listOfTiles.length==0) return -1;
466
return choose(listOfTiles);
467
}
468
this.Map.prototype.getBestSpotInRoom=function(room)
469
{
470
var highest=-1;
471
var listOfHighest=[];
472
for (var i in room.tiles)
473
{
474
if ((room.tiles[i].type==TILE_FLOOR_EDGE || room.tiles[i].type==TILE_FLOOR_CENTER) && this.isFloor(room.tiles[i].x,room.tiles[i].y)!=-1)
475
{
476
if (room.tiles[i].score>highest)
477
{
478
listOfHighest=[];
479
highest=room.tiles[i].score;
480
listOfHighest.push(room.tiles[i]);
481
}
482
else if (room.tiles[i].score==highest)
483
{
484
listOfHighest.push(room.tiles[i]);
485
}
486
}
487
}
488
if (listOfHighest.length==0) return -1;
489
return choose(listOfHighest);
490
}
491
this.Map.prototype.getEarliestRoom=function()
492
{
493
return this.rooms[0];
494
}
495
this.Map.prototype.getDeepestRoom=function()
496
{
497
var deepest=0;
498
var deepestRoom=this.rooms[0];
499
for (var i in this.rooms)
500
{
501
if ((this.rooms[i].gen+Math.sqrt(this.rooms[i].freeTiles)*0.05)>=deepest && this.rooms[i].corridor==0 && this.rooms[i].freeTiles>4) {deepest=(this.rooms[i].gen+Math.sqrt(this.rooms[i].freeTiles)*0.05);deepestRoom=this.rooms[i];}
502
}
503
return deepestRoom;
504
}
505
506
this.Map.prototype.dig=function()
507
{
508
//one step in which we try to carve new stuff
509
//returns 0 when we couldn't dig this step, 1 when we could, and 2 when the digging is complete
510
Math.random=this.seedState;
511
512
var badDig=0;
513
514
if (this.digs==0)//first dig : build a starting room in the middle of the map
515
{
516
var w=rand(3,7);
517
var h=rand(3,7);
518
var room=this.newRoom(Math.floor(this.w/2-w/2),Math.floor(this.h/2-h/2),w,h);
519
room.corridor=0;
520
this.planRoom(room);
521
this.carve(room);
522
}
523
else
524
{
525
if (this.newRandomRoom()==0) badDig++;
526
}
527
if (badDig>0) this.stuck++;
528
529
this.digs++;
530
531
var finished=0;
532
if (this.tilesDug>=this.tiles*this.fillRatio) finished=1;
533
if (this.stuck>100) finished=1;
534
535
if (finished==1)//last touch : try to add a whole room at the end
536
{
537
for (var i=0;i<10;i++)
538
{
539
var newRoom=this.newRandomRoom({corridor:0,w:rand(3,7),h:rand(3,7)});
540
if (newRoom!=0 && newRoom.freeTiles>15) break;
541
}
542
}
543
544
Math.seedrandom();
545
if (finished==1) return 1; else if (badDig>0) return -1; else return 0;
546
}
547
548
this.Map.prototype.finish=function()
549
{
550
//touch up the map : add pillars in corners etc
551
/*
552
//set paths
553
for (var i in this.rooms)
554
{
555
var me=this.rooms[i];
556
if (me.door!=0)
557
{
558
var doors=[];
559
doors.push(me.door);
560
for (var ii in me.children)
561
{
562
if (me.children[ii].door!=0) doors.push(me.children[ii].door);
563
}
564
for (var ii in doors)
565
{
566
this.data[doors[ii][0]][doors[ii][1]][0]=TILE_LIMIT;
567
//ideally we should run agents that step from each door to the next
568
}
569
}
570
}
571
*/
572
for (var i in this.rooms)
573
{
574
var pillars=Math.random()<this.pillarRatio;
575
for (var ii in this.rooms[i].tiles)
576
{
577
var x=this.rooms[i].tiles[ii].x;
578
var y=this.rooms[i].tiles[ii].y;
579
var me=this.data[x][y][0];
580
var x1=this.data[x-1][y][0];
581
var x2=this.data[x+1][y][0];
582
var y1=this.data[x][y-1][0];
583
var y2=this.data[x][y+1][0];
584
var xy1=this.data[x-1][y-1][0];
585
var xy2=this.data[x+1][y-1][0];
586
var xy3=this.data[x-1][y+1][0];
587
var xy4=this.data[x+1][y+1][0];
588
589
var walls=0;
590
if ((x1==TILE_WALL||x1==TILE_WALL_CORNER)) walls++;
591
if ((y1==TILE_WALL||y1==TILE_WALL_CORNER)) walls++;
592
if ((x2==TILE_WALL||x2==TILE_WALL_CORNER)) walls++;
593
if ((y2==TILE_WALL||y2==TILE_WALL_CORNER)) walls++;
594
if ((xy1==TILE_WALL||xy1==TILE_WALL_CORNER)) walls++;
595
if ((xy2==TILE_WALL||xy2==TILE_WALL_CORNER)) walls++;
596
if ((xy3==TILE_WALL||xy3==TILE_WALL_CORNER)) walls++;
597
if ((xy4==TILE_WALL||xy4==TILE_WALL_CORNER)) walls++;
598
599
var floors=0;
600
if ((x1==TILE_FLOOR_CENTER||x1==TILE_FLOOR_EDGE)) floors++;
601
if ((y1==TILE_FLOOR_CENTER||y1==TILE_FLOOR_EDGE)) floors++;
602
if ((x2==TILE_FLOOR_CENTER||x2==TILE_FLOOR_EDGE)) floors++;
603
if ((y2==TILE_FLOOR_CENTER||y2==TILE_FLOOR_EDGE)) floors++;
604
if ((xy1==TILE_FLOOR_CENTER||xy1==TILE_FLOOR_EDGE)) floors++;
605
if ((xy2==TILE_FLOOR_CENTER||xy2==TILE_FLOOR_EDGE)) floors++;
606
if ((xy3==TILE_FLOOR_CENTER||xy3==TILE_FLOOR_EDGE)) floors++;
607
if ((xy4==TILE_FLOOR_CENTER||xy4==TILE_FLOOR_EDGE)) floors++;
608
609
var complete=0;
610
if (walls+floors==8) complete=1;
611
612
var angle=0;
613
if (complete)
614
{
615
var top=0;
616
var left=0;
617
var right=0;
618
var bottom=0;
619
if ((xy1==TILE_WALL||xy1==TILE_WALL_CORNER) && (y1==TILE_WALL||y1==TILE_WALL_CORNER) && (xy2==TILE_WALL||xy2==TILE_WALL_CORNER)) top=1;
620
else if ((xy1==TILE_FLOOR_CENTER||xy1==TILE_FLOOR_EDGE) && (y1==TILE_FLOOR_CENTER||y1==TILE_FLOOR_EDGE) && (xy2==TILE_FLOOR_CENTER||xy2==TILE_FLOOR_EDGE)) top=-1;
621
if ((xy2==TILE_WALL||xy2==TILE_WALL_CORNER) && (x2==TILE_WALL||x2==TILE_WALL_CORNER) && (xy4==TILE_WALL||xy4==TILE_WALL_CORNER)) right=1;
622
else if ((xy2==TILE_FLOOR_CENTER||xy2==TILE_FLOOR_EDGE) && (x2==TILE_FLOOR_CENTER||x2==TILE_FLOOR_EDGE) && (xy4==TILE_FLOOR_CENTER||xy4==TILE_FLOOR_EDGE)) right=-1;
623
if ((xy1==TILE_WALL||xy1==TILE_WALL_CORNER) && (x1==TILE_WALL||x1==TILE_WALL_CORNER) && (xy3==TILE_WALL||xy3==TILE_WALL_CORNER)) left=1;
624
else if ((xy1==TILE_FLOOR_CENTER||xy1==TILE_FLOOR_EDGE) && (x1==TILE_FLOOR_CENTER||x1==TILE_FLOOR_EDGE) && (xy3==TILE_FLOOR_CENTER||xy3==TILE_FLOOR_EDGE)) left=-1;
625
if ((xy3==TILE_WALL||xy3==TILE_WALL_CORNER) && (y2==TILE_WALL||y2==TILE_WALL_CORNER) && (xy4==TILE_WALL||xy4==TILE_WALL_CORNER)) bottom=1;
626
else if ((xy3==TILE_FLOOR_CENTER||xy3==TILE_FLOOR_EDGE) && (y2==TILE_FLOOR_CENTER||y2==TILE_FLOOR_EDGE) && (xy4==TILE_FLOOR_CENTER||xy4==TILE_FLOOR_EDGE)) bottom=-1;
627
if ((top==1 && bottom==-1) || (top==-1 && bottom==1) || (left==1 && right==-1) || (left==-1 && right==1)) angle=1;
628
}
629
630
if (pillars && Math.random()<0.8 && this.rooms[i].freeTiles>4)
631
{
632
if ((angle==1 || (complete && walls==7)) && me==TILE_FLOOR_EDGE && x1!=TILE_DOOR && x2!=TILE_DOOR && y1!=TILE_DOOR && y2!=TILE_DOOR)
633
{
634
this.data[x][y][0]=TILE_PILLAR;
635
me=TILE_PILLAR;
636
this.removeFreeTile(x,y);
637
this.rooms[i].freeTiles--;
638
}
639
}
640
641
//calculate score (for placing items and exits)
642
if (top==1 || bottom==1 || left==1 || right==1)
643
{
644
this.rooms[i].tiles[ii].score+=2;
645
}
646
if (walls>5 || floors>5)
647
{
648
this.rooms[i].tiles[ii].score+=1;
649
}
650
if (walls==7 || floors==8)
651
{
652
this.rooms[i].tiles[ii].score+=5;
653
}
654
if ((me!=TILE_FLOOR_CENTER && me!=TILE_FLOOR_EDGE) || x1==TILE_DOOR || x2==TILE_DOOR || y1==TILE_DOOR || y2==TILE_DOOR) this.rooms[i].tiles[ii].score=-1;
655
656
}
657
}
658
659
660
661
//carve entrance and exit
662
var entrance=this.getBestSpotInRoom(this.getEarliestRoom());
663
this.data[entrance.x][entrance.y][0]=TILE_ENTRANCE;
664
this.entrance=[entrance.x,entrance.y];
665
entrance.score=0;
666
this.removeFreeTile(entrance.x,entrance.y);
667
var exit=this.getBestSpotInRoom(this.getDeepestRoom());
668
this.data[exit.x][exit.y][0]=TILE_EXIT;
669
this.exit=[exit.x,exit.y];
670
this.removeFreeTile(exit.x,exit.y);
671
exit.score=0;
672
673
/*
674
for (var i in this.doors)//remove door tiles (to add later; replace the tiles by entities that delete themselves when opened)
675
{
676
this.data[this.doors[i][0]][this.doors[i][1]][0]=TILE_FLOOR_EDGE;
677
}
678
*/
679
}
680
681
this.Map.prototype.isObstacle=function(x,y)
682
{
683
var free=[TILE_FLOOR_EDGE,TILE_FLOOR_CENTER,TILE_DOOR,TILE_ENTRANCE,TILE_EXIT];
684
for (var i in free)
685
{
686
if (this.data[x][y][0]==free[i]) return 0;
687
}
688
return 1;
689
}
690
691
var joinTile=function(map,x,y,joinWith)
692
{
693
//for the tile at x,y, return 2 if it joins with its horizontal neighbors, 3 if it joins with its vertical neighbors, 1 if it joins with either both or neither.
694
//joinWith contains the tile types that count as joinable, in addition to this tile. (don't add the tested tile to joinWith!)
695
var p=1;
696
var me=map.data[x][y][0];
697
var x1=map.data[x-1][y][0];
698
var x2=map.data[x+1][y][0];
699
var y1=map.data[x][y-1][0];
700
var y2=map.data[x][y+1][0];
701
joinWith.push(me);
702
var joinsX=0;
703
for (var i in joinWith)
704
{
705
if (x1==joinWith[i]) joinsX++;
706
if (x2==joinWith[i]) joinsX++;
707
}
708
var joinsY=0;
709
for (var i in joinWith)
710
{
711
if (y1==joinWith[i]) joinsY++;
712
if (y2==joinWith[i]) joinsY++;
713
}
714
if (joinsX==2 && joinsY==2) p=1;
715
else if (joinsX==2) p=2;
716
else if (joinsY==2) p=3;
717
return p;
718
}
719
this.Map.prototype.getPic=function(x,y)
720
{
721
//return a position [x,y] in the tiles (as 0, 1, 2...) for the tile on the map at position x,y
722
if (Tiles[this.data[x][y][2]])
723
{
724
if (Tiles[this.data[x][y][2]].joinType=='join')
725
{
726
var thisPic=Tiles[this.data[x][y][2]].pic;
727
thisPic=[thisPic[0],thisPic[1]];//why is this even necessary?
728
var joinWith=[];
729
if (this.data[x][y][0]==TILE_WALL) joinWith.push(TILE_WALL_CORNER);
730
else if (this.data[x][y][0]==TILE_DOOR) joinWith.push(TILE_WALL,TILE_WALL_CORNER);
731
thisPic[0]+=joinTile(this,x,y,joinWith)-1;
732
return thisPic;
733
}
734
else if (Tiles[this.data[x][y][2]].joinType=='random3')
735
{
736
var thisPic=Tiles[this.data[x][y][2]].pic;
737
thisPic=[thisPic[0],thisPic[1]];
738
thisPic[0]+=Math.floor(Math.random()*3);
739
return thisPic;
740
}
741
return Tiles[this.data[x][y][2]].pic;
742
}
743
return [0,0];
744
}
745
746
var Tiles=[];
747
var TilesByName=[];
748
this.Tile=function(name,pic,joinType)
749
{
750
this.name=name;
751
this.pic=pic;
752
this.joinType=joinType||'none';
753
this.id=Tiles.length;
754
Tiles[this.id]=this;
755
TilesByName[this.name]=this;
756
}
757
new this.Tile('void',[0,0]);
758
this.loadTiles=function(tiles)
759
{
760
for (var i in tiles)
761
{
762
var name=tiles[i][0];
763
var pic=tiles[i][1];
764
var joinType=tiles[i][2];
765
new this.Tile(name,pic,joinType);
766
}
767
}
768
769
var computeTile=function(tile,tiles,value,name)
770
{
771
if (tile==value && tiles[name]) return TilesByName[tiles[name]];
772
return 0;
773
}
774
this.Map.prototype.assignTiles=function(room,tiles)
775
{
776
//set the displayed tiles for this room
777
for (var i in room.tiles)
778
{
779
var type=Tiles[0];
780
var me=room.tiles[i];
781
var tile=this.data[me.x][me.y][0];
782
type=computeTile(tile,tiles,TILE_WALL_CORNER,'wall corner')||type;
783
type=computeTile(tile,tiles,TILE_WALL,'wall')||type;
784
type=computeTile(tile,tiles,TILE_FLOOR_EDGE,'floor edges')||type;
785
type=computeTile(tile,tiles,TILE_FLOOR_CENTER,'floor')||type;
786
type=computeTile(tile,tiles,TILE_PILLAR,'pillar')||type;
787
type=computeTile(tile,tiles,TILE_DOOR,'door')||type;
788
type=computeTile(tile,tiles,TILE_WATER,'water')||type;
789
type=computeTile(tile,tiles,TILE_ENTRANCE,'entrance')||type;
790
type=computeTile(tile,tiles,TILE_EXIT,'exit')||type;
791
792
this.data[me.x][me.y][2]=type.id;
793
}
794
}
795
796
797
this.Map.prototype.draw=function(size)
798
{
799
//return a string containing a rough visual representation of the map
800
var str='';
801
var size=size||10;
802
for (var y=0;y<this.h;y++){for (var x=0;x<this.w;x++){
803
var text='';
804
if (this.isFloor(x,y)!=-1) text='o';
805
if (this.isWall(x,y)!=-1) text+='x';
806
var room=this.getRoom(x,y);
807
var opacity=Math.max(0.1,1-(this.getRoom(x,y).gen/10));
808
var title=room.freeTiles;//this.data[x][y][0].toString();
809
text='';
810
str+='<div style="opacity:'+opacity+';width:'+size+'px;height:'+size+'px;position:absolute;left:'+(x*size)+'px;top:'+(y*size)+'px;display:block;padding:0px;margin:0px;background:#'+colors[this.data[x][y][0]]+';color:#999;" title="'+title+'">'+text+'</div>';
811
}
812
str+='<br>';
813
}
814
str='<div style="position:relative;width:'+(this.w*size)+'px;height:'+(this.h*size)+'px;background:#000;font-family:Courier;font-size:'+size+'px;float:left;margin:10px;">'+str+'</div>';
815
return str;
816
}
817
818
this.Map.prototype.drawDetailed=function()
819
{
820
//return a string containing a rough visual representation of the map (with graphics)
821
var str='';
822
var size=16;
823
for (var y=0;y<this.h;y++){for (var x=0;x<this.w;x++){
824
var room=this.getRoom(x,y);
825
//var opacity=Math.max(0.1,room.tiles[this.getRoomTile(room,x,y)].score);
826
var opacity=1;
827
var title='void';
828
if (room!=-1)
829
{
830
opacity=Math.max(0.1,1-room.gen/5);
831
if (this.data[x][y][0]==TILE_ENTRANCE || this.data[x][y][0]==TILE_EXIT) opacity=1;
832
title=(room.corridor?'corridor':'room')+' '+room.id+' | depth : '+room.gen+' | children : '+room.children.length;
833
}
834
var pic=this.getPic(x,y);
835
str+='<div style="opacity:'+opacity+';width:'+size+'px;height:'+size+'px;position:absolute;left:'+(x*size)+'px;top:'+(y*size)+'px;display:block;padding:0px;margin:0px;background:#'+colors[this.data[x][y][0]]+' url(img/dungeonTiles.png) '+(-pic[0]*16)+'px '+(-pic[1]*16)+'px;color:#999;" title="'+title+'"></div>';
836
}
837
str+='<br>';
838
}
839
str='<div style="box-shadow:0px 0px 12px 6px #00061b;position:relative;width:'+(this.w*size)+'px;height:'+(this.h*size)+'px;background:#00061b;font-family:Courier;font-size:'+size+'px;float:left;margin:10px;">'+str+'</div>';
840
return str;
841
}
842
843
this.Map.prototype.getStr=function()
844
{
845
//return a string containing the map with tile graphics, ready to be pasted in a wrapper
846
var str='';
847
var size=16;
848
for (var y=0;y<this.h;y++){for (var x=0;x<this.w;x++){
849
var room=this.getRoom(x,y);
850
//var opacity=Math.max(0.1,room.tiles[this.getRoomTile(room,x,y)].score);
851
var opacity=1;
852
var title='void';
853
var pic=this.getPic(x,y);
854
if (room!=-1)
855
{
856
/*
857
opacity=Math.max(0.1,1-room.gen/5);
858
if (room.hidden) opacity=0;
859
if (this.data[x][y][0]==TILE_ENTRANCE || this.data[x][y][0]==TILE_EXIT) opacity=1;
860
*/
861
if (room.hidden) pic=[0,0];
862
title=(room.corridor?'corridor':'room')+' '+room.id+' | depth : '+room.gen+' | children : '+room.children.length;
863
}
864
str+='<div style="opacity:'+opacity+';width:'+size+'px;height:'+size+'px;position:absolute;left:'+(x*size)+'px;top:'+(y*size)+'px;display:block;padding:0px;margin:0px;background:#'+colors[this.data[x][y][0]]+' url(img/dungeonTiles.png) '+(-pic[0]*16)+'px '+(-pic[1]*16)+'px;color:#999;" title="'+title+'"></div>';
865
}
866
str+='<br>';
867
}
868
return str;
869
}
870
871
}
872