704 lines
29 KiB
JavaScript
704 lines
29 KiB
JavaScript
"use strict";
|
|
|
|
const DungeonManager = (function(){
|
|
|
|
const DungeonManager = function(hollow_bond){
|
|
|
|
const self = this;
|
|
let debug_hud = null; // <-- Referencia al HUD
|
|
|
|
this.scene = null;
|
|
this.camera = null;
|
|
this.renderer = null;
|
|
this.on = false;
|
|
|
|
this.floor_height = hollow_bond.room_height || 3;
|
|
|
|
// Variables de streaming por topología
|
|
this.current_room = null;
|
|
this.current_chunk = { x: 0, y: 0, z: 0 };
|
|
|
|
// Parámetros de niveles de salas (con fallback a 3 y 5)
|
|
hollow_bond.horizontal_loaded_rooms_levels = (hollow_bond.horizontal_loaded_rooms_levels !== undefined)
|
|
? hollow_bond.horizontal_loaded_rooms_levels
|
|
: 3;
|
|
|
|
hollow_bond.horizontal_unloaded_rooms_level = (hollow_bond.horizontal_unloaded_rooms_level !== undefined)
|
|
? hollow_bond.horizontal_unloaded_rooms_level
|
|
: 5;
|
|
|
|
// Gestores de geometrías y materiales
|
|
this.geometries = {
|
|
room : (typeof RoomGeometryGame !== "undefined") ? new RoomGeometryGame(hollow_bond) : null,
|
|
frame : (typeof FrameGeometryGame !== "undefined") ? new FrameGeometryGame(hollow_bond) : null,
|
|
hole : (typeof HoleGeometryGame !== "undefined") ? new HoleGeometryGame(hollow_bond) : null,
|
|
stair : (typeof StairGeometryGame !== "undefined") ? new StairGeometryGame(hollow_bond) : null,
|
|
elevator : (typeof ElevatorGeometryGame !== "undefined") ? new ElevatorGeometryGame(hollow_bond) : null,
|
|
railing : (typeof RailingGeometryGame !== "undefined") ? new RailingGeometryGame(hollow_bond) : null,
|
|
elevator_frame : (typeof ElevatorFrameGeometryGame !== "undefined") ? new ElevatorFrameGeometryGame(hollow_bond) : null
|
|
};
|
|
|
|
this.materials = {
|
|
room : (typeof RoomMaterialGame !== "undefined") ? new RoomMaterialGame(hollow_bond) : null,
|
|
frame : (typeof FrameMaterialGame !== "undefined") ? new FrameMaterialGame(hollow_bond) : null,
|
|
hole : (typeof HoleMaterialGame !== "undefined") ? new HoleMaterialGame(hollow_bond) : null,
|
|
stair : (typeof StairMaterialGame !== "undefined") ? new StairMaterialGame(hollow_bond) : null,
|
|
elevator : (typeof ElevatorMaterialGame !== "undefined") ? new ElevatorMaterialGame(hollow_bond) : null,
|
|
railing : (typeof RailingMaterialGame !== "undefined") ? new RailingMaterialGame(hollow_bond) : null,
|
|
};
|
|
|
|
// Diccionarios de objetos visuales actualmente instanciados en escena
|
|
this.objects = {
|
|
room : {},
|
|
frame : {},
|
|
hole : {},
|
|
stair : {},
|
|
elevator : {},
|
|
railing : {}
|
|
};
|
|
|
|
let last_frame_time = performance.now();
|
|
let animation_frame_id = null;
|
|
|
|
/**
|
|
* Inicializa la partida en primera persona con la altura seleccionada.
|
|
* @param {number} player_height
|
|
*/
|
|
this.start = (player_height = 1.75) => {
|
|
self.on = true;
|
|
setup_scene();
|
|
|
|
debug_hud = new DungeonHudView(hollow_bond, self);
|
|
|
|
if(hollow_bond.controls && hollow_bond.controls.game){
|
|
hollow_bond.controls.game.set_dimensions(player_height);
|
|
hollow_bond.controls.game.setup(self);
|
|
}
|
|
|
|
self.current_chunk = { x: 0, y: 0, z: 0 };
|
|
|
|
// Generamos el chunk inicial y sus vecinos en MapManager
|
|
hollow_bond.map.update_active_center(0, 0, 0, () => {
|
|
let spawn_room_model = null;
|
|
const center_chunk = hollow_bond.map.get_chunk(0, 0, 0);
|
|
|
|
if(center_chunk && center_chunk.rooms){
|
|
for(const id in center_chunk.rooms){
|
|
const r = center_chunk.rooms[id];
|
|
// Priorizamos sala segura sin agujeros de caída
|
|
if((!r.holes || r.holes.length === 0) && (!r.voids || r.voids.length === 0)){
|
|
spawn_room_model = r;
|
|
break;
|
|
}
|
|
}
|
|
if(!spawn_room_model){
|
|
const keys = Object.keys(center_chunk.rooms);
|
|
if(keys.length > 0) spawn_room_model = center_chunk.rooms[keys[0]];
|
|
}
|
|
}
|
|
|
|
if(spawn_room_model){
|
|
// 1. Instanciamos la sala inicial de aparición
|
|
const room_obj = new RoomObjectGame(
|
|
hollow_bond,
|
|
spawn_room_model,
|
|
self.geometries.room,
|
|
self.materials.room
|
|
);
|
|
self.objects.room[spawn_room_model.id] = room_obj;
|
|
self.scene.add(room_obj.mesh);
|
|
self.current_room = room_obj;
|
|
|
|
// 2. Ejecutamos el streaming para cargar las adyacentes hasta nivel 3
|
|
self.update_rooms_streaming();
|
|
|
|
// 3. Ubicamos al jugador en el centro de la sala
|
|
const spawn_x = spawn_room_model.x + spawn_room_model.width / 2;
|
|
const spawn_z = spawn_room_model.z + spawn_room_model.depth / 2;
|
|
const floor_y = room_obj.collider.get_floor_height();
|
|
const eye_height = hollow_bond.controls.game.standing_height;
|
|
|
|
self.camera.position.set(spawn_x, floor_y + eye_height, spawn_z);
|
|
}
|
|
|
|
if(hollow_bond.show_fps && hollow_bond.views && hollow_bond.views.fps){
|
|
hollow_bond.views.fps.go();
|
|
}
|
|
|
|
last_frame_time = performance.now();
|
|
render_loop();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Comprueba en qué sala cargada se encuentra el jugador.
|
|
*/
|
|
const check_room_transition = () => {
|
|
if(!self.current_room || !self.camera) return;
|
|
|
|
const pos = self.camera.position;
|
|
const eye_height = (hollow_bond.controls && hollow_bond.controls.game)
|
|
? hollow_bond.controls.game.current_height
|
|
: 1.75;
|
|
const feet_y = pos.y - eye_height;
|
|
|
|
// Si sigue dentro de la sala activa en 3D, no hacemos nada
|
|
if(self.current_room.collider.contains(pos.x, feet_y, pos.z)){
|
|
return;
|
|
}
|
|
|
|
// Buscamos sala candidata filtrando estrictamente por cota vertical primero
|
|
for(const rid in self.objects.room){
|
|
const room_obj = self.objects.room[rid];
|
|
if(room_obj === self.current_room) continue;
|
|
|
|
const room_floor_y = room_obj.collider.get_floor_height();
|
|
|
|
// Si la sala está en otro piso (a más de 1 metro de distancia vertical de los pies), SE IGNORA
|
|
if(Math.abs(feet_y - room_floor_y) > 1.0) continue;
|
|
|
|
if(room_obj.collider.contains(pos.x, feet_y, pos.z)){
|
|
self.set_current_room(room_obj);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Asigna la nueva sala activa y actualiza el streaming y los Chunks si corresponde.
|
|
* @param {RoomObjectGame} room_obj
|
|
*/
|
|
this.set_current_room = room_obj => {
|
|
if(!room_obj || self.current_room === room_obj) return;
|
|
|
|
self.current_room = room_obj;
|
|
|
|
const chunk_size = hollow_bond.chunk_size || 16;
|
|
const chunk_height = (hollow_bond.chunk_height || (chunk_size * hollow_bond.room_height));
|
|
|
|
const cx = Math.floor((room_obj.model.x + room_obj.model.width / 2) / chunk_size);
|
|
const cz = Math.floor((room_obj.model.z + room_obj.model.depth / 2) / chunk_size);
|
|
const cy = Math.floor((room_obj.model.y + room_obj.model.height / 2) / chunk_height);
|
|
|
|
if(cx !== self.current_chunk.x || cy !== self.current_chunk.y || cz !== self.current_chunk.z){
|
|
self.current_chunk = { x: cx, y: cy, z: cz };
|
|
if(hollow_bond.map && hollow_bond.map.update_active_center){
|
|
hollow_bond.map.update_active_center(cx, cy, cz, () => {
|
|
self.update_rooms_streaming();
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
self.update_rooms_streaming();
|
|
};
|
|
|
|
/**
|
|
* Algoritmo BFS para streaming de habitaciones y marcos por niveles de conectividad.
|
|
*/
|
|
this.update_rooms_streaming = () => {
|
|
if(!self.current_room || !hollow_bond.map) return;
|
|
|
|
const load_limit = hollow_bond.horizontal_loaded_rooms_levels || 3;
|
|
const unload_limit = hollow_bond.horizontal_unloaded_rooms_level || 5;
|
|
|
|
const queue = [{ id: self.current_room.model.id, depth: 0 }];
|
|
const room_depths = { [self.current_room.model.id]: 0 };
|
|
|
|
while(queue.length > 0){
|
|
const { id, depth } = queue.shift();
|
|
|
|
const room_model = hollow_bond.map.rooms ? hollow_bond.map.rooms[id] : null;
|
|
if(!room_model) continue;
|
|
|
|
// A) Expansión horizontal por MARCOS
|
|
if(room_model.frames){
|
|
for(const fid of room_model.frames){
|
|
const frame_model = hollow_bond.map.frames ? hollow_bond.map.frames[fid] : null;
|
|
if(!frame_model || !frame_model.rooms) continue;
|
|
|
|
for(const neighbor_id of frame_model.rooms){
|
|
if(neighbor_id && neighbor_id !== id && room_depths[neighbor_id] === undefined){
|
|
const next_depth = depth + 1;
|
|
room_depths[neighbor_id] = next_depth;
|
|
if(next_depth <= unload_limit) queue.push({ id: neighbor_id, depth: next_depth });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// B) Expansión vertical por HUECOS
|
|
if(room_model.holes){
|
|
for(const hid of room_model.holes){
|
|
const hole_model = hollow_bond.map.holes ? hollow_bond.map.holes[hid] : null;
|
|
if(!hole_model || !hole_model.rooms) continue;
|
|
|
|
for(const neighbor_id of hole_model.rooms){
|
|
if(neighbor_id && neighbor_id !== id && room_depths[neighbor_id] === undefined){
|
|
const next_depth = depth + 1;
|
|
room_depths[neighbor_id] = next_depth;
|
|
if(next_depth <= unload_limit) queue.push({ id: neighbor_id, depth: next_depth });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// C) Expansión vertical por ASCENSORES
|
|
if(room_model.elevators){
|
|
for(const eid of room_model.elevators){
|
|
const elev_model = hollow_bond.map.elevators ? hollow_bond.map.elevators[eid] : null;
|
|
if(!elev_model || !elev_model.rooms) continue;
|
|
|
|
for(const neighbor_id of elev_model.rooms){
|
|
if(neighbor_id && neighbor_id !== id && room_depths[neighbor_id] === undefined){
|
|
const next_depth = depth + 1;
|
|
room_depths[neighbor_id] = next_depth;
|
|
if(next_depth <= unload_limit) queue.push({ id: neighbor_id, depth: next_depth });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// D) Expansión vertical por ESCALERAS
|
|
if(room_model.stairs){
|
|
for(const sid of room_model.stairs){
|
|
const stair_model = hollow_bond.map.stairs ? hollow_bond.map.stairs[sid] : null;
|
|
if(!stair_model || !stair_model.rooms) continue;
|
|
|
|
for(const neighbor_id of stair_model.rooms){
|
|
if(neighbor_id && neighbor_id !== id && room_depths[neighbor_id] === undefined){
|
|
const next_depth = depth + 1;
|
|
room_depths[neighbor_id] = next_depth;
|
|
if(next_depth <= unload_limit) queue.push({ id: neighbor_id, depth: next_depth });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Instanciar salas dentro del rango de carga
|
|
for(const rid in room_depths){
|
|
const depth = room_depths[rid];
|
|
if(depth <= load_limit){
|
|
if(!self.objects.room[rid]){
|
|
const model = hollow_bond.map.rooms ? hollow_bond.map.rooms[rid] : null;
|
|
if(model){
|
|
const room_obj = new RoomObjectGame(
|
|
hollow_bond,
|
|
model,
|
|
self.geometries.room,
|
|
self.materials.room
|
|
);
|
|
self.objects.room[rid] = room_obj;
|
|
self.scene.add(room_obj.mesh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Instanciar marcos de las salas cargadas
|
|
for(const rid in self.objects.room){
|
|
const r_obj = self.objects.room[rid];
|
|
if(r_obj.model && r_obj.model.frames){
|
|
for(const fid of r_obj.model.frames){
|
|
if(!self.objects.frame[fid]){
|
|
const frame_model = hollow_bond.map.frames ? hollow_bond.map.frames[fid] : null;
|
|
if(frame_model){
|
|
const frame_obj = new FrameObjectGame(
|
|
hollow_bond,
|
|
frame_model,
|
|
self.geometries.frame,
|
|
self.materials.frame
|
|
);
|
|
self.objects.frame[fid] = frame_obj;
|
|
self.scene.add(frame_obj.mesh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4. Instanciar huecos de las salas cargadas
|
|
for(const rid in self.objects.room){
|
|
const r_obj = self.objects.room[rid];
|
|
if(r_obj.model && r_obj.model.holes){
|
|
for(const hid of r_obj.model.holes){
|
|
if(!self.objects.hole[hid]){
|
|
const hole_model = hollow_bond.map.holes ? hollow_bond.map.holes[hid] : null;
|
|
if(hole_model){
|
|
const hole_obj = new HoleObjectGame(
|
|
hollow_bond,
|
|
hole_model,
|
|
self.geometries.hole,
|
|
self.materials.hole
|
|
);
|
|
self.objects.hole[hid] = hole_obj;
|
|
self.scene.add(hole_obj.mesh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. Instanciar escaleras de los huecos / salas cargadas
|
|
for(const rid in self.objects.room){
|
|
const r_obj = self.objects.room[rid];
|
|
|
|
// Escaleras alojadas dentro de los huecos de la sala
|
|
if(r_obj.model && r_obj.model.holes){
|
|
for(const hid of r_obj.model.holes){
|
|
const hole_model = hollow_bond.map.holes ? hollow_bond.map.holes[hid] : null;
|
|
if(hole_model && hole_model.stairs){
|
|
for(const stair_model of hole_model.stairs){
|
|
if(!self.objects.stair[stair_model.id]){
|
|
const stair_obj = new StairObjectGame(
|
|
hollow_bond,
|
|
stair_model,
|
|
self.materials.stair,
|
|
self.geometries.stair
|
|
);
|
|
self.objects.stair[stair_model.id] = stair_obj;
|
|
self.scene.add(stair_obj.mesh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Escaleras registradas directamente en la sala
|
|
if(r_obj.model && r_obj.model.stairs){
|
|
for(const sid of r_obj.model.stairs){
|
|
if(!self.objects.stair[sid]){
|
|
const stair_model = hollow_bond.map.stairs ? hollow_bond.map.stairs[sid] : null;
|
|
if(stair_model){
|
|
const stair_obj = new StairObjectGame(
|
|
hollow_bond,
|
|
stair_model,
|
|
self.materials.stair,
|
|
self.geometries.stair
|
|
);
|
|
self.objects.stair[sid] = stair_obj;
|
|
self.scene.add(stair_obj.mesh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. Descargar salas fuera de rango
|
|
for(const rid in self.objects.room){
|
|
const depth = room_depths[rid];
|
|
if(depth === undefined || depth > unload_limit){
|
|
if(self.current_room && self.current_room.model.id === rid) continue;
|
|
|
|
const obj = self.objects.room[rid];
|
|
self.scene.remove(obj.mesh);
|
|
obj.destroy && obj.destroy();
|
|
delete self.objects.room[rid];
|
|
}
|
|
}
|
|
|
|
// 6. Descargar marcos huérfanos
|
|
for(const fid in self.objects.frame){
|
|
const f_obj = self.objects.frame[fid];
|
|
let is_needed = false;
|
|
if(f_obj.model && f_obj.model.rooms){
|
|
for(const rid of f_obj.model.rooms){
|
|
if(rid && self.objects.room[rid]){
|
|
is_needed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!is_needed){
|
|
self.scene.remove(f_obj.mesh);
|
|
f_obj.destroy && f_obj.destroy();
|
|
delete self.objects.frame[fid];
|
|
}
|
|
}
|
|
|
|
// 6. Instanciar ascensores de los chunks / mapa cargados
|
|
for(const rid in self.objects.room){
|
|
const r_obj = self.objects.room[rid];
|
|
if(r_obj.model && r_obj.model.elevators){
|
|
for(const eid of r_obj.model.elevators){
|
|
if(!self.objects.elevator[eid]){
|
|
const el_model = hollow_bond.map.elevators ? hollow_bond.map.elevators[eid] : null;
|
|
if(el_model){
|
|
const el_obj = new ElevatorObjectGame(
|
|
hollow_bond,
|
|
el_model,
|
|
self.materials.elevator,
|
|
self.geometries.elevator
|
|
);
|
|
self.objects.elevator[eid] = el_obj;
|
|
self.scene.add(el_obj.mesh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 7. Descargar huecos huérfanos
|
|
for(const hid in self.objects.hole){
|
|
const h_obj = self.objects.hole[hid];
|
|
let is_needed = false;
|
|
if(h_obj.model && h_obj.model.rooms){
|
|
for(const rid of h_obj.model.rooms){
|
|
if(rid && self.objects.room[rid]){
|
|
is_needed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!is_needed){
|
|
self.scene.remove(h_obj.mesh);
|
|
h_obj.destroy && h_obj.destroy();
|
|
delete self.objects.hole[hid];
|
|
}
|
|
}
|
|
|
|
// 8. Descargar escaleras huérfanas
|
|
for(const sid in self.objects.stair){
|
|
const s_obj = self.objects.stair[sid];
|
|
let is_needed = false;
|
|
|
|
if(s_obj.model){
|
|
// Si su foso sigue cargado en escena
|
|
if(s_obj.model.hole_id && self.objects.hole[s_obj.model.hole_id]){
|
|
is_needed = true;
|
|
} else if(s_obj.model.rooms){
|
|
for(const rid of s_obj.model.rooms){
|
|
if(rid && self.objects.room[rid]){
|
|
is_needed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!is_needed){
|
|
self.scene.remove(s_obj.mesh);
|
|
s_obj.destroy && s_obj.destroy();
|
|
delete self.objects.stair[sid];
|
|
}
|
|
}
|
|
|
|
// 9. Descargar ascensores huérfanos
|
|
for(const eid in self.objects.elevator){
|
|
const el_obj = self.objects.elevator[eid];
|
|
let is_needed = false;
|
|
|
|
for(const rid in self.objects.room){
|
|
const r_model = self.objects.room[rid].model;
|
|
if(r_model && r_model.elevators && r_model.elevators.includes(eid)){
|
|
is_needed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!is_needed){
|
|
self.scene.remove(el_obj.mesh);
|
|
el_obj.destroy && el_obj.destroy();
|
|
delete self.objects.elevator[eid];
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Callback de integración con MapManager cuando se genera un Chunk.
|
|
*/
|
|
this.sync_chunk = chunk => {
|
|
if(self.on && self.current_room){
|
|
self.update_rooms_streaming();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Callback de integración con MapManager cuando se descarga un Chunk.
|
|
*/
|
|
this.remove_chunk = chunk => {
|
|
if(self.on && self.current_room){
|
|
self.update_rooms_streaming();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Detiene el bucle y limpia la sesión.
|
|
*/
|
|
this.stop = () => {
|
|
self.on = false;
|
|
if(animation_frame_id){
|
|
cancelAnimationFrame(animation_frame_id);
|
|
}
|
|
if(debug_hud){
|
|
debug_hud.destroy();
|
|
debug_hud = null;
|
|
}
|
|
self.reset();
|
|
};
|
|
|
|
/**
|
|
* Limpia todos los objetos 3D y buffers de GPU.
|
|
*/
|
|
this.reset = () => {
|
|
|
|
if(debug_hud){
|
|
debug_hud.destroy();
|
|
debug_hud = null;
|
|
}
|
|
|
|
for(const key in self.objects){
|
|
for(const id in self.objects[key]){
|
|
self.scene && self.scene.remove(self.objects[key][id].mesh);
|
|
self.objects[key][id].destroy && self.objects[key][id].destroy();
|
|
}
|
|
self.objects[key] = {};
|
|
}
|
|
|
|
for(const key in self.geometries){
|
|
if(self.geometries[key] && self.geometries[key].reset){
|
|
self.geometries[key].reset();
|
|
}
|
|
}
|
|
|
|
for(const key in self.materials){
|
|
if(self.materials[key] && self.materials[key].reset){
|
|
self.materials[key].reset();
|
|
}
|
|
}
|
|
};
|
|
|
|
const setup_scene = () => {
|
|
self.scene = new THREE.Scene();
|
|
self.scene.background = new THREE.Color(0x050505);
|
|
|
|
// Niebla volumétrica ligera acorde a los Backrooms
|
|
self.scene.fog = new THREE.FogExp2(0x050505, 0.035);
|
|
|
|
self.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
|
|
|
|
self.renderer = new THREE.WebGLRenderer({
|
|
antialias : hollow_bond.render_scale >= 100,
|
|
powerPreference : "high-performance"
|
|
});
|
|
|
|
self.renderer.domElement.className = "hb-canvas-dungeon";
|
|
self.renderer.domElement.tabIndex = 1;
|
|
hollow_bond.item_self.appendChild(self.renderer.domElement);
|
|
|
|
self.update_resolution();
|
|
|
|
const ambient = new THREE.AmbientLight(0xfff5e6, 0.45);
|
|
self.scene.add(ambient);
|
|
|
|
// window.addEventListener("keydown", e => {
|
|
// if(e.code === "KeyE" && self.camera){
|
|
// for(const eid in self.objects.elevator){
|
|
// if(self.objects.elevator[eid].interact(self.camera.position)){
|
|
// break;
|
|
// }
|
|
// }
|
|
// }
|
|
// });
|
|
|
|
// Interacción por click en el botón del ascensor
|
|
const raycaster = new THREE.Raycaster();
|
|
const center_screen = new THREE.Vector2(0, 0); // Centro exacto del punto de mira
|
|
|
|
window.addEventListener("mousedown", e => {
|
|
// Solo botón izquierdo del ratón (button === 0)
|
|
if(e.button !== 0 || !self.camera || !self.on) return;
|
|
|
|
raycaster.setFromCamera(center_screen, self.camera);
|
|
|
|
for(const eid in self.objects.elevator){
|
|
const el_obj = self.objects.elevator[eid];
|
|
if(el_obj.try_click_button(raycaster)){
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
window.addEventListener("resize", self.update_resolution);
|
|
};
|
|
|
|
this.update_resolution = () => {
|
|
if(!self.renderer || !self.camera) return;
|
|
const w = window.innerWidth,
|
|
h = window.innerHeight,
|
|
scale = Math.max(0.03125, hollow_bond.render_scale / 100);
|
|
|
|
self.camera.aspect = w / h;
|
|
self.camera.updateProjectionMatrix();
|
|
self.renderer.setSize(Math.floor(w * scale), Math.floor(h * scale), false);
|
|
self.renderer.domElement.style.width = "100%";
|
|
self.renderer.domElement.style.height = "100%";
|
|
};
|
|
|
|
const render_loop = () => {
|
|
if(!self.on) return;
|
|
|
|
animation_frame_id = requestAnimationFrame(render_loop);
|
|
|
|
const now = performance.now(),
|
|
delta = Math.min((now - last_frame_time) / 1000, 0.1);
|
|
|
|
last_frame_time = now;
|
|
|
|
// 1. Detección y transición entre habitaciones
|
|
check_room_transition();
|
|
|
|
// // 2. Físicas y movimiento del jugador
|
|
// if(hollow_bond.controls && hollow_bond.controls.game){
|
|
// hollow_bond.controls.game.update(self, delta);
|
|
|
|
// // Colisiones contra escaleras instanciadas
|
|
// for(const sid in self.objects.stair){
|
|
// self.objects.stair[sid].collider.resolve_player(
|
|
// self.camera.position,
|
|
// hollow_bond.controls.game,
|
|
// 0.22
|
|
// );
|
|
// }
|
|
// }
|
|
|
|
// 2. Físicas y movimiento del jugador
|
|
if(hollow_bond.controls && hollow_bond.controls.game){
|
|
hollow_bond.controls.game.update(self, delta);
|
|
|
|
// Colisiones contra escaleras instanciadas
|
|
for(const sid in self.objects.stair){
|
|
self.objects.stair[sid].collider.resolve_player(
|
|
self.camera.position,
|
|
hollow_bond.controls.game,
|
|
0.22
|
|
);
|
|
}
|
|
|
|
// Animación y colisiones de ascensores instanciados
|
|
for(const eid in self.objects.elevator){
|
|
const el_obj = self.objects.elevator[eid];
|
|
el_obj.update(delta);
|
|
el_obj.collider.resolve_player(
|
|
self.camera.position,
|
|
hollow_bond.controls.game,
|
|
hollow_bond.controls.game.player_radius
|
|
);
|
|
}
|
|
}
|
|
|
|
// Actualizamos la telemetría en cada frame
|
|
debug_hud && debug_hud.tick();
|
|
|
|
if(hollow_bond.show_fps && hollow_bond.views && hollow_bond.views.fps){
|
|
hollow_bond.views.fps.tick();
|
|
}
|
|
|
|
self.renderer.render(self.scene, self.camera);
|
|
};
|
|
|
|
};
|
|
|
|
return DungeonManager;
|
|
})(); |