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