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