145 lines
5.4 KiB
JavaScript
145 lines
5.4 KiB
JavaScript
"use strict";
|
|
|
|
const ElevatorCollider = (function(){
|
|
|
|
const ElevatorCollider = function(hollow_bond, model, object_ref){
|
|
|
|
const self = this;
|
|
this.model = model;
|
|
this.object = object_ref;
|
|
|
|
const base_y = model.y * hollow_bond.room_height;
|
|
const h = model.height || hollow_bond.room_height;
|
|
const w = model.width || hollow_bond.elevator_size || 2.0;
|
|
const d = model.depth || hollow_bond.elevator_size || 2.0;
|
|
const th = 0.08; // Grosor de paredes
|
|
|
|
this.bounds = {
|
|
min_x: model.x,
|
|
max_x: model.x + w,
|
|
min_z: model.z,
|
|
max_z: model.z + d,
|
|
min_y: base_y,
|
|
max_y: base_y + h
|
|
};
|
|
|
|
const hw = w * 0.5;
|
|
const hd = d * 0.5;
|
|
const door_w = hollow_bond.elevator_door_width || 1.1;
|
|
const h_door_w = door_w * 0.5;
|
|
|
|
// Comprueba si un punto XZ está dentro de la base de la cabina
|
|
this.is_inside = (px, pz) => {
|
|
return (
|
|
px >= self.bounds.min_x && px <= self.bounds.max_x &&
|
|
pz >= self.bounds.min_z && pz <= self.bounds.max_z
|
|
);
|
|
};
|
|
|
|
this.resolve_player = (position, controls, player_radius = 0.22) => {
|
|
const eye_height = controls.current_height || 1.75;
|
|
const feet_y = position.y - eye_height;
|
|
|
|
// Filtro vertical de planta
|
|
if(feet_y < self.bounds.min_y - 0.25 || feet_y > self.bounds.max_y + 0.25){
|
|
return;
|
|
}
|
|
|
|
const r = player_radius;
|
|
const px = position.x;
|
|
const pz = position.z;
|
|
|
|
// 1. Transformar a coordenadas locales de la cabina (centro = 0,0)
|
|
const cx = (self.bounds.min_x + self.bounds.max_x) * 0.5;
|
|
const cz = (self.bounds.min_z + self.bounds.max_z) * 0.5;
|
|
const dx = px - cx;
|
|
const dz = pz - cz;
|
|
|
|
// Rotación local inversa según orientación de la puerta (puerta siempre en +Z local)
|
|
let lx = 0, lz = 0;
|
|
switch(model.facing){
|
|
case "south": lx = dx; lz = dz; break;
|
|
case "north": lx = -dx; lz = -dz; break;
|
|
case "east": lx = -dz; lz = dx; break;
|
|
case "west": lx = dz; lz = -dx; break;
|
|
}
|
|
|
|
// 2. Estado de apertura dinámica de las puertas
|
|
const openness = self.object ? self.object.door_openness : 0.0;
|
|
// Apertura libre entre las dos hojas correderas
|
|
const free_half_gap = openness * h_door_w;
|
|
|
|
const is_in = (
|
|
lx > (-hw + th) && lx < (hw - th) &&
|
|
lz > (-hd + th) && lz < (hd - th)
|
|
);
|
|
|
|
// 3. SUELO Y TECHO (si está en la cabina o pisando el umbral)
|
|
if(is_in || (Math.abs(lx) <= h_door_w && Math.abs(lz - hd) <= 0.35)){
|
|
const target_eye_y = self.bounds.min_y + eye_height;
|
|
const dist_to_floor = position.y - target_eye_y;
|
|
|
|
if(controls.velocity_y <= 0 && dist_to_floor >= -0.35 && dist_to_floor <= 0.25){
|
|
position.y = target_eye_y;
|
|
controls.velocity_y = 0;
|
|
controls.is_grounded = true;
|
|
}
|
|
|
|
if(is_in && controls.velocity_y > 0 && position.y >= (self.bounds.max_y - 0.1)){
|
|
position.y = self.bounds.max_y - 0.1;
|
|
controls.velocity_y = 0;
|
|
}
|
|
}
|
|
|
|
// 4. COLISIONES EN PLANO HORIZONTAL (X / Z)
|
|
if(is_in){
|
|
// --- DENTRO DE LA CABINA ---
|
|
// Pared trasera (-Z)
|
|
if(lz < (-hd + th + r)) lz = -hd + th + r;
|
|
// Pared izquierda (-X)
|
|
if(lx < (-hw + th + r)) lx = -hw + th + r;
|
|
// Pared derecha (+X)
|
|
if(lx > (hw - th - r)) lx = hw - th - r;
|
|
|
|
// Pared frontal (+Z): se permite cruzar únicamente si el jugador cabe en el hueco libre
|
|
const can_pass_front = (
|
|
(lx - r) >= -free_half_gap &&
|
|
(lx + r) <= free_half_gap
|
|
);
|
|
|
|
if(!can_pass_front && lz > (hd - th - r)){
|
|
lz = hd - th - r;
|
|
}
|
|
} else {
|
|
// --- FUERA DE LA CABINA (aproximándose desde la sala) ---
|
|
// Si está cerca del vano frontal
|
|
if(Math.abs(lx) < (hw + 0.2) && lz > hd && lz < (hd + 0.5)){
|
|
const can_enter = (
|
|
(lx - r) >= -free_half_gap &&
|
|
(lx + r) <= free_half_gap
|
|
);
|
|
|
|
// Si choca contra las hojas de la puerta o los laterales fijos del marco
|
|
if(!can_enter && lz < (hd + r)){
|
|
lz = hd + r;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. Devolver coordenadas corregidas a espacio de mundo
|
|
let w_dx = 0, w_dz = 0;
|
|
switch(model.facing){
|
|
case "south": w_dx = lx; w_dz = lz; break;
|
|
case "north": w_dx = -lx; w_dz = -lz; break;
|
|
case "east": w_dx = lz; w_dz = -lx; break;
|
|
case "west": w_dx = -lz; w_dz = lx; break;
|
|
}
|
|
|
|
position.x = cx + w_dx;
|
|
position.z = cz + w_dz;
|
|
};
|
|
|
|
};
|
|
|
|
return ElevatorCollider;
|
|
})(); |