HollowBond/Public/ecma/Builders/RoomsBuilder.ecma.js

745 lines
32 KiB
JavaScript

"use strict";
const RoomsBuilder = (function(){
const RoomsBuilder = function(hollow_bond){
const self = this;
const get_hole_reserved_bounds_by_side = (hole, elevators, py) => {
const maximum_margin_x = Math.max(hollow_bond.room_minimum_distance_hole, (hollow_bond.room_maximum_size - Math.abs(hole.xt - hole.xf)) * 0.5),
maximum_margin_z = Math.max(hollow_bond.room_minimum_distance_hole, (hollow_bond.room_maximum_size - Math.abs(hole.zt - hole.zf)) * 0.5),
bounds = {
xf : hole.xf - Common.random_decay(hollow_bond.hash.rooms(hole.xf, py, hole.zf, 1), hollow_bond.room_minimum_distance_hole, maximum_margin_x, hollow_bond.room_size_decay) | 0,
xt : hole.xt + Common.random_decay(hollow_bond.hash.rooms(hole.xt, py, hole.zt, 2), hollow_bond.room_minimum_distance_hole, maximum_margin_x, hollow_bond.room_size_decay) | 0,
zf : hole.zf - Common.random_decay(hollow_bond.hash.rooms(hole.xf, py, hole.zf, 3), hollow_bond.room_minimum_distance_hole, maximum_margin_z, hollow_bond.room_size_decay) | 0,
zt : hole.zt + Common.random_decay(hollow_bond.hash.rooms(hole.xt, py, hole.zt, 4), hollow_bond.room_minimum_distance_hole, maximum_margin_z, hollow_bond.room_size_decay) | 0
};
for(const elevator of elevators)
if(
bounds.xt > elevator.xf && bounds.xf < elevator.xt &&
bounds.zt > elevator.zf && bounds.zf < elevator.zt
){
const distance = {
xf : elevator.xt - bounds.xf,
xt : bounds.xt - elevator.xf,
zf : elevator.zt - bounds.zf,
zt : bounds.zt - elevator.zf
};
switch(Math.min(distance.xf, distance.xt, distance.zf, distance.zt)){
case distance.xf:
bounds.xf = elevator.xt;
break;
case distance.xt:
bounds.xt = elevator.xf;
break;
case distance.zf:
bounds.zf = elevator.zt;
break;
case distance.zt:
bounds.zt = elevator.zf;
break;
};
};
return bounds;
};
const cut_z = (cx, rectangle) => [
{xf : rectangle.xf, xt : cx, zf : rectangle.zf, zt : rectangle.zt, done : false},
{xf : cx, xt : rectangle.xt, zf : rectangle.zf, zt : rectangle.zt, done : false}
];
const cut_x = (cz, rectangle) => [
{xf : rectangle.xf, xt : rectangle.xt, zf : rectangle.zf, zt : cz, done : false},
{xf : rectangle.xf, xt : rectangle.xt, zf : cz, zt : rectangle.zt, done : false}
];
const obstacules_cut_z = (cx, obstacules, rectangle) => obstacules.some(obstacule => (
cx > obstacule.xf && cx < obstacule.xt &&
Math.max(rectangle.zf, obstacule.zf) < Math.min(rectangle.zt, obstacule.zt)
)) ? [false, null] : [true, cut_z(cx, rectangle)];
const obstacules_cut_x = (cz, obstacules, rectangle) => obstacules.some(obstacule => (
cz > obstacule.zf && cz < obstacule.zt &&
Math.max(rectangle.xf, obstacule.xf) < Math.min(rectangle.xt, obstacule.xt)
)) ? [false, null] : [true, cut_x(cz, rectangle)];
const subdivide_rooms = (rectangles, py) => {
let has_changes = false;
for(const rectangle of rectangles){
if(!rectangle.free)
rectangle.done = true;
else if(rectangle.done === undefined)
rectangle.done = !rectangle.free;
};
do{
const new_rooms = [];
has_changes = false;
for(const room of rectangles){
if(room.done || !room.free)
continue;
const width = Math.abs(room.xt - room.xf),
depth = Math.abs(room.zt - room.zf);
const can_split_x = width >= hollow_bond.room_minimum_size * 2,
can_split_z = depth >= hollow_bond.room_minimum_size * 2;
if(!can_split_x && !can_split_z){
room.done = true;
continue;
};
const x_oversized = width > hollow_bond.room_maximum_size,
z_oversized = depth > hollow_bond.room_maximum_size,
is_oversized = x_oversized || z_oversized,
// 1. Hash con firma 2D completa (evita cuadrículas alineadas entre vecinos)
seed_x = (room.xf * 73856093) ^ (room.xt * 19349663),
seed_z = (room.zf * 83492791) ^ (room.zt * 50624027);
let should_split = is_oversized;
if(!should_split)
should_split = hollow_bond.hash.rooms(seed_x, py, seed_z, 10) < (Math.max(width, depth) - hollow_bond.room_minimum_size * 2) / Math.max(1, hollow_bond.room_maximum_size - hollow_bond.room_minimum_size * 2);
if (!should_split) {
room.done = true;
continue;
};
// 2. Selección de eje: romper la relación de aspecto para forzar habitaciones cuadradas
let split_axis = null;
if(can_split_x && can_split_z){
// Si uno supera el máximo y el otro no, cortar obligatoriamente el sobredimensionado
if(x_oversized && !z_oversized)
split_axis = 'x';
else if(z_oversized && !x_oversized)
split_axis = 'z';
else
// Ponderación cuadrática por aspecto para evitar tiras/pasillos
split_axis = hollow_bond.hash.rooms(seed_x, py, seed_z, 11) < width * width / (width * width + depth * depth) ? 'x' : 'z';
}else
split_axis = can_split_x ? 'x' : 'z';
// 3. Punto de corte: oscilar cerca del centro en lugar de pelar los bordes
if(split_axis === 'x'){
const split_x = (room.xf + Math.max(hollow_bond.room_minimum_size, Math.min(width - hollow_bond.room_minimum_size, width * .5 + Common.random_decay(hollow_bond.hash.rooms(seed_x, py, seed_z, 12), 0, (width - hollow_bond.room_minimum_size * 2) * .5, hollow_bond.room_size_decay) * (hollow_bond.hash.rooms(seed_x, py, seed_z, 14) < .5 ? -1 : 1)))) | 0,
old_xt = room.xt;
room.xt = split_x;
room.done = false;
new_rooms.push({
xf : split_x,
xt : old_xt,
zf : room.zf,
zt : room.zt,
free : true,
done : false
});
}else{
const split_z = (room.zf + Math.max(hollow_bond.room_minimum_size, Math.min(depth - hollow_bond.room_minimum_size, depth * .5 + Common.random_decay(hollow_bond.hash.rooms(seed_z, py, seed_x, 13), 0, depth - hollow_bond.room_minimum_size * 2 * .5, hollow_bond.room_size_decay) * (hollow_bond.hash.rooms(seed_z, py, seed_x, 15) < .5 ? -1 : 1)))) | 0,
old_zt = room.zt;
room.zt = split_z;
room.done = false;
new_rooms.push({
xf : room.xf,
xt : room.xt,
zf : split_z,
zt : old_zt,
free : true,
done : false
});
};
has_changes = true;
};
new_rooms.length > 0 && rectangles.push(...new_rooms);
}while(has_changes);
return rectangles;
};
const divide_plane = (chunk, py) => {
const elevators = Object.values(chunk.elevators).filter(elevator => elevator.y == py).map(elevator => ({
xf : elevator.x,
xt : elevator.x + elevator.width,
zf : elevator.z,
zt : elevator.z + elevator.depth
})),
holes_y = Object.values(chunk.holes).filter(hole => py >= hole.yf && py < hole.yt),
holes = holes_y.map(hole => get_hole_reserved_bounds_by_side(hole, elevators, py)),
obstacules = elevators.concat(holes),
queue = [{ xf : chunk.xf, xt : chunk.xt, zf : chunk.zf, zt : chunk.zt }],
obstacules_cut = { x : obstacules_cut_x, z : obstacules_cut_z },
// 3. Ajuste de habitaciones sobredimensionadas que contienen huecos
new_rectangles = [];
let rectangles = [];
// 1. Partición de espacios con cola estricta
while(queue.length > 0){
const rectangle = queue.pop(),
local_obstacules = obstacules.filter(obstacule =>
obstacule.xt > rectangle.xf && obstacule.xf < rectangle.xt &&
obstacule.zt > rectangle.zf && obstacule.zf < rectangle.zt
);
if(local_obstacules.length === 0){
rectangles.push({...rectangle, free : true});
continue;
};
if(
local_obstacules.length === 1 &&
rectangle.xf === local_obstacules[0].xf &&
rectangle.xt === local_obstacules[0].xt &&
rectangle.zf === local_obstacules[0].zf &&
rectangle.zt === local_obstacules[0].zt
){
rectangles.push({...rectangle, free : false});
continue;
};
let split_found = false;
for(const obstacule of local_obstacules){
for (const [axis, value] of [].concat(
[["z", obstacule.xf, 0], ["z", obstacule.xt, 1]].filter(([_, x]) => x > rectangle.xf && x < rectangle.xt),
[["x", obstacule.zf, 2], ["x", obstacule.zt, 3]].filter(([_, z]) => z > rectangle.zf && z < rectangle.zt)
).sort((a, b) => hollow_bond.hash.room_cut_axis(py, a, b))){
const [ok, subrectangles] = obstacules_cut[axis](value, local_obstacules, rectangle);
if(ok){
queue.push(...subrectangles);
split_found = true;
break;
};
};
if(split_found)
break;
};
split_found || rectangles.push({...rectangle, free : !obstacules.some(obstacule =>
rectangle.xf >= obstacule.xf && rectangle.xt <= obstacule.xt &&
rectangle.zf >= obstacule.zf && rectangle.zt <= obstacule.zt
)});
};
// 2. Descartar los rectángulos que corresponden exactamente a los ascensores
rectangles = rectangles.filter(rectangle => !(!rectangle.free && elevators.some(elevator => (
rectangle.xf === elevator.xf && rectangle.xt === elevator.xt &&
rectangle.zf === elevator.zf && rectangle.zt === elevator.zt
))));
for(let i = 0; i < rectangles.length; i ++){
// Obtener los agujeros reales dentro de este rectángulo
const contained_holes = holes_y.filter(hole => (
rectangles[i].xf <= hole.xf && rectangles[i].xt >= hole.xt &&
rectangles[i].zf <= hole.zf && rectangles[i].zt >= hole.zt
));
if(contained_holes.length === 0)
continue;
let width = Math.abs(rectangles[i].xt - rectangles[i].xf),
depth = Math.abs(rectangles[i].zt - rectangles[i].zf);
if(width > hollow_bond.room_maximum_size || depth > hollow_bond.room_maximum_size){
// Bounding box protector envolvente de todos los agujeros contenidos
const minimum = {
xf : Math.min(...contained_holes.map(hole => hole.xf)) - hollow_bond.room_minimum_distance_hole,
xt : Math.max(...contained_holes.map(hole => hole.xt)) + hollow_bond.room_minimum_distance_hole,
zf : Math.min(...contained_holes.map(hole => hole.zf)) - hollow_bond.room_minimum_distance_hole,
zt : Math.max(...contained_holes.map(hole => hole.zt)) + hollow_bond.room_minimum_distance_hole
};
// Recortes en X
if(width > hollow_bond.room_maximum_size){
if(rectangles[i].xf < minimum.xf){
const subrectangles = cut_z(minimum.xf, rectangles[i]);
width -= Math.abs(minimum.xf - rectangles[i].xf);
rectangles[i] = subrectangles[1];
new_rectangles.push(subrectangles[0]);
};
if(width > hollow_bond.room_maximum_size && rectangles[i].xt > minimum.xt){
const subrectangles = cut_z(minimum.xt, rectangles[i]);
rectangles[i] = subrectangles[0];
new_rectangles.push(subrectangles[1]);
};
};
// Recortes en Z
if(depth > hollow_bond.room_maximum_size){
if(rectangles[i].zf < minimum.zf){
const subrectangles = cut_x(minimum.zf, rectangles[i]);
depth -= Math.abs(minimum.zf - rectangles[i].zf);
rectangles[i] = subrectangles[1];
new_rectangles.push(subrectangles[0]);
};
if(depth > hollow_bond.room_maximum_size && rectangles[i].zt > minimum.zt){
const subrectangles = cut_x(minimum.zt, rectangles[i]);
rectangles[i] = subrectangles[0];
new_rectangles.push(subrectangles[1]);
};
};
};
};
rectangles.push(...new_rectangles);
// Blindar las habitaciones que contienen o tocan fosos para que no sean cortadas
for(const rect of rectangles){
const touches_hole = holes_y.some(hole => (
rect.xt > hole.xf && rect.xf < hole.xt &&
rect.zt > hole.zf && rect.zf < hole.zt
));
if(touches_hole){
rect.done = true;
rect.free = false;
};
};
return subdivide_rooms(rectangles, py);
};
const get_bounds = (x, z, axis) => {
const margin = hollow_bond.frame_padding + hollow_bond.room_padding
return {
xf : axis === "horizontal" ? x : x - margin,
xt : axis === "horizontal" ? x + hollow_bond.frame_width : x + margin,
zf : axis === "horizontal" ? z - margin : z,
zt : axis === "horizontal" ? z + margin : z + hollow_bond.frame_width
};
};
const collides = (b1, b2) => (
b1.xt > b2.xf && b1.xf < b2.xt &&
b1.zt > b2.zf && b1.zf < b2.zt
);
/**
* Genera los marcos interiores entre habitaciones en un piso determinado.
* @param {ChunkModel} chunk - Instancia del Chunk actual.
* @param {Array<RoomModel>} rooms - Habitaciones del piso actual.
* @param {number} py - Índice del piso.
*/
const generate_frames = (chunk, rooms, py) => {
chunk.frames = chunk.frames || {};
const margin = hollow_bond.frame_padding + hollow_bond.room_padding,
chunk_xf = chunk.xf,
chunk_xt = chunk.xt,
chunk_zf = chunk.zf,
chunk_zt = chunk.zt,
// Estructura para registrar los AABB de los marcos ya aceptados en este piso
accepted_bounds = [],
// Evaluamos cada par de habitaciones del piso una sola vez
total_rooms = rooms.length;
for(let i = 0; i < total_rooms; i ++){
const room_a = rooms[i];
for(const room_b of rooms){
let candidate = null;
// 1. Comprobar pared vertical compartida (normal en X, discurre a lo largo de Z)
if(room_a.x + room_a.width === room_b.x || room_b.x + room_b.width === room_a.x){
const wall_x = (room_a.x + room_a.width === room_b.x) ? room_b.x : room_a.x;
// Descartar si coincide con el límite exterior del Chunk
if(wall_x > chunk_xf && wall_x < chunk_xt){
// Dejamos margen para no pegar el marco a las esquinas ni al borde del Chunk
const minimum_z = Math.max(Math.max(room_a.z, room_b.z) + margin, chunk_zf + margin),
maximum_z = Math.min(Math.min(room_a.z + room_a.depth, room_b.z + room_b.depth) - margin, chunk_zt - margin),
span = maximum_z - minimum_z;
if(span >= hollow_bond.frame_width && hollow_bond.hash.rooms(wall_x, py, (minimum_z + maximum_z) | 0, 20) < hollow_bond.frame_wall_probability)
candidate = {
axis : "vertical",
x : wall_x,
z : minimum_z + (Common.random_decay(hollow_bond.hash.rooms(wall_x, py, (minimum_z + maximum_z) | 0, 21), 0, span - hollow_bond.frame_width, hollow_bond.frame_wall_decay) | 0),
room_a : room_a,
room_b : room_b
};
};
};
// 2. Comprobar pared horizontal compartida (normal en Z, discurre a lo largo de X)
if(!candidate && (room_a.z + room_a.depth === room_b.z || room_b.z + room_b.depth === room_a.z)){
const wall_z = room_a.z + room_a.depth === room_b.z ? room_b.z : room_a.z;
// Descartar si coincide con el límite exterior del Chunk
if(wall_z > chunk_zf && wall_z < chunk_zt){
const minimum_x = Math.max(Math.max(room_a.x, room_b.x) + margin, chunk_xf + margin),
maximum_x = Math.min(Math.min(room_a.x + room_a.width, room_b.x + room_b.width) - margin, chunk_xt - margin),
span = maximum_x - minimum_x;
if(span >= hollow_bond.frame_width && hollow_bond.hash.rooms((minimum_x + maximum_x) | 0, py, wall_z, 22) < hollow_bond.frame_wall_probability)
candidate = {
axis : "horizontal",
x : minimum_x + (Common.random_decay(hollow_bond.hash.rooms((minimum_x + maximum_x) | 0, py, wall_z, 23), 0, span - hollow_bond.frame_width, hollow_bond.frame_wall_decay) | 0),
z : wall_z,
room_a : room_a,
room_b : room_b
};
};
};
// 3. Si hay candidato, verificar que no colisiona con ningún marco existente
if(candidate){
const room_b_can = get_bounds(candidate.x, candidate.z, candidate.axis);
if(!accepted_bounds.some(bound => collides(bound, room_b_can))){
const frame = new FrameModel(hollow_bond, {
x: candidate.x,
y: py,
z: candidate.z,
axis: candidate.axis,
rooms: [candidate.room_a.id, candidate.room_b.id]
});
accepted_bounds.push(room_b_can);
// Vinculación bidireccional
candidate.room_a.frames.push(frame.id);
candidate.room_b.frames.push(frame.id);
// Registro en el Chunk y en el mapa global
chunk.frames[frame.id] = frame;
if(hollow_bond.map && hollow_bond.map.frames)
hollow_bond.map.frames[frame.id] = frame;
};
};
};
};
};
/**
* Conecta los marcos perimetrales del Chunk con las habitaciones correspondientes.
* @param {ChunkModel} chunk - Instancia del Chunk actual.
*/
const connect_chunk_frames = chunk => {
const eps = .001,
// Obtenemos todos los marcos del Chunk que tengan conexiones libres ([null, null] o [id, null])
chunk_frames = Object.values(chunk.frames).filter(frame => frame.rooms && frame.rooms.includes(null));
if(chunk_frames.length === 0)
return;
// Buscamos contra todas las habitaciones disponibles (las del mapa global para unir con vecinos, o las del chunk)
const available_rooms = Object.values(chunk.rooms);
for(const frame of chunk_frames)
for(const room of available_rooms)
// 1. Comprobar que coincidan en el piso
if(Math.round(room.y / (room.height || hollow_bond.room_height)) === frame.y && (frame.axis === "horizontal" ? (
(Math.abs(room.z + room.depth - frame.z) < eps || Math.abs(room.z - frame.z) < eps) &&
frame.x >= room.x - eps && frame.x + frame.width <= room.x + room.width + eps
) : (
(Math.abs(room.x + room.width - frame.x) < eps || Math.abs(room.x - frame.x) < eps) &&
frame.z >= room.z - eps && frame.z + frame.width <= room.z + room.depth + eps
))){
// Vinculación bidireccional
frame.connect_room(room.id);
room.frames.includes(frame.id) || room.frames.push(frame.id);
// Si el marco ya conectó sus 2 habitaciones, no hace falta evaluar más para este marco
if(!frame.rooms.includes(null))
break;
};
};
const get_frame = (chunk, id) => chunk.frames[id] || hollow_bond.map.frames[id];
// Criterio para identificar si un marco da al exterior del Chunk
const is_chunk_frame = (chunk, frame) => {
if(!frame)
return false;
if(
(chunk.chunk_frames && Object.values(chunk.chunk_frames).some(f => f.id === frame.id)) ||
frame.is_chunk_frame ||
(frame.rooms && frame.rooms.includes(null))
)
return true;
// Comprobación geométrica: el marco reposa sobre los bordes del Chunk
const eps = .5;
return (
Math.abs(frame.x - chunk.xf) < eps || Math.abs(frame.x - chunk.xt) < eps ||
Math.abs(frame.z - chunk.zf) < eps || Math.abs(frame.z - chunk.zt) < eps
);
};
/**
* Elimina habitaciones aisladas e islas cerradas dentro del Chunk.
* @param {ChunkModel} chunk - Instancia del Chunk actual.
*/
const cleanup_unconnected_rooms = chunk => {
// 1. Identificar las habitaciones "raíz" (tienen salida directa garantizada)
const reachable = new Set(),
queue = [];
for(const room_id in chunk.rooms)
if(
(chunk.rooms[room_id].elevators && chunk.rooms[room_id].elevators.length > 0) ||
(chunk.rooms[room_id].holes && chunk.rooms[room_id].holes.length > 0) ||
(chunk.rooms[room_id].frames && chunk.rooms[room_id].frames.some(frame_id => is_chunk_frame(chunk, get_frame(chunk, frame_id))))
){
reachable.add(chunk.rooms[room_id].id);
queue.push(chunk.rooms[room_id]);
};
// 2. Propagar la alcanzabilidad mediante BFS a través de los marcos transitables
while(queue.length > 0){
const current = queue.shift();
if(!current.frames)
continue;
for(const frame_id of current.frames){
const frame = get_frame(chunk, frame_id);
if(!frame || !frame.rooms)
continue;
for(const neighbor_id of frame.rooms)
// Si el vecino pertenece a este Chunk y aún no fue alcanzado
if(neighbor_id && chunk.rooms[neighbor_id] && !reachable.has(neighbor_id)){
reachable.add(neighbor_id);
queue.push(chunk.rooms[neighbor_id]);
};
};
};
// 3. Purgar las habitaciones inalcanzables y sus marcos huérfanos
const frames_to_delete = new Set();
for(const room_id in chunk.rooms)
if(!reachable.has(room_id)){
// Marcar sus marcos para eliminarlos
if (chunk.rooms[room_id].frames)
for (const frame_id of chunk.rooms[room_id].frames)
frames_to_delete.add(frame_id);
// Eliminar habitación del Chunk y del mapa global
delete chunk.rooms[room_id];
delete hollow_bond.map.rooms[room_id];
};
// 4. Limpiar los marcos que pertenecían a las habitaciones eliminadas
for(const frame_id of frames_to_delete){
delete chunk.frames[frame_id];
delete hollow_bond.map.frames[frame_id];
};
};
const generate_floor = (chunk, py) => {
const eps = .05;
// Delegación directa a tu método en HollowBond
const rectangles = divide_plane(chunk, py),
rooms = [],
floor_elevators = Object.values(chunk.elevators).filter(elevator => elevator.y == py);
if(!Array.isArray(rectangles))
return;
for(let i = 0; i < rectangles.length; i ++){
const width = rectangles[i].xt - rectangles[i].xf,
depth = rectangles[i].zt - rectangles[i].zf;
if(width <= 0 || depth <= 0)
continue;
// Asociar huecos que toquen o queden dentro de este rectángulo en este piso
const associated_holes = [],
// Asociar ascensores presentes en la habitación en este piso
associated_elevators = [];
let room;
for(const elevator of floor_elevators){
let is_connected = false;
switch(elevator.facing){
case "north":
is_connected = Math.abs(rectangles[i].zt - elevator.z) < eps && (Math.max(rectangles[i].xf, elevator.x) < Math.min(rectangles[i].xt, elevator.x + elevator.width));
break;
case "south":
is_connected = Math.abs(rectangles[i].zf - (elevator.z + elevator.depth)) < eps && (Math.max(rectangles[i].xf, elevator.x) < Math.min(rectangles[i].xt, elevator.x + elevator.width));
break;
case "west":
is_connected = Math.abs(rectangles[i].xt - elevator.x) < eps && (Math.max(rectangles[i].zf, elevator.z) < Math.min(rectangles[i].zt, elevator.z + elevator.depth));
break;
case "east":
is_connected = Math.abs(rectangles[i].xf - (elevator.x + elevator.width)) < eps && (Math.max(rectangles[i].zf, elevator.z) < Math.min(rectangles[i].zt, elevator.z + elevator.depth));
break;
};
is_connected && associated_elevators.push(elevator.id);
};
for(const hole_id in chunk.holes)
!(py < chunk.holes[hole_id].yf || py >= chunk.holes[hole_id].yt) && (
rectangles[i].xt > chunk.holes[hole_id].xf && rectangles[i].xf < chunk.holes[hole_id].xt &&
rectangles[i].zt > chunk.holes[hole_id].zf && rectangles[i].zf < chunk.holes[hole_id].zt
) && associated_holes.push(chunk.holes[hole_id].id || hole_id);
rooms.push(room = new RoomModel(hollow_bond, {
x : rectangles[i].xf,
y : py * hollow_bond.room_height,
z : rectangles[i].zf,
width : width,
height : hollow_bond.room_height,
depth : depth,
padding : hollow_bond.room_padding,
holes : associated_holes,
elevators : associated_elevators,
frames : []
}));
// Registro en el Chunk y en el consolidado global de MapManager
chunk.rooms[room.id] = room;
for(const id of associated_holes)
chunk.holes[id].rooms.push(room.id);
for(const id of associated_elevators)
chunk.elevators[id].room = room.id;
if(hollow_bond.map && hollow_bond.map.rooms)
hollow_bond.map.rooms[room.id] = room.id;
};
generate_frames(chunk, rooms, py);
};
this.generate_asyncronous = (chunk, callback) => new Promise(resolve => {
let py = chunk.yf;
const secure_time = 5,
tick = () => {
const start_time = performance.now();
while(py < chunk.yt && performance.now() - start_time < secure_time)
generate_floor(chunk, py ++);
if(py < chunk.yt)
requestAnimationFrame(tick);
else{
connect_chunk_frames(chunk);
cleanup_unconnected_rooms(chunk);
callback();
resolve();
};
};
requestAnimationFrame(tick);
});
/**
* Genera las instancias de RoomModel a partir de los rectángulos devueltos por divide_plane.
* @param {ChunkModel} chunk - Instancia del Chunk padre.
*/
this.generate = chunk => {
for(let py = chunk.yf; py < chunk.yt; py ++)
generate_floor(chunk, py);
connect_chunk_frames(chunk);
cleanup_unconnected_rooms(chunk);
};
};
return RoomsBuilder;
})();