HollowBond/Public/ecma/Colliders/ElevatorCollider.ecma.js

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;
})();