1
0
Fork 0
forked from Simnation/Main
Main/resources/[qb]/qb-minigames/html/js/lockpick.js
2025-06-07 08:51:21 +02:00

253 lines
6.9 KiB
JavaScript

const lockpickContainer = document.querySelector("#lockpick-container"),
pin = document.querySelector("#pin"),
cyl = document.querySelector("#cylinder"),
driver = document.querySelector("#driver");
const minRot = -90,
maxRot = 90,
solvePadding = 4,
maxDistFromSolve = 45,
mouseSmoothing = 2,
keyRepeatRate = 25,
cylRotSpeed = 3,
pinDamage = 20,
pinDamageInterval = 150;
let solveDeg = Math.random() * (maxRot - minRot + 1) + minRot,
pinRot = 0,
cylRot = 0,
lastMousePos = 0,
pinHealth = 100,
numPins = 1,
userPushingCyl = false,
gameOver = false,
gamePaused = false,
cylRotationInterval,
pinLastDamaged;
const clamp = (val, min, max) => Math.min(Math.max(val, min), max);
const convertRanges = (value, oldMin, oldMax, newMin, newMax) => ((value - oldMin) * (newMax - newMin)) / (oldMax - oldMin) + newMin;
const closeLockpick = () => {
solveDeg = Math.random() * (maxRot - minRot + 1) + minRot;
pinRot = 0;
cylRot = 0;
lastMousePos = 0;
pinHealth = 100;
numPins = 1;
userPushingCyl = false;
gameOver = true;
gamePaused = true;
lockpickContainer.style.display = "none";
if (cylRotationInterval) {
cancelAnimationFrame(cylRotationInterval);
cylRotationInterval = null;
}
};
const unlock = () => {
closeLockpick();
fetch(`https://${GetParentResourceName()}/lockpickFinish`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ success: true }),
});
};
const pushCyl = () => {
cancelAnimationFrame(cylRotationInterval);
userPushingCyl = true;
let distFromSolve = Math.abs(pinRot - solveDeg) - solvePadding;
distFromSolve = clamp(distFromSolve, 0, maxDistFromSolve);
let cylRotationAllowance = convertRanges(distFromSolve, 0, maxDistFromSolve, maxRot, maxRot * 0.02);
function updateCylinderRotation() {
if (!userPushingCyl || cylRot >= maxRot || gameOver) {
cancelAnimationFrame(cylRotationInterval);
if (cylRot >= maxRot) {
unlock();
}
return;
}
cylRot += cylRotSpeed;
cylRot = Math.min(cylRot, cylRotationAllowance);
if (cylRot >= cylRotationAllowance) {
damagePin();
}
cyl.style.transform = `translate(-50%, -50%) rotateZ(${cylRot}deg)`;
driver.style.transform = `rotateZ(${cylRot}deg)`;
cylRotationInterval = requestAnimationFrame(updateCylinderRotation);
}
cylRotationInterval = requestAnimationFrame(updateCylinderRotation);
};
const unpushCyl = () => {
userPushingCyl = false;
cancelAnimationFrame(cylRotationInterval);
function updateCylinderRotationBackward() {
if (cylRot <= 0 || gameOver) {
cylRot = 0;
cancelAnimationFrame(cylRotationInterval);
return;
}
cylRot -= cylRotSpeed;
cylRot = Math.max(cylRot, 0);
cyl.style.transform = `translate(-50%, -50%) rotateZ(${cylRot}deg)`;
driver.style.transform = `rotateZ(${cylRot}deg)`;
cylRotationInterval = requestAnimationFrame(updateCylinderRotationBackward);
}
cylRotationInterval = requestAnimationFrame(updateCylinderRotationBackward);
};
const damagePin = () => {
if (!pinLastDamaged || Date.now() - pinLastDamaged > pinDamageInterval) {
pinHealth -= pinDamage;
pinLastDamaged = Date.now();
const keyframes = [{ transform: `rotateZ(${pinRot}deg)` }, { transform: `rotateZ(${pinRot - 2}deg)` }, { transform: `rotateZ(${pinRot}deg)` }];
const options = {
duration: pinDamageInterval / 2,
easing: "ease-out",
};
pin.animate(keyframes, options);
if (pinHealth <= 0) {
breakPin();
}
}
};
const reset = () => {
cylRot = 0;
pinHealth = 100;
pinRot = 0;
pin.style.transform = `rotateZ(${pinRot}deg)`;
cyl.style.transform = `translate(-50%, -50%) rotateZ(${cylRot}deg)`;
driver.style.transform = `rotateZ(${cylRot}deg)`;
const pinTop = pin.querySelector(".top");
const pinBott = pin.querySelector(".bott");
[pinTop, pinBott].forEach((el) => {
if (el) {
el.style.transform = "rotateZ(0deg) translateX(0) translateY(0)";
el.style.opacity = "1";
}
});
if (cylRotationInterval) {
cancelAnimationFrame(cylRotationInterval);
cylRotationInterval = null;
}
};
const outOfPins = () => {
closeLockpick();
fetch(`https://${GetParentResourceName()}/lockpickFinish`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ success: false }),
});
};
const breakPin = () => {
gamePaused = true;
cancelAnimationFrame(cylRotationInterval);
numPins--;
const pinTop = pin.querySelector(".top");
const pinBott = pin.querySelector(".bott");
const animateOptions = {
duration: 700,
};
pinTop.animate(
[
{ transform: "rotateZ(0deg) translateX(0) translateY(0)", opacity: 1 },
{ transform: "rotateZ(-400deg) translateX(-200px) translateY(-100px)", opacity: 0 },
],
animateOptions
);
const bottomAnimation = pinBott.animate(
[
{ transform: "rotateZ(0deg) translateX(0) translateY(0)", opacity: 1 },
{ transform: "rotateZ(400deg) translateX(200px) translateY(100px)", opacity: 0 },
],
animateOptions
);
bottomAnimation.onfinish = () => {
if (numPins > 0) {
gamePaused = false;
reset();
} else {
outOfPins();
}
};
};
document.addEventListener("mousemove", (e) => {
if (!gameOver && !gamePaused) {
let pinRotChange = (e.clientX - lastMousePos) / mouseSmoothing;
pinRot += pinRotChange;
pinRot = clamp(pinRot, minRot, maxRot);
pin.style.transform = `rotateZ(${pinRot}deg)`;
}
lastMousePos = e.clientX;
});
document.addEventListener("mouseleave", () => (lastMousePos = 0));
const keyActionMap = {
w: pushCyl,
a: pushCyl,
s: pushCyl,
d: pushCyl,
escape: () => {
closeLockpick();
fetch(`https://${GetParentResourceName()}/lockpickExit`, { method: "POST" }).catch(console.error);
},
};
document.addEventListener("keydown", (e) => {
let action = keyActionMap[e.key.toLowerCase()];
if (action && !userPushingCyl && !gameOver && !gamePaused) {
action();
}
});
document.addEventListener("keyup", (e) => {
let action = keyActionMap[e.key.toLowerCase()];
if (action && !gameOver) {
unpushCyl();
}
});
window.addEventListener("message", (event) => {
const eventData = event.data;
if (eventData.action === "startLockpick") {
lockpickContainer.style.display = "block";
numPins = eventData.pins;
gameOver = false;
gamePaused = false;
reset();
}
});