HollowBond/Public/ecma/Controls/GameControls.ecma.js

160 lines
6.5 KiB
JavaScript

"use strict";
const GameControls = (function(){
const GameControls = function(hollow_bond){
const self = this,
euler = new THREE.Euler(0, 0, 0, "YXZ"),
keys = {
forward: false,
backward: false,
left: false,
right: false,
sprint: false,
crouch: false,
jump: false
};
let is_pointer_locked = false;
this.standing_height = 1.75;
this.player_width = this.standing_height / 4;
this.player_radius = this.player_width / 2;
this.crouch_height = this.standing_height / 2;
this.current_height = this.standing_height;
this.walk_speed = 3.5;
this.sprint_speed = 6.2;
this.crouch_speed = 1.8;
this.velocity_y = 0;
this.is_grounded = false;
this.is_crouching = false;
this.set_dimensions = height => {
self.standing_height = height || 1.75;
self.player_width = self.standing_height / 4;
self.player_radius = self.player_width / 2;
self.crouch_height = self.standing_height / 2;
self.current_height = self.standing_height;
};
this.setup = dungeon => {
const canvas = dungeon.renderer.domElement;
canvas.addEventListener("click", () => canvas.requestPointerLock());
document.addEventListener("pointerlockchange", () => {
is_pointer_locked = (document.pointerLockElement === canvas);
});
document.addEventListener("mousemove", evt => {
if(!is_pointer_locked) return;
euler.setFromQuaternion(dungeon.camera.quaternion);
euler.y -= evt.movementX * 0.002;
euler.x -= evt.movementY * 0.002;
euler.x = Math.max(-Math.PI / 2 + 0.01, Math.min(Math.PI / 2 - 0.01, euler.x));
dungeon.camera.quaternion.setFromEuler(euler);
});
window.addEventListener("keydown", evt => {
switch(evt.code){
case "KeyW": case "ArrowUp": keys.forward = true; break;
case "KeyS": case "ArrowDown": keys.backward = true; break;
case "KeyA": case "ArrowLeft": keys.left = true; break;
case "KeyD": case "ArrowRight": keys.right = true; break;
case "ShiftLeft": case "ShiftRight": keys.sprint = true; break;
case "KeyC": case "ControlLeft": keys.crouch = true; break;
case "Space": keys.jump = true; break;
}
});
window.addEventListener("keyup", evt => {
switch(evt.code){
case "KeyW": case "ArrowUp": keys.forward = false; break;
case "KeyS": case "ArrowDown": keys.backward = false; break;
case "KeyA": case "ArrowLeft": keys.left = false; break;
case "KeyD": case "ArrowRight": keys.right = false; break;
case "ShiftLeft": case "ShiftRight": keys.sprint = false; break;
case "KeyC": case "ControlLeft": keys.crouch = false; break;
case "Space": keys.jump = false; break;
}
});
};
this.update = (dungeon, delta) => {
if(!is_pointer_locked) return;
const gravity = hollow_bond.gravity || 9.81;
// 1. Estado de agachado
self.is_crouching = keys.crouch;
const target_height = self.is_crouching ? self.crouch_height : self.standing_height;
self.current_height += (target_height - self.current_height) * Math.min(1, 14 * delta);
// 2. Velocidad de traslación
let current_speed = self.walk_speed;
if(self.is_crouching){
current_speed = self.crouch_speed;
} else if(keys.sprint){
current_speed = self.sprint_speed;
}
// 3. Movimiento horizontal en XZ
const input_dir = new THREE.Vector3();
if(keys.forward) input_dir.z -= 1;
if(keys.backward) input_dir.z += 1;
if(keys.left) input_dir.x -= 1;
if(keys.right) input_dir.x += 1;
if(input_dir.lengthSq() > 0){
input_dir.normalize();
const move_dir = new THREE.Vector3(input_dir.x, 0, input_dir.z);
move_dir.applyAxisAngle(new THREE.Vector3(0, 1, 0), euler.y);
dungeon.camera.position.x += move_dir.x * current_speed * delta;
dungeon.camera.position.z += move_dir.z * current_speed * delta;
}
// 4. Salto
if(self.is_grounded && keys.jump){
const jump_height = self.standing_height / 2;
self.velocity_y = Math.sqrt(2 * gravity * jump_height);
self.is_grounded = false;
}
// 5. Aplicar gravedad en el aire
self.velocity_y -= gravity * delta;
dungeon.camera.position.y += self.velocity_y * delta;
// Reiniciamos apoyo; sólo un suelo bajo los pies lo activará
self.is_grounded = false;
// 6. Comprobación de apoyo en la sala actual
if(dungeon.current_room && dungeon.current_room.collider){
dungeon.current_room.collider.resolve_player(dungeon.camera.position, self, self.player_radius);
}
// 7. Comprobación de marcos (solo de la sala actual y a la misma altura)
if(dungeon.current_room && dungeon.current_room.model && dungeon.current_room.model.frames){
const feet_y = dungeon.camera.position.y - self.current_height;
for(const fid of dungeon.current_room.model.frames){
const f_obj = dungeon.objects.frame ? dungeon.objects.frame[fid] : null;
if(!f_obj || !f_obj.collider) continue;
// Comprobamos XZ y verificamos que el marco esté en este mismo piso (+- 1 metro)
const frame_y = f_obj.model ? f_obj.model.y : 0;
if(Math.abs(feet_y - frame_y) < 1.0){
if(f_obj.collider.contains(dungeon.camera.position.x, dungeon.camera.position.z)){
f_obj.collider.resolve_player(dungeon.camera.position, self, self.player_radius);
}
}
}
}
};
};
return GameControls;
})();