193 lines
7.6 KiB
JavaScript
193 lines
7.6 KiB
JavaScript
"use strict";
|
|
|
|
const RoomCollider = (function(){
|
|
|
|
const RoomCollider = function(hollow_bond, model, frames = [], holes = []){
|
|
|
|
const self = this;
|
|
this.model = model;
|
|
this.frames = frames;
|
|
this.holes = holes;
|
|
|
|
const pad = model.padding || 0;
|
|
|
|
// 1. Límites exactos del espacio de la sala (contacto continuo)
|
|
this.room_bounds = {
|
|
min_x: model.x,
|
|
max_x: model.x + model.width,
|
|
min_z: model.z,
|
|
max_z: model.z + model.depth,
|
|
min_y: model.y,
|
|
max_y: model.y + model.height
|
|
};
|
|
|
|
// 2. Límites de los muros interiores
|
|
this.wall_bounds = {
|
|
min_x: model.x + pad,
|
|
max_x: model.x + model.width - pad,
|
|
min_z: model.z + pad,
|
|
max_z: model.z + model.depth - pad
|
|
};
|
|
|
|
const py = Math.round(model.y / model.height);
|
|
|
|
// Huecos activos que perforan suelo y techo
|
|
this.floor_holes = holes.filter(h => py > h.yf && py < h.yt);
|
|
this.ceiling_holes = holes.filter(h => py >= h.yf && py < (h.yt - 1));
|
|
|
|
this.get_floor_height = () => self.room_bounds.min_y;
|
|
|
|
this.contains_xz = (x, z) => {
|
|
return (
|
|
x >= self.room_bounds.min_x && x <= self.room_bounds.max_x &&
|
|
z >= self.room_bounds.min_z && z <= self.room_bounds.max_z
|
|
);
|
|
};
|
|
|
|
// Sin solapamiento vertical entre plantas: solo reconoce la sala correspondiente
|
|
this.contains_3d = (x, y, z) => {
|
|
if(!self.contains_xz(x, z)) return false;
|
|
return (y >= (self.room_bounds.min_y - 0.2) && y < (self.room_bounds.max_y - 0.5));
|
|
};
|
|
|
|
this.contains = (a, b, c) => {
|
|
if(c !== undefined){
|
|
return self.contains_3d(a, b, c);
|
|
}
|
|
return self.contains_xz(a, b);
|
|
};
|
|
|
|
this.resolve_player = (position, controls, player_radius = 0.22) => {
|
|
|
|
const margin = 0.5;
|
|
const is_near = (
|
|
position.x >= (self.room_bounds.min_x - margin) && position.x <= (self.room_bounds.max_x + margin) &&
|
|
position.z >= (self.room_bounds.min_z - margin) && position.z <= (self.room_bounds.max_z + margin)
|
|
);
|
|
|
|
if(!is_near) return;
|
|
|
|
const eye_height = controls.current_height || 1.75;
|
|
const floor_y = self.room_bounds.min_y;
|
|
const ceiling_y = self.room_bounds.max_y;
|
|
const feet_y = position.y - eye_height;
|
|
|
|
// Filtro estricto: si el jugador no pertenece a esta altura, no resolver nada
|
|
if(feet_y < floor_y - 0.25 || feet_y > ceiling_y - 0.4){
|
|
return;
|
|
}
|
|
|
|
// 1. SUELO Y TECHO
|
|
const in_floor_area = (
|
|
position.x >= (self.room_bounds.min_x - 0.15) && position.x <= (self.room_bounds.max_x + 0.15) &&
|
|
position.z >= (self.room_bounds.min_z - 0.15) && position.z <= (self.room_bounds.max_z + 0.15)
|
|
);
|
|
|
|
if(in_floor_area){
|
|
|
|
let is_over_hole = false;
|
|
for(const h of self.floor_holes){
|
|
const min_x = Math.min(h.xf, h.xt);
|
|
const max_x = Math.max(h.xf, h.xt);
|
|
const min_z = Math.min(h.zf, h.zt);
|
|
const max_z = Math.max(h.zf, h.zt);
|
|
|
|
if(position.x > min_x && position.x < max_x &&
|
|
position.z > min_z && position.z < max_z){
|
|
is_over_hole = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Apoyo de suelo: solo si cae o está quieto (permite saltar con velocity_y > 0)
|
|
// Apoyo de suelo: EXCLUSIVAMENTE si el jugador está a menos de 25cm por encima o debajo del suelo de ESTA sala
|
|
if(!is_over_hole){
|
|
const target_eye_y = floor_y + eye_height;
|
|
const dist_to_floor = position.y - target_eye_y;
|
|
|
|
// Solo apoya si está cayendo/parado Y a una distancia inmediata del suelo (-0.25m a +0.10m)
|
|
if(controls.velocity_y <= 0 && dist_to_floor >= -0.25 && dist_to_floor <= 0.10){
|
|
position.y = target_eye_y;
|
|
controls.velocity_y = 0;
|
|
controls.is_grounded = true;
|
|
} else if(dist_to_floor > 0.10){
|
|
controls.is_grounded = false;
|
|
}
|
|
}
|
|
|
|
// Techo: solo frena si el jugador va subiendo
|
|
let is_under_hole = false;
|
|
for(const h of self.ceiling_holes){
|
|
const min_x = Math.min(h.xf, h.xt);
|
|
const max_x = Math.max(h.xf, h.xt);
|
|
const min_z = Math.min(h.zf, h.zt);
|
|
const max_z = Math.max(h.zf, h.zt);
|
|
|
|
if(position.x > min_x && position.x < max_x &&
|
|
position.z > min_z && position.z < max_z){
|
|
is_under_hole = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!is_under_hole && controls.velocity_y > 0 && position.y >= ceiling_y){
|
|
position.y = ceiling_y;
|
|
controls.velocity_y = 0;
|
|
}
|
|
}
|
|
|
|
// 2. PAREDES Y PUERTAS
|
|
const min_allowed_x = self.wall_bounds.min_x + player_radius;
|
|
const max_allowed_x = self.wall_bounds.max_x - player_radius;
|
|
const min_allowed_z = self.wall_bounds.min_z + player_radius;
|
|
const max_allowed_z = self.wall_bounds.max_z - player_radius;
|
|
|
|
let in_door_north = false, in_door_south = false, in_door_west = false, in_door_east = false;
|
|
|
|
for(const f of self.frames){
|
|
if(f.axis === "horizontal"){
|
|
if(position.x >= (f.x - 0.05) && position.x <= (f.x + f.width + 0.05)){
|
|
if(Math.abs(f.z - model.z) < Math.abs(f.z - (model.z + model.depth))){
|
|
in_door_north = true;
|
|
} else {
|
|
in_door_south = true;
|
|
}
|
|
}
|
|
} else {
|
|
if(position.z >= (f.z - 0.05) && position.z <= (f.z + f.width + 0.05)){
|
|
if(Math.abs(f.x - model.x) < Math.abs(f.x - (model.x + model.width))){
|
|
in_door_west = true;
|
|
} else {
|
|
in_door_east = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!in_door_west && position.x < min_allowed_x && position.x >= self.room_bounds.min_x - 0.1){
|
|
position.x = min_allowed_x;
|
|
}
|
|
if(!in_door_east && position.x > max_allowed_x && position.x <= self.room_bounds.max_x + 0.1){
|
|
position.x = max_allowed_x;
|
|
}
|
|
|
|
if(!in_door_north && position.z < min_allowed_z && position.z >= self.room_bounds.min_z - 0.1){
|
|
position.z = min_allowed_z;
|
|
}
|
|
if(!in_door_south && position.z > max_allowed_z && position.z <= self.room_bounds.max_z + 0.1){
|
|
position.z = max_allowed_z;
|
|
}
|
|
|
|
// 3. BARANDILLAS (bloquean el borde del hueco salvo si se salta >= 1.0m o por descansillo)
|
|
if(self.railings){
|
|
for(let i = 0; i < self.railings.length; i++){
|
|
self.railings[i].resolve_player(position, controls, player_radius);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
return RoomCollider;
|
|
})(); |