HollowBond/Public/ecma/Colliders/StairCollider.ecma.js

100 lines
3.8 KiB
JavaScript

"use strict";
const StairCollider = (function(){
const StairCollider = function(hollow_bond, model){
const self = this;
this.model = model;
const bounds = model.get_world_bounds();
const is_x = (model.wall === "north" || model.wall === "south");
const length = is_x ? model.width : model.depth;
const landing_size = hollow_bond.stairs_landing_size || 1.0;
const flight_len = Math.max(1.0, length - (landing_size * 2));
const rise = hollow_bond.room_height;
const dir = model.direction;
this.bounds = {
min_x: bounds.x,
max_x: bounds.x + bounds.width,
min_y: bounds.y,
max_y: bounds.y + rise,
min_z: bounds.z,
max_z: bounds.z + bounds.depth
};
this.resolve_player = (position, controls, player_radius = 0.22) => {
const eye_height = controls.current_height || 1.75;
const px = position.x;
const py = position.y - eye_height;
const pz = position.z;
// Filtro general AABB
if(
px < self.bounds.min_x - player_radius || px > self.bounds.max_x + player_radius ||
pz < self.bounds.min_z - player_radius || pz > self.bounds.max_z + player_radius ||
py < self.bounds.min_y - 0.4 || py > self.bounds.max_y + 1.2
){
return;
}
// Progresión longitudinal a lo largo de la escalera (0 a 1)
const coord = is_x ? (px - self.bounds.min_x) : (pz - self.bounds.min_z);
let expected_y = self.bounds.min_y;
if(dir === 1){
if(coord <= landing_size){
expected_y = self.bounds.min_y;
} else if(coord >= (length - landing_size)){
expected_y = self.bounds.max_y;
} else {
const t = (coord - landing_size) / flight_len;
expected_y = self.bounds.min_y + t * rise;
}
} else {
if(coord <= landing_size){
expected_y = self.bounds.max_y;
} else if(coord >= (length - landing_size)){
expected_y = self.bounds.min_y;
} else {
const t = (length - landing_size - coord) / flight_len;
expected_y = self.bounds.min_y + t * rise;
}
}
// 1. PISADA (Subida suave de rampa)
const dist_to_stair = py - expected_y;
if(controls.velocity_y <= 0 && dist_to_stair >= -0.25 && dist_to_stair <= 0.25){
position.y = expected_y + eye_height;
controls.velocity_y = 0;
controls.is_grounded = true;
}
// 2. COLISIÓN CONTRA BARANDILLA INTERIOR
// Si el jugador no salta por encima de 1.0m, se le bloquea la salida lateral hacia el hueco
if(py < (expected_y + 1.0)){
const r = player_radius;
switch(model.wall){
case "north": // Hueco hacia +Z
if(position.z > self.bounds.max_z - r) position.z = self.bounds.max_z - r;
break;
case "south": // Hueco hacia -Z
if(position.z < self.bounds.min_z + r) position.z = self.bounds.min_z + r;
break;
case "west": // Hueco hacia +X
if(position.x > self.bounds.max_x - r) position.x = self.bounds.max_x - r;
break;
case "east": // Hueco hacia -X
if(position.x < self.bounds.min_x + r) position.x = self.bounds.min_x + r;
break;
}
}
};
};
return StairCollider;
})();