UNPKG

dungeon-generator

Version:

prodecural dungeon generator for games

275 lines (228 loc) 11.3 kB
'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); Object.defineProperty(exports, "__esModule", { value: true }); var _generator = require('./generator'); var _generator2 = _interopRequireDefault(_generator); var _corridor = require('../pieces/corridor'); var _corridor2 = _interopRequireDefault(_corridor); var _room = require('../pieces/room'); var _room2 = _interopRequireDefault(_room); var _const = require('../const'); var _utils = require('../utils'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var Dungeon = function (_Generator) { _inherits(Dungeon, _Generator); function Dungeon(options) { _classCallCheck(this, Dungeon); options = Object.assign({}, { size: [100, 100], rooms: { initial: { min_size: [3, 3], max_size: [3, 3], max_exits: 1 }, any: { min_size: [2, 2], max_size: [5, 5], max_exits: 4 } }, max_corridor_length: 6, min_corridor_length: 2, corridor_density: 0.5, //corridors per room symmetric_rooms: false, // exits must be in the middle of walls interconnects: 1, //extra corridors to connect rooms and make circular paths. not guaranteed max_interconnect_length: 10, room_count: 10 }, options); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Dungeon).call(this, options)); _this.room_tags = Object.keys(_this.rooms).filter(function (tag) { return tag !== 'any' && tag !== 'initial'; }); for (var i = _this.room_tags.length; i < _this.room_count; i++) { _this.room_tags.push('any'); } _this.rooms = []; _this.corridors = []; return _this; } _createClass(Dungeon, [{ key: 'add_room', value: function add_room(room, exit) { var add_to_room = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; var g_add_to_room = add_to_room; //add a new piece, exit is local perimeter pos for that exit; var choices = undefined, old_room = undefined, i = 0; while (true) { //pick a placed room to connect this piece to if (add_to_room) { old_room = add_to_room; add_to_room = null; } else { choices = this.get_open_pieces(this.children); if (choices && choices.length) { old_room = this.random.choose(choices); } else { console.log('ran out of choices connecting'); break; } } //if exit is specified, try joining to this specific exit if (exit) { //try joining the rooms if (this.join(old_room, exit, room)) { return true; } //else try all perims to see } else { var perim = room.perimeter.slice(); while (perim.length) { if (this.join(old_room, this.random.choose(perim, true), room)) { return true; } } } if (i++ === 100) { console.log('failed to connect 100 times :(', room, exit, g_add_to_room); return false; } } } }, { key: 'new_corridor', value: function new_corridor() { return new _corridor2.default({ length: this.random.int(this.min_corridor_length, this.max_corridor_length), facing: this.random.choose(_const.FACING) }); } }, { key: 'add_interconnect', value: function add_interconnect() { var perims = {}, hash = undefined, exit = undefined, p = undefined; //hash all possible exits this.children.forEach(function (child) { if (child.exits.length < child.max_exits) { child.perimeter.forEach(function (exit) { p = child.parent_pos(exit[0]); hash = p[0] + '_' + p[1]; perims[hash] = [exit, child]; }); } }); //search each room for a possible interconnect, backwards var room = undefined, mod = undefined, length = undefined, corridor = undefined, room2 = undefined; for (var i = this.children.length - 1; i--; i >= 0) { room = this.children[i]; //if room has exits available if (room.exits.length < room.max_exits) { //iterate exits for (var k = 0; k < room.perimeter.length; k++) { exit = room.perimeter[k]; p = room.parent_pos(exit[0]); length = -1; //try to dig a tunnel from this exit and see if it hits anything while (length <= this.max_interconnect_length) { //check if space is not occupied if (!this.walls.get(p) || !this.walls.get((0, _utils.shift_left)(p, exit[1])) || !this.walls.get((0, _utils.shift_right)(p, exit[1]))) { break; } hash = p[0] + '_' + p[1]; //is there a potential exit at these coordiantes (not of the same room) if (perims[hash] && perims[hash][1].id !== room.id) { room2 = perims[hash][1]; //rooms cant be joined directly, add a corridor if (length > -1) { corridor = new _corridor2.default({ length: length, facing: exit[1] }); if (this.join(room, corridor.perimeter[0], corridor, exit)) { this.join_exits(room2, perims[hash][0], corridor, corridor.perimeter[corridor.perimeter.length - 1]); return true; } else { return false; } //rooms can be joined directly } else { this.join_exits(room2, perims[hash][0], room, exit); return true; } } //exit not found, try to make the interconnect longer p = (0, _utils.shift)(p, exit[1]); length++; } } } } } }, { key: 'new_room', value: function new_room(key) { //spawn next room key = key || this.random.choose(this.room_tags, false); var opts = this.options.rooms[key]; var room = new _room2.default({ size: this.random.vec(opts.min_size, opts.max_size), max_exits: opts.max_exits, symmetric: this.symmetric_rooms, tag: key }); this.room_tags.splice(this.room_tags.indexOf(key), 1); if (key === 'initial') { this.initial_room = room; } return room; } }, { key: 'generate', value: function generate() { var no_rooms = this.options.room_count - 1, room = this.new_room(this.options.rooms.initial ? 'initial' : undefined), no_corridors = Math.round(this.corridor_density * no_rooms); this.add_piece(room, this.options.rooms.initial && this.options.rooms.initial.position ? this.options.rooms.initial.position : this.get_center_pos()); var k = undefined; while (no_corridors || no_rooms) { k = this.random.int(1, no_rooms + no_corridors); if (k <= no_corridors) { var corridor = this.new_corridor(); var added = this.add_room(corridor, corridor.perimeter[0]); no_corridors--; //try to connect to this corridor next if (no_rooms > 0 && added) { this.add_room(this.new_room(), null, corridor); no_rooms--; } } else { this.add_room(this.new_room()); no_rooms--; } } for (k = 0; k < this.interconnects; k++) { this.add_interconnect(); } this.trim(); if (this.initial_room) { this.start_pos = this.initial_room.global_pos(this.initial_room.get_center_pos()); } } }]); return Dungeon; }(_generator2.default); exports.default = Dungeon;