diff --git a/src/client/.eslintrc b/src/client/.eslintrc index 343630c8..37b851aa 100644 --- a/src/client/.eslintrc +++ b/src/client/.eslintrc @@ -53,7 +53,9 @@ "$": false, "define": false, "_": false, - "PIXI": false + "PIXI": false, + "scale": false, + "scaleMult": false }, "rules": { diff --git a/src/client/js/components/attackAnimation.js b/src/client/js/components/attackAnimation.js index 16a2ee67..9092bbc7 100644 --- a/src/client/js/components/attackAnimation.js +++ b/src/client/js/components/attackAnimation.js @@ -5,8 +5,6 @@ define([ effects, renderer ) { - let scale = 40; - return { type: 'attackAnimation', diff --git a/src/client/js/components/chatter.js b/src/client/js/components/chatter.js index 72672200..fc407426 100644 --- a/src/client/js/components/chatter.js +++ b/src/client/js/components/chatter.js @@ -3,8 +3,6 @@ define([ ], function ( renderer ) { - let scale = 40; - return { type: 'chatter', diff --git a/src/client/js/components/effects.js b/src/client/js/components/effects.js index 2dd4825b..c362351b 100644 --- a/src/client/js/components/effects.js +++ b/src/client/js/components/effects.js @@ -3,8 +3,6 @@ define([ ], function ( renderer ) { - let scale = 40; - let auras = { reflectDamage: 0, stealth: 1, diff --git a/src/client/js/components/light.js b/src/client/js/components/light.js index 35901c3c..f6f9ad54 100644 --- a/src/client/js/components/light.js +++ b/src/client/js/components/light.js @@ -5,8 +5,6 @@ define([ effects, renderer ) { - let scale = 40; - return { type: 'light', diff --git a/src/client/js/components/lightPatch.js b/src/client/js/components/lightPatch.js index ab7cc736..68bc7492 100644 --- a/src/client/js/components/lightPatch.js +++ b/src/client/js/components/lightPatch.js @@ -5,9 +5,6 @@ define([ renderer, picture ) { - let scale = 40; - let scaleMult = 5; - return { type: 'lightPatch', diff --git a/src/client/js/components/mouseMover.js b/src/client/js/components/mouseMover.js index be38d094..5d1058a7 100644 --- a/src/client/js/components/mouseMover.js +++ b/src/client/js/components/mouseMover.js @@ -11,8 +11,6 @@ define([ input, objects ) { - let scale = 40; - return { type: 'mouseMover', diff --git a/src/client/js/components/particles.js b/src/client/js/components/particles.js index 590a8d60..988198cf 100644 --- a/src/client/js/components/particles.js +++ b/src/client/js/components/particles.js @@ -3,8 +3,6 @@ define([ ], function ( renderer ) { - let scale = 40; - return { type: 'particles', emitter: null, diff --git a/src/client/js/components/pather.js b/src/client/js/components/pather.js index ed4ed51d..8d9c98e0 100644 --- a/src/client/js/components/pather.js +++ b/src/client/js/components/pather.js @@ -5,8 +5,6 @@ define([ renderer, events ) { - let scale = 40; - let scaleMult = 5; let round = Math.round.bind(Math); let maxPathLength = 50; diff --git a/src/client/js/components/player.js b/src/client/js/components/player.js index f641a87e..acd9314d 100644 --- a/src/client/js/components/player.js +++ b/src/client/js/components/player.js @@ -9,8 +9,6 @@ define([ physics, sound ) { - let scale = 40; - return { type: 'player', diff --git a/src/client/js/components/projectile.js b/src/client/js/components/projectile.js index 586d3776..e88b3c92 100644 --- a/src/client/js/components/projectile.js +++ b/src/client/js/components/projectile.js @@ -3,8 +3,6 @@ define([ ], function ( effects ) { - let scale = 40; - return { type: 'projectile', diff --git a/src/client/js/components/spellbook.js b/src/client/js/components/spellbook.js index 462440a7..485dd7d4 100644 --- a/src/client/js/components/spellbook.js +++ b/src/client/js/components/spellbook.js @@ -7,8 +7,6 @@ define([ renderer, events ) { - let scale = 40; - let objects = null; require(['js/objects/objects'], function (o) { objects = o; diff --git a/src/client/js/components/stats.js b/src/client/js/components/stats.js index 390af92f..89d63e73 100644 --- a/src/client/js/components/stats.js +++ b/src/client/js/components/stats.js @@ -5,8 +5,6 @@ define([ events, renderer ) { - let scale = 40; - return { type: 'stats', diff --git a/src/client/js/misc/helpers.js b/src/client/js/misc/helpers.js index e79d02e4..79ef2831 100644 --- a/src/client/js/misc/helpers.js +++ b/src/client/js/misc/helpers.js @@ -1,4 +1,7 @@ -/* global _ */ +/* global _, scale, scaleMult */ + +window.scale = 40; +window.scaleMult = 5; //eslint-disable-next-line no-extend-native Array.prototype.firstIndex = function (callback, thisArg) { diff --git a/src/client/js/objects/objBase.js b/src/client/js/objects/objBase.js index e2d4f88b..71b70305 100644 --- a/src/client/js/objects/objBase.js +++ b/src/client/js/objects/objBase.js @@ -7,9 +7,6 @@ define([ renderer, events ) { - let scale = 40; - let scaleMult = 5; - return { components: [], offsetX: 0, diff --git a/src/client/js/objects/objects.js b/src/client/js/objects/objects.js index 8c2b99cb..9e6aadb3 100644 --- a/src/client/js/objects/objects.js +++ b/src/client/js/objects/objects.js @@ -9,8 +9,6 @@ define([ renderer, sound ) { - let scale = 40; - return { showNames: false, diff --git a/src/client/js/rendering/lightningBuilder.js b/src/client/js/rendering/lightningBuilder.js index 791ccaaa..ecf4b9d5 100644 --- a/src/client/js/rendering/lightningBuilder.js +++ b/src/client/js/rendering/lightningBuilder.js @@ -5,9 +5,6 @@ define([ renderer, picture ) { - let scale = 40; - let scaleMult = 5; - return { build: function (config) { let obj = { diff --git a/src/client/js/rendering/numbers.js b/src/client/js/rendering/numbers.js index 7e188e7a..2b221a5a 100644 --- a/src/client/js/rendering/numbers.js +++ b/src/client/js/rendering/numbers.js @@ -7,9 +7,6 @@ define([ objects, renderer ) { - let scale = 40; - let scaleMult = 5; - return { list: [], diff --git a/src/client/js/rendering/renderer.js b/src/client/js/rendering/renderer.js index b570cd9a..08647f03 100644 --- a/src/client/js/rendering/renderer.js +++ b/src/client/js/rendering/renderer.js @@ -19,8 +19,6 @@ define([ spritePool, picture ) { - let scale = 40; - let scaleMult = 5; let pixi = PIXI; return { diff --git a/src/server/misc/helpers.js b/src/server/misc/helpers.js index efc780a4..a149bb74 100644 --- a/src/server/misc/helpers.js +++ b/src/server/misc/helpers.js @@ -125,6 +125,13 @@ module.exports = { return o; }, + getGuid: function () { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + }, + //Only use this method for official logging. Temporary logs should use console.log // so those instances can be reported by eslint log: function (msg) { diff --git a/src/server/world/instancer.js b/src/server/world/instancer.js index ec0e22e9..e50f5906 100644 --- a/src/server/world/instancer.js +++ b/src/server/world/instancer.js @@ -28,514 +28,172 @@ module.exports = { herbs.init(); map.init(args); - if (!map.instanced) { - let fakeInstance = { - objects: objects, - syncer: syncer, - physics: physics, - zoneId: this.zoneId, - spawners: spawners, - questBuilder: questBuilder, - events: events, - zone: map.zone, - mail: mail, + let fakeInstance = { + objects: objects, + syncer: syncer, + physics: physics, + zoneId: this.zoneId, + spawners: spawners, + questBuilder: questBuilder, + events: events, + zone: map.zone, + mail: mail, + map: map, + scheduler: scheduler, + eventEmitter: eventEmitter + }; + + this.instances.push(fakeInstance); + + spawners.init(fakeInstance); + scheduler.init(); + + map.create(); + if (map.mapFile.properties.isRandom) { + if (!map.oldCollisionMap) + map.oldCollisionMap = map.collisionMap; + + randomMap.generate({ map: map, - scheduler: scheduler, - eventEmitter: eventEmitter - }; - - this.instances.push(fakeInstance); - - spawners.init(fakeInstance); - scheduler.init(); - - map.create(); - map.clientMap.zoneId = this.zoneId; - - [resourceSpawner, syncer, objects, questBuilder, events, mail].forEach(i => i.init(fakeInstance)); - - this.addObject = this.nonInstanced.addObject.bind(this); - this.onAddObject = this.nonInstanced.onAddObject.bind(this); - this.updateObject = this.nonInstanced.updateObject.bind(this); - this.queueAction = this.nonInstanced.queueAction.bind(this); - this.performAction = this.nonInstanced.performAction.bind(this); - this.removeObject = this.nonInstanced.removeObject.bind(this); - - this.tick = this.nonInstanced.tick.bind(this); - this.tick(); - } else { - spawners.init({ - zone: map.zone + physics: physics, + spawners: spawners }); - map.create(); - map.clientMap.zoneId = this.zoneId; - - this.addObject = this.instanced.addObject.bind(this); - this.onAddObject = this.instanced.onAddObject.bind(this); - this.updateObject = this.instanced.updateObject.bind(this); - this.queueAction = this.instanced.queueAction.bind(this); - this.performAction = this.instanced.performAction.bind(this); - this.removeObject = this.instanced.removeObject.bind(this); - - if (map.mapFile.properties.isRandom) - this.ttlGen = 0; - - this.tick = this.instanced.tick.bind(this); - this.tick(); + map.seed = _.getGuid(); } - }, - - nonInstanced: { - tick: function () { - events.update(); - objects.update(); - resourceSpawner.update(); - spawners.update(); - syncer.update(); - scheduler.update(); - - setTimeout(this.tick.bind(this), this.speed); - }, - - addObject: function (msg) { - let obj = msg.obj; - obj.serverId = obj.id; - delete obj.id; - - if ((msg.keepPos) && (!physics.isValid(obj.x, obj.y))) - msg.keepPos = false; - - let spawnPos = map.getSpawnPos(obj); - - if (!msg.keepPos || !obj.has('x')) { - obj.x = spawnPos.x; - obj.y = spawnPos.y; - } - - obj.spawn = map.spawn; - syncer.queue('onGetMap', map.clientMap, [obj.serverId]); + map.clientMap.zoneId = this.zoneId; - if (!msg.transfer) - objects.addObject(obj, this.onAddObject.bind(this)); - else { - let o = objects.transferObject(obj); - questBuilder.obtain(o); - } - }, - onAddObject: function (obj) { - if (obj.player) - obj.stats.onLogin(); - - questBuilder.obtain(obj); - obj.fireEvent('afterMove'); - - if (obj.dead) { - obj.instance.syncer.queue('onDeath', { - x: obj.x, - y: obj.y - }, [obj.serverId]); - } - }, - updateObject: function (msg) { - let obj = objects.find(o => o.serverId === msg.id); - if (!obj) - return; + [resourceSpawner, syncer, objects, questBuilder, events, mail].forEach(i => i.init(fakeInstance)); - let msgObj = msg.obj; - - let components = msgObj.components || []; - delete msgObj.components; + this.tick(); + }, - for (let p in msgObj) - obj[p] = msgObj[p]; + tick: function () { + events.update(); + objects.update(); + resourceSpawner.update(); + spawners.update(); + syncer.update(); + scheduler.update(); - let cLen = components.length; - for (let i = 0; i < cLen; i++) { - let c = components[i]; - let component = obj[c.type]; - for (let p in c) - component[p] = c[p]; - } - }, + setTimeout(this.tick.bind(this), this.speed); + }, - queueAction: function (msg) { - let obj = objects.find(o => o.serverId === msg.id); - if (!obj) - return; - else if (msg.action.action === 'move') { - let moveEntries = obj.actionQueue.filter(q => (q.action === 'move')).length; - if (moveEntries >= 50) - return; - } + addObject: function (msg) { + let obj = msg.obj; + obj.serverId = obj.id; + delete obj.id; - obj.queue(msg.action); - }, - - performAction: function (msg) { - let obj = null; - let targetId = msg.action.targetId; - if (!targetId) - obj = objects.find(o => o.serverId === msg.id); - else { - obj = objects.find(o => o.id === targetId); - if (obj) { - let action = msg.action; - if (!action.data) - action.data = {}; - action.data.sourceId = msg.id; - } - } + if ((msg.keepPos) && (!physics.isValid(obj.x, obj.y))) + msg.keepPos = false; - if (!obj) - return; + let spawnPos = map.getSpawnPos(obj); - obj.performAction(msg.action); - }, + if (!msg.keepPos || !obj.has('x') || (map.mapFile.properties.isRandom && obj.instanceId !== map.seed)) { + obj.x = spawnPos.x; + obj.y = spawnPos.y; + } - removeObject: function (msg) { - let obj = msg.obj; - obj = objects.find(o => o.serverId === obj.id); - if (!obj) { - //We should probably never reach this - return; - } + obj.instanceId = map.seed; - if (obj.auth) - obj.auth.doSave(); + obj.spawn = map.spawn; - if (obj.player) - obj.fireEvent('beforeRezone'); + syncer.queue('onGetMap', map.clientMap, [obj.serverId]); - obj.destroyed = true; + if (!msg.transfer) + objects.addObject(obj, this.onAddObject.bind(this)); + else { + let o = objects.transferObject(obj); + questBuilder.obtain(o); } }, - instanced: { - tick: function () { - if (map.mapFile.properties.isRandom) { - if (this.ttlGen <= 0) { - if (!map.oldMap) - map.oldMap = map.clientMap.map; - if (!map.oldCollisionMap) - map.oldCollisionMap = map.collisionMap; - - spawners.reset(); - - randomMap.generate({ - map: map, - physics: physics, - spawners: spawners - }); - - this.ttlGen = 2000; - } else - this.ttlGen--; - } - let instances = this.instances; - let iLen = instances.length; - for (let i = 0; i < iLen; i++) { - let instance = instances[i]; - - instance.objects.update(); - instance.spawners.update(); - instance.resourceSpawner.update(); - instance.scheduler.update(); - - instance.syncer.update(); - - if (instance.closeTtl) { - let hasPlayers = instance.objects.objects.some(o => o.player); - if (hasPlayers) { - delete instance.closeTtl; - continue; - } - - instance.closeTtl--; - if (instance.closeTtl <= 0) { - instances.splice(i, 1); - i--; - iLen--; - } - } else { - let isEmpty = !instance.objects.objects.some(o => o.player); - if (isEmpty) { - //Zones reset after being empty for 2 minutes (about 342 ticks) - instance.closeTtl = 342; - } - } - } - - setTimeout(this.tick.bind(this), this.speed); - }, - - addObject: function (msg) { - let obj = msg.obj; - let instanceId = msg.instanceId; - - //Maybe a party member is in here already? - let social = obj.components.find(c => c.type === 'social'); - if ((social) && (social.party)) { - let party = social.party; - let instances = this.instances; - let iLen = instances.length; - for (let i = 0; i < iLen; i++) { - let instance = instances[i]; - - let partyInside = instance.objects.objects.some(o => party.indexOf(o.serverId) > -1); - if (partyInside) { - if (instance.id !== obj.instanceId) - msg.keepPos = false; - obj.instanceId = instance.id; - obj.instance = instance; - instanceId = instance.id; - break; - } - } - } + onAddObject: function (obj) { + if (obj.player) + obj.stats.onLogin(); - if (msg.transfer) - msg.keepPos = false; + questBuilder.obtain(obj); + obj.fireEvent('afterMove'); - let exists = this.instances.find(i => i.id === instanceId); - - if (exists) { - if ((msg.keepPos) && (!exists.physics.isValid(obj.x, obj.y))) - msg.keepPos = false; - } - - let spawnPos = map.getSpawnPos(obj); - - if (exists) - spawnPos = exists.map.getSpawnPos(obj); - - if (!msg.keepPos || !obj.has('x')) { - obj.x = spawnPos.x; - obj.y = spawnPos.y; - } - - obj.spawn = map.spawn; - - if (exists) { - //Keep track of what the connection id is (sent from the server) - obj.serverId = obj.id; - delete obj.id; - - obj.spawn = exists.map.spawn; - - exists.syncer.queue('onGetMap', exists.map.clientMap, [obj.serverId]); - - if (!msg.transfer) - exists.objects.addObject(obj, this.onAddObject.bind(this, msg.keepPos)); - else { - let newObj = exists.objects.transferObject(obj); - this.onAddObject(false, newObj); - } - - process.send({ - method: 'object', - serverId: obj.serverId, - obj: { - instanceId: exists.id - } - }); - } else - obj = this.instanced.createInstance.call(this, obj, msg.transfer); - }, - onAddObject: function (keepPos, obj) { - if (!keepPos) { - let spawnPos = obj.instance.map.getSpawnPos(obj); - - obj.x = spawnPos.x; - obj.y = spawnPos.y; - } - - obj.instance.questBuilder.obtain(obj); - - if (obj.player) - obj.stats.onLogin(); - - obj.fireEvent('afterMove'); - - if (obj.dead) { - obj.instance.syncer.queue('onDeath', { - x: obj.x, - y: obj.y - }, [obj.serverId]); - } - }, - updateObject: function (msg) { - let id = msg.id; - let instanceId = msg.instanceId; - - let exists = this.instances.find(i => i.id === instanceId); - if (!exists) - return; - - let obj = exists.objects.find(o => o.serverId === id); - if (!obj) - return; + if (obj.dead) { + obj.instance.syncer.queue('onDeath', { + x: obj.x, + y: obj.y + }, [obj.serverId]); + } + }, - let msgObj = msg.obj; + updateObject: function (msg) { + let obj = objects.find(o => o.serverId === msg.id); + if (!obj) + return; - let components = msgObj.components || []; - delete msgObj.components; + let msgObj = msg.obj; - for (let p in msgObj) - obj[p] = msgObj[p]; + let components = msgObj.components || []; + delete msgObj.components; - let cLen = components.length; - for (let i = 0; i < cLen; i++) { - let c = components[i]; - let component = obj[c.type]; - for (let p in c) - component[p] = c[p]; - } - }, + for (let p in msgObj) + obj[p] = msgObj[p]; - performAction: function (msg) { - let id = msg.id; - let instanceId = msg.instanceId; - - let exists = this.instances.find(i => i.id === instanceId); - if (!exists) - return; + let cLen = components.length; + for (let i = 0; i < cLen; i++) { + let c = components[i]; + let component = obj[c.type]; + for (let p in c) + component[p] = c[p]; + } + }, - let obj = exists.objects.find(o => o.serverId === id); - if (!obj) + queueAction: function (msg) { + let obj = objects.find(o => o.serverId === msg.id); + if (!obj) + return; + else if (msg.action.action === 'move') { + let moveEntries = obj.actionQueue.filter(q => (q.action === 'move')).length; + if (moveEntries >= 50) return; + } - obj.performAction(msg.action); - }, - - queueAction: function (msg) { - let id = msg.id; - let instanceId = msg.instanceId; - - let exists = this.instances.find(i => i.id === instanceId); - if (!exists) - return; + obj.queue(msg.action); + }, - let obj = exists.objects.find(o => o.serverId === id); + performAction: function (msg) { + let obj = null; + let targetId = msg.action.targetId; + if (!targetId) + obj = objects.find(o => o.serverId === msg.id); + else { + obj = objects.find(o => o.id === targetId); if (obj) { - if (msg.action.action === 'move') { - let moveEntries = obj.actionQueue.filter(q => (q.action === 'move')).length; - if (moveEntries >= 50) - return; - } - - obj.queue(msg.action); + let action = msg.action; + if (!action.data) + action.data = {}; + action.data.sourceId = msg.id; } - }, - - removeObject: function (msg) { - let obj = msg.obj; - let instanceId = msg.instanceId; - - let exists = this.instances.find(i => i.id === instanceId); - if (!exists) - return; + } - obj = exists.objects.find(o => o.serverId === obj.id); + if (!obj) + return; - if (!obj) - return; + obj.performAction(msg.action); + }, - if (obj.auth) - obj.auth.doSave(); - - obj.destroyed = true; - }, - - createInstance: function (objToAdd, transfer) { - let newMap = { - name: map.name, - spawn: extend([], map.spawn), - clientMap: extend({}, map.clientMap) - }; - newMap.getSpawnPos = map.getSpawnPos.bind(newMap); - - //Hack: We need to actually just always use the instanced eventEmitter - let eventQueue = eventEmitter.queue; - delete eventEmitter.queue; - let newEventEmitter = extend({ - queue: [] - }, eventEmitter); - eventEmitter.queue = eventQueue; - - let instance = { - id: objToAdd.name + '_' + (+new Date()), - objects: extend({}, objects), - spawners: extend({}, spawners), - syncer: extend({}, syncer), - physics: extend({}, physics), - resourceSpawner: extend({}, resourceSpawner), - zoneId: this.zoneId, - zone: map.zone, - closeTtl: null, - questBuilder: extend({}, questBuilder), - events: extend({}, events), - scheduler: extend({}, scheduler), - mail: extend({}, mail), - map: newMap, - eventEmitter: newEventEmitter, - instanced: true - }; - - ['objects', 'spawners', 'syncer', 'resourceSpawner', 'questBuilder', 'events', 'scheduler', 'mail'].forEach(i => instance[i].init(instance)); - - this.instances.push(instance); - - let onDone = this.instanced.onCreateInstance.bind(this, instance, objToAdd, transfer); - - if (map.custom) { - instance.customMap = extend({}, customMap); - instance.customMap.load(instance, objToAdd, onDone); - } else - onDone(); - }, - onCreateInstance: function (instance, objToAdd, transfer) { - objToAdd.instance = instance; - objToAdd.instanceId = instance.id; - - //Keep track of what the connection id is (sent from the server) - objToAdd.serverId = objToAdd.id; - delete objToAdd.id; - - let obj = null; - - instance.syncer.queue('onGetMap', instance.map.clientMap, [objToAdd.serverId]); - - if (!transfer) - obj = instance.objects.addObject(objToAdd, this.onAddObject.bind(this, false)); - else { - obj = instance.objects.transferObject(objToAdd); - - let spawnPos = instance.map.getSpawnPos(obj); - - obj.x = spawnPos.x; - obj.y = spawnPos.y; - - instance.questBuilder.obtain(obj); - } + removeObject: function (msg) { + let obj = msg.obj; + obj = objects.find(o => o.serverId === obj.id); + if (!obj) { + //We should probably never reach this + return; + } - process.send({ - method: 'object', - serverId: obj.serverId, - obj: { - instanceId: instance.id - } - }); + if (obj.auth) + obj.auth.doSave(); - if (obj.dead) { - obj.instance.syncer.queue('onDeath', { - x: obj.x, - y: obj.y - }, [obj.serverId]); - } + if (obj.player) + obj.fireEvent('beforeRezone'); - return obj; - } + obj.destroyed = true; } }; diff --git a/src/server/world/map.js b/src/server/world/map.js index 5d95a666..ae8a075e 100644 --- a/src/server/world/map.js +++ b/src/server/world/map.js @@ -86,10 +86,7 @@ module.exports = { this.mapFile.properties = this.mapFile.properties || {}; mapScale = mapFile.tilesets[0].tileheight; - this.instanced = mapFile.properties.instanced; this.custom = mapFile.properties.custom; - if (this.instanced) - this.instanced = (this.instanced === '1'); if (mapFile.properties.spawn) { this.spawn = JSON.parse(mapFile.properties.spawn); diff --git a/src/server/world/physics.js b/src/server/world/physics.js index 0a8d7f5c..9d9b40bd 100644 --- a/src/server/world/physics.js +++ b/src/server/world/physics.js @@ -315,7 +315,7 @@ module.exports = { let node = this.graph.grid[x][y]; if (node) - return node.isWall(); + return (node.weight === 0); return true; }, isCellOpen: function (x, y) { diff --git a/src/server/world/randomMap.js b/src/server/world/randomMap.js index fffa1860..3e022beb 100644 --- a/src/server/world/randomMap.js +++ b/src/server/world/randomMap.js @@ -5,7 +5,7 @@ module.exports = { rooms: [], exitAreas: [], - maxDistance: 12, + maxDistance: 14, minDistance: 0, bounds: [0, 0, 0, 0], @@ -374,7 +374,8 @@ module.exports = { this.updateBounds(room); if (room.distance < this.maxDistance) { - let count = this.randInt(1, room.template.exits.length); + const maxExits = room.template.exits.length; + let count = this.randInt(Math.min(maxExits, 2), maxExits); for (let i = 0; i < count; i++) this.setupConnection(room, !isHallway); }