237 lines
10 KiB
JavaScript
237 lines
10 KiB
JavaScript
"use strict";
|
|
|
|
const RoomObjectGame = (function(){
|
|
|
|
const RoomObjectGame = function(hollow_bond, model, geometry_factory, material_factory){
|
|
|
|
const self = this;
|
|
this.model = model;
|
|
|
|
const geom_fac = geometry_factory
|
|
|| (hollow_bond.dungeon && hollow_bond.dungeon.geometries && hollow_bond.dungeon.geometries.room)
|
|
|| new RoomGeometryGame(hollow_bond);
|
|
|
|
const mat_fac = material_factory
|
|
|| (hollow_bond.dungeon && hollow_bond.dungeon.materials && hollow_bond.dungeon.materials.room)
|
|
|| new RoomMaterialGame(hollow_bond);
|
|
|
|
// 1. Recuperamos marcos de esta sala
|
|
const frame_models = [];
|
|
if(model.frames && hollow_bond.map && hollow_bond.map.frames){
|
|
for(const fid of model.frames){
|
|
const f = hollow_bond.map.frames[fid];
|
|
if(f) frame_models.push(f);
|
|
}
|
|
}
|
|
|
|
// 1.1 Detectamos ascensores que lindan con esta sala para abrir el corte en la pared
|
|
const py = Math.round(model.y / model.height);
|
|
const map_elevators = (hollow_bond.map && hollow_bond.map.elevators) || {};
|
|
|
|
const door_w = hollow_bond.elevator_door_width || 1.1;
|
|
const cut_w = door_w + 0.08; // 1.18m (4cm de holgura a cada lado para que el marco solape)
|
|
const cut_h = 2.10; // 2.10m de altura para que el dintel cubra la parte superior
|
|
|
|
for(const eid in map_elevators){
|
|
const el = map_elevators[eid];
|
|
if(el.y !== py) continue;
|
|
|
|
const ex1 = el.x, ex2 = el.x + el.width;
|
|
const ez1 = el.z, ez2 = el.z + el.depth;
|
|
const rx1 = model.x, rx2 = model.x + model.width;
|
|
const rz1 = model.z, rz2 = model.z + model.depth;
|
|
|
|
let touches = false;
|
|
let axis = "horizontal";
|
|
let open_x = 0, open_z = 0;
|
|
|
|
// Norte: ascensor mira al norte, habitación está al norte (pared sur de la sala)
|
|
if(el.facing === "north" && Math.abs(rz2 - ez1) < 0.2 && ex2 > rx1 && ex1 < rx2){
|
|
touches = true;
|
|
axis = "horizontal";
|
|
open_x = (ex1 + ex2) * 0.5 - cut_w * 0.5;
|
|
open_z = rz2;
|
|
}
|
|
// Sur: ascensor mira al sur, habitación está al sur (pared norte de la sala)
|
|
else if(el.facing === "south" && Math.abs(rz1 - ez2) < 0.2 && ex2 > rx1 && ex1 < rx2){
|
|
touches = true;
|
|
axis = "horizontal";
|
|
open_x = (ex1 + ex2) * 0.5 - cut_w * 0.5;
|
|
open_z = rz1;
|
|
}
|
|
// Oeste: ascensor mira al oeste, habitación está al oeste (pared este de la sala)
|
|
else if(el.facing === "west" && Math.abs(rx2 - ex1) < 0.2 && ez2 > rz1 && ez1 < rz2){
|
|
touches = true;
|
|
axis = "vertical";
|
|
open_x = rx2;
|
|
open_z = (ez1 + ez2) * 0.5 - cut_w * 0.5;
|
|
}
|
|
// Este: ascensor mira al este, habitación está al este (pared oeste de la sala)
|
|
else if(el.facing === "east" && Math.abs(rx1 - ex2) < 0.2 && ez2 > rz1 && ez1 < rz2){
|
|
touches = true;
|
|
axis = "vertical";
|
|
open_x = rx1;
|
|
open_z = (ez1 + ez2) * 0.5 - cut_w * 0.5;
|
|
}
|
|
|
|
if(touches){
|
|
model.elevators = model.elevators || [];
|
|
if(!model.elevators.includes(el.id)) model.elevators.push(el.id);
|
|
|
|
frame_models.push({
|
|
id: `elev_cut_${el.id}`,
|
|
axis: axis,
|
|
x: open_x,
|
|
z: open_z,
|
|
width: cut_w,
|
|
height: cut_h
|
|
});
|
|
}
|
|
}
|
|
|
|
// 2. Recuperamos huecos verticales de esta sala
|
|
const hole_models = [];
|
|
if(model.holes && hollow_bond.map && hollow_bond.map.holes){
|
|
for(const hid of model.holes){
|
|
const h = hollow_bond.map.holes[hid];
|
|
if(h) hole_models.push(h);
|
|
}
|
|
}
|
|
|
|
// 3. Geometría con vanos de pared y perforaciones en suelo/techo
|
|
const geometry = geom_fac.get_for_room(model, frame_models, hole_models);
|
|
|
|
// 4. Materiales del tema
|
|
const materials = mat_fac.get_materials();
|
|
|
|
this.mesh = new THREE.Mesh(geometry, materials);
|
|
|
|
const center_x = model.x + model.width / 2;
|
|
const center_y = model.y + (model.height / 2);
|
|
const center_z = model.z + model.depth / 2;
|
|
|
|
this.mesh.position.set(center_x, center_y, center_z);
|
|
|
|
// 5. Generación de Barandillas en los huecos con caída inferior
|
|
this.railings = [];
|
|
|
|
const build_railings = () => {
|
|
const py = Math.round(model.y / model.height);
|
|
// Solo huecos que perforan hacia abajo en este piso
|
|
const active_holes = hole_models.filter(h => py > h.yf && py < h.yt);
|
|
if(active_holes.length === 0) return;
|
|
|
|
const railing_mat = (hollow_bond.materials && hollow_bond.materials.railing)
|
|
? hollow_bond.materials.railing.get()
|
|
: (hollow_bond.dungeon && hollow_bond.dungeon.materials && hollow_bond.dungeon.materials.railing
|
|
? hollow_bond.dungeon.materials.railing.get()
|
|
: null);
|
|
|
|
const landing_size = hollow_bond.stairs_landing_size || 1.0;
|
|
|
|
const subtract_1d = (min, max, cuts) => {
|
|
let segments = [{ start: min, end: max }];
|
|
for(const c of cuts){
|
|
const next = [];
|
|
for(const s of segments){
|
|
if(c.end <= s.start || c.start >= s.end){
|
|
next.push(s);
|
|
continue;
|
|
}
|
|
if(c.start > s.start) next.push({ start: s.start, end: c.start });
|
|
if(c.end < s.end) next.push({ start: c.end, end: s.end });
|
|
}
|
|
segments = next;
|
|
}
|
|
return segments.filter(s => (s.end - s.start) > 0.05);
|
|
};
|
|
|
|
for(const hole of active_holes){
|
|
const hx1 = Math.max(model.x, hole.xf);
|
|
const hx2 = Math.min(model.x + model.width, hole.xt);
|
|
const hz1 = Math.max(model.z, hole.zf);
|
|
const hz2 = Math.min(model.z + model.depth, hole.zt);
|
|
|
|
if(hx2 <= hx1 || hz2 <= hz1) continue;
|
|
|
|
// Identificar descansillos de escaleras
|
|
const cuts = { north: [], south: [], west: [], east: [] };
|
|
|
|
for(const stair of (hole.stairs || [])){
|
|
const stair_top_py = stair.yf + 1;
|
|
let at_floor = (stair.yf === py) ? "bottom" : (stair_top_py === py ? "top" : null);
|
|
if(!at_floor) continue;
|
|
|
|
const is_start = (at_floor === "bottom" && stair.direction === 1) ||
|
|
(at_floor === "top" && stair.direction === -1);
|
|
|
|
if(stair.wall === "north" || stair.wall === "south"){
|
|
const lx1 = is_start ? stair.xf : (stair.xt - landing_size);
|
|
const lx2 = lx1 + landing_size;
|
|
cuts[stair.wall].push({ start: lx1, end: lx2 });
|
|
|
|
// Esquinas: liberar el lado perpendicular si toca el lateral
|
|
if(Math.abs(lx1 - hole.xf) < 0.05) cuts.west.push({ start: hole.zf, end: hole.zf + landing_size });
|
|
if(Math.abs(lx2 - hole.xt) < 0.05) cuts.east.push({ start: hole.zf, end: hole.zf + landing_size });
|
|
} else {
|
|
const lz1 = is_start ? stair.zf : (stair.zt - landing_size);
|
|
const lz2 = lz1 + landing_size;
|
|
cuts[stair.wall].push({ start: lz1, end: lz2 });
|
|
|
|
if(Math.abs(lz1 - hole.zf) < 0.05) cuts.north.push({ start: (stair.wall === "west" ? hole.xf : hole.xt - landing_size), end: (stair.wall === "west" ? hole.xf + landing_size : hole.xt) });
|
|
if(Math.abs(lz2 - hole.zt) < 0.05) cuts.south.push({ start: (stair.wall === "west" ? hole.xf : hole.xt - landing_size), end: (stair.wall === "west" ? hole.xf + landing_size : hole.xt) });
|
|
}
|
|
}
|
|
|
|
// Generar los 4 lados perimetrales
|
|
const add_side = (side, min, max, fixed, cuts_arr) => {
|
|
const valid_segs = subtract_1d(min, max, cuts_arr);
|
|
for(const s of valid_segs){
|
|
const is_horiz = (side === "north" || side === "south");
|
|
const seg_data = {
|
|
side: side,
|
|
y: model.y, // Cota de mundo
|
|
height: 1.0,
|
|
thickness: 0.08,
|
|
xf: is_horiz ? s.start : fixed,
|
|
xt: is_horiz ? s.end : fixed,
|
|
zf: is_horiz ? fixed : s.start,
|
|
zt: is_horiz ? fixed : s.end
|
|
};
|
|
|
|
const rail_obj = new RailingObjectGame(hollow_bond, seg_data, railing_mat, model);
|
|
self.railings.push(rail_obj);
|
|
self.mesh.add(rail_obj.mesh);
|
|
}
|
|
};
|
|
|
|
if(hole.zf >= model.z) add_side("north", hx1, hx2, hole.zf, cuts.north);
|
|
if(hole.zt <= (model.z + model.depth)) add_side("south", hx1, hx2, hole.zt, cuts.south);
|
|
if(hole.xf >= model.x) add_side("west", hz1, hz2, hole.xf, cuts.west);
|
|
if(hole.xt <= (model.x + model.width)) add_side("east", hz1, hz2, hole.xt, cuts.east);
|
|
}
|
|
};
|
|
|
|
build_railings();
|
|
|
|
// 6. Collider con caídas y pasos libres
|
|
this.collider = new RoomCollider(hollow_bond, model, frame_models, hole_models);
|
|
// Vinculamos los colliders de las barandillas directamente a la sala
|
|
this.collider.railings = this.railings.map(r => r.collider);
|
|
|
|
this.destroy = () => {
|
|
if(self.railings){
|
|
for(let i = 0; i < self.railings.length; i++){
|
|
self.railings[i].destroy();
|
|
}
|
|
self.railings = [];
|
|
}
|
|
if(self.mesh){
|
|
self.mesh.geometry.dispose();
|
|
}
|
|
};
|
|
|
|
};
|
|
|
|
return RoomObjectGame;
|
|
})(); |