diff --git a/dist/modules/cheats.js b/dist/modules/cheats.js
index 86a4e25..8721b40 100644
--- a/dist/modules/cheats.js
+++ b/dist/modules/cheats.js
@@ -1,28 +1,144 @@
(function() {
+ // Touch device detection
+ const isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
+ const isTouch = isMobile && window.matchMedia('(pointer: coarse)').matches;
+
const style = document.createElement('style');
style.textContent = `
#cheat-engine-ui {
position: fixed;
top: 0;
right: 0;
- width: 340px;
+ width: ${isTouch ? '100vw' : '340px'};
height: 100vh;
- background: rgba(10, 10, 10, 0.9);
+ background: rgba(10, 10, 10, 0.95);
backdrop-filter: blur(15px);
color: #eee;
font-family: 'Consolas', 'Monaco', monospace;
- padding: 20px 20px 100px 20px; /* Extra bottom padding for scroll */
+ padding: ${isTouch ? '15px 15px 150px 15px' : '20px 20px 100px 20px'};
z-index: 10000;
display: none;
flex-direction: column;
- gap: 15px;
+ gap: ${isTouch ? '12px' : '15px'};
overflow-y: auto;
user-select: none;
- border-left: 2px solid #ff00ff;
+ border-left: ${isTouch ? 'none' : '2px solid #ff00ff'};
box-shadow: -10px 0 30px rgba(0,0,0,0.8);
box-sizing: border-box;
}
+
+ /* Touch toggle button */
+ #cheat-toggle-btn {
+ display: ${isTouch ? 'flex' : 'none'};
+ position: fixed;
+ top: 10px;
+ right: 10px;
+ width: 50px;
+ height: 50px;
+ background: rgba(255, 0, 255, 0.3);
+ border: 2px solid #ff00ff;
+ border-radius: 50%;
+ color: #ff00ff;
+ font-size: 20px;
+ font-weight: bold;
+ z-index: 9999;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ backdrop-filter: blur(5px);
+ touch-action: manipulation;
+ }
+ #cheat-toggle-btn:active {
+ background: rgba(255, 0, 255, 0.6);
+ transform: scale(0.95);
+ }
+ #cheat-toggle-btn.active {
+ background: rgba(255, 0, 255, 0.6);
+ }
+
+ /* Airbreak touch controls */
+ #airbreak-touch-controls {
+ display: none;
+ position: fixed;
+ bottom: 20px;
+ left: 20px;
+ z-index: 10001;
+ touch-action: none;
+ }
+ #airbreak-touch-controls.active {
+ display: block;
+ }
+ .airbreak-joystick {
+ width: 120px;
+ height: 120px;
+ background: rgba(0, 255, 255, 0.2);
+ border: 2px solid #0ff;
+ border-radius: 50%;
+ position: relative;
+ touch-action: none;
+ }
+ .airbreak-joystick-knob {
+ width: 50px;
+ height: 50px;
+ background: rgba(0, 255, 255, 0.6);
+ border: 2px solid #0ff;
+ border-radius: 50%;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ pointer-events: none;
+ }
+ .airbreak-vertical-btns {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ z-index: 10001;
+ }
+ .airbreak-v-btn {
+ width: 60px;
+ height: 60px;
+ background: rgba(0, 255, 255, 0.2);
+ border: 2px solid #0ff;
+ border-radius: 10px;
+ color: #0ff;
+ font-size: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ touch-action: manipulation;
+ }
+ .airbreak-v-btn:active {
+ background: rgba(0, 255, 255, 0.5);
+ }
+ #airbreak-toggle-fly {
+ position: fixed;
+ bottom: 160px;
+ right: 20px;
+ width: 80px;
+ height: 40px;
+ background: rgba(255, 255, 0, 0.2);
+ border: 2px solid #ff0;
+ border-radius: 10px;
+ color: #ff0;
+ font-size: 12px;
+ font-weight: bold;
+ z-index: 10001;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ touch-action: manipulation;
+ }
+ #airbreak-toggle-fly.active {
+ background: rgba(255, 255, 0, 0.5);
+ }
+ #airbreak-toggle-fly.visible {
+ display: flex;
+ }
#cheat-engine-ui h1 {
font-size: 18px;
text-align: left;
@@ -58,15 +174,17 @@
cursor: pointer;
transition: all 0.1s;
text-transform: uppercase;
- font-size: 10px;
+ font-size: ${isTouch ? '12px' : '10px'};
+ min-height: ${isTouch ? '44px' : 'auto'};
+ touch-action: manipulation;
}
- #cheat-engine-ui button:hover {
+ #cheat-engine-ui button:hover, #cheat-engine-ui button:active {
background: #ff00ff;
color: #000;
border-color: #ff00ff;
}
#cheat-engine-ui .results {
- max-height: 120px;
+ max-height: ${isTouch ? '150px' : '120px'};
overflow-y: auto;
background: rgba(0,0,0,0.3);
border: 1px solid #222;
@@ -74,24 +192,47 @@
.cheat-grid {
display: grid;
grid-template-columns: 1fr 1fr;
- gap: 4px;
+ gap: ${isTouch ? '8px' : '4px'};
}
.cheat-cat {
- font-size: 11px;
+ font-size: ${isTouch ? '13px' : '11px'};
color: #0ff;
- margin-top: 10px;
+ margin-top: ${isTouch ? '15px' : '10px'};
text-transform: uppercase;
}
.cheat-btn {
background: transparent !important;
border: 1px solid #444 !important;
text-align: left;
+ padding: ${isTouch ? '12px 10px' : '6px 10px'};
}
- .cheat-btn:hover {
+ .cheat-btn:hover, .cheat-btn:active {
border-color: #0ff !important;
color: #0ff !important;
background: rgba(0, 255, 255, 0.1) !important;
}
+
+ /* Close button for touch */
+ #cheat-close-btn {
+ display: ${isTouch ? 'flex' : 'none'};
+ position: absolute;
+ top: 15px;
+ right: 15px;
+ width: 40px;
+ height: 40px;
+ background: rgba(255, 0, 0, 0.3);
+ border: 2px solid #f00;
+ border-radius: 50%;
+ color: #f00;
+ font-size: 20px;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ touch-action: manipulation;
+ }
+ #cheat-close-btn:active {
+ background: rgba(255, 0, 0, 0.6);
+ }
::-webkit-scrollbar {
width: 4px;
}
@@ -104,9 +245,44 @@
`;
document.head.appendChild(style);
+ // Create touch toggle button
+ const toggleBtn = document.createElement('div');
+ toggleBtn.id = 'cheat-toggle-btn';
+ toggleBtn.innerHTML = '⚙';
+ toggleBtn.title = 'Open Cheat Menu';
+ document.body.appendChild(toggleBtn);
+
+ // Create airbreak touch controls
+ const airbreakTouchControls = document.createElement('div');
+ airbreakTouchControls.id = 'airbreak-touch-controls';
+ airbreakTouchControls.innerHTML = `
+
+ `;
+ document.body.appendChild(airbreakTouchControls);
+
+ // Create vertical buttons for airbreak (up/down)
+ const airbreakVerticalBtns = document.createElement('div');
+ airbreakVerticalBtns.className = 'airbreak-vertical-btns';
+ airbreakVerticalBtns.id = 'airbreak-vertical-btns';
+ airbreakVerticalBtns.innerHTML = `
+ ↑
+ ↓
+ `;
+ airbreakVerticalBtns.style.display = 'none';
+ document.body.appendChild(airbreakVerticalBtns);
+
+ // Create fly toggle button for touch
+ const flyToggleBtn = document.createElement('div');
+ flyToggleBtn.id = 'airbreak-toggle-fly';
+ flyToggleBtn.innerHTML = 'FLY';
+ document.body.appendChild(flyToggleBtn);
+
const ui = document.createElement('div');
ui.id = 'cheat-engine-ui';
ui.innerHTML = `
+ ✕
Cheat Engine
@@ -337,6 +513,7 @@
function toggleMenu() {
menuOpen = !menuOpen;
ui.style.display = menuOpen ? 'flex' : 'none';
+ toggleBtn.classList.toggle('active', menuOpen);
if (menuOpen) {
if (document.pointerLockElement) document.exitPointerLock();
@@ -348,6 +525,7 @@
}
}
+ // Keyboard toggle (F3)
window.addEventListener('keydown', (e) => {
if (e.key === 'F3') {
e.preventDefault();
@@ -356,6 +534,116 @@
}
}, true);
+ // Touch toggle button handler with drag support
+ let toggleBtnDragging = false;
+ let toggleBtnStartX = 0;
+ let toggleBtnStartY = 0;
+ let toggleBtnInitialLeft = 0;
+ let toggleBtnInitialTop = 0;
+ const dragThreshold = 10; // pixels to move before considered a drag
+
+ // Load saved position from localStorage
+ const savedPos = localStorage.getItem('cheat-toggle-pos');
+ if (savedPos) {
+ try {
+ const pos = JSON.parse(savedPos);
+ toggleBtn.style.right = 'auto';
+ toggleBtn.style.left = pos.left + 'px';
+ toggleBtn.style.top = pos.top + 'px';
+ } catch(e) {}
+ }
+
+ toggleBtn.addEventListener('click', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (!toggleBtnDragging) {
+ toggleMenu();
+ }
+ });
+
+ if (isTouch) {
+ toggleBtn.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ toggleBtnDragging = false;
+
+ const touch = e.touches[0];
+ toggleBtnStartX = touch.clientX;
+ toggleBtnStartY = touch.clientY;
+
+ // Get current position
+ const rect = toggleBtn.getBoundingClientRect();
+ toggleBtnInitialLeft = rect.left;
+ toggleBtnInitialTop = rect.top;
+ }, { passive: false });
+
+ toggleBtn.addEventListener('touchmove', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const touch = e.touches[0];
+ const deltaX = touch.clientX - toggleBtnStartX;
+ const deltaY = touch.clientY - toggleBtnStartY;
+
+ // Check if we've moved enough to consider it a drag
+ if (Math.abs(deltaX) > dragThreshold || Math.abs(deltaY) > dragThreshold) {
+ toggleBtnDragging = true;
+ }
+
+ if (toggleBtnDragging) {
+ let newLeft = toggleBtnInitialLeft + deltaX;
+ let newTop = toggleBtnInitialTop + deltaY;
+
+ // Clamp to screen bounds
+ const btnWidth = toggleBtn.offsetWidth;
+ const btnHeight = toggleBtn.offsetHeight;
+ newLeft = Math.max(0, Math.min(window.innerWidth - btnWidth, newLeft));
+ newTop = Math.max(0, Math.min(window.innerHeight - btnHeight, newTop));
+
+ toggleBtn.style.right = 'auto';
+ toggleBtn.style.left = newLeft + 'px';
+ toggleBtn.style.top = newTop + 'px';
+ }
+ }, { passive: false });
+
+ toggleBtn.addEventListener('touchend', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (toggleBtnDragging) {
+ // Save position
+ const rect = toggleBtn.getBoundingClientRect();
+ localStorage.setItem('cheat-toggle-pos', JSON.stringify({
+ left: rect.left,
+ top: rect.top
+ }));
+ } else {
+ // It was a tap, not a drag - toggle menu
+ toggleMenu();
+ }
+
+ toggleBtnDragging = false;
+ }, { passive: false });
+ }
+
+ // Close button handler (works for both touch and click)
+ const closeBtn = document.getElementById('cheat-close-btn');
+ if (closeBtn) {
+ closeBtn.addEventListener('click', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ toggleMenu();
+ });
+
+ if (isTouch) {
+ closeBtn.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ toggleMenu();
+ }, { passive: false });
+ }
+ }
+
window.addEventListener('keydown', (e) => {
if (menuOpen && e.target.tagName === 'INPUT') e.stopPropagation();
}, true);
@@ -368,6 +656,17 @@
window.addEventListener('mousedown', (e) => {
if (menuOpen && ui.contains(e.target)) e.stopPropagation();
}, true);
+
+ // Touch event handling for menu
+ window.addEventListener('touchstart', (e) => {
+ if (menuOpen && ui.contains(e.target)) e.stopPropagation();
+ }, true);
+ window.addEventListener('touchmove', (e) => {
+ if (menuOpen && ui.contains(e.target)) e.stopPropagation();
+ }, true);
+ window.addEventListener('touchend', (e) => {
+ if (menuOpen && ui.contains(e.target)) e.stopPropagation();
+ }, true);
function updateStatus(text) {
document.getElementById('ce-status').textContent = text;
@@ -972,4 +1271,243 @@
flySpeed = parseFloat(document.getElementById('ce-fly-speed').value) || 2.0;
};
+ // ========== TOUCH CONTROLS FOR AIRBREAK ==========
+
+ if (isTouch) {
+ const joystick = document.getElementById('airbreak-joystick');
+ const joystickKnob = document.getElementById('airbreak-joystick-knob');
+ const upBtn = document.getElementById('airbreak-up-btn');
+ const downBtn = document.getElementById('airbreak-down-btn');
+ const verticalBtns = document.getElementById('airbreak-vertical-btns');
+
+ let joystickActive = false;
+ let joystickCenterX = 0;
+ let joystickCenterY = 0;
+ const joystickRadius = 60; // Half of the joystick width
+ const deadzone = 15;
+
+ // Update visibility of touch controls based on airbreak state
+ function updateTouchControlsVisibility() {
+ if (airbreakConfigured && airbreakEnabled) {
+ airbreakTouchControls.classList.add('active');
+ verticalBtns.style.display = 'flex';
+ } else {
+ airbreakTouchControls.classList.remove('active');
+ verticalBtns.style.display = 'none';
+ }
+
+ // Show fly toggle button when configured
+ if (airbreakConfigured) {
+ flyToggleBtn.classList.add('visible');
+ flyToggleBtn.classList.toggle('active', airbreakEnabled);
+ flyToggleBtn.textContent = airbreakEnabled ? 'STOP' : 'FLY';
+ } else {
+ flyToggleBtn.classList.remove('visible');
+ }
+ }
+
+ // Fly toggle button with drag support
+ let flyBtnDragging = false;
+ let flyBtnStartX = 0;
+ let flyBtnStartY = 0;
+ let flyBtnInitialRight = 0;
+ let flyBtnInitialBottom = 0;
+
+ // Load saved fly button position
+ const savedFlyPos = localStorage.getItem('cheat-fly-pos');
+ if (savedFlyPos) {
+ try {
+ const pos = JSON.parse(savedFlyPos);
+ flyToggleBtn.style.right = pos.right + 'px';
+ flyToggleBtn.style.bottom = pos.bottom + 'px';
+ } catch(e) {}
+ }
+
+ flyToggleBtn.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ flyBtnDragging = false;
+
+ const touch = e.touches[0];
+ flyBtnStartX = touch.clientX;
+ flyBtnStartY = touch.clientY;
+
+ // Get current position (calculate from right/bottom)
+ const rect = flyToggleBtn.getBoundingClientRect();
+ flyBtnInitialRight = window.innerWidth - rect.right;
+ flyBtnInitialBottom = window.innerHeight - rect.bottom;
+ }, { passive: false });
+
+ flyToggleBtn.addEventListener('touchmove', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const touch = e.touches[0];
+ const deltaX = touch.clientX - flyBtnStartX;
+ const deltaY = touch.clientY - flyBtnStartY;
+
+ // Check if we've moved enough to consider it a drag
+ if (Math.abs(deltaX) > dragThreshold || Math.abs(deltaY) > dragThreshold) {
+ flyBtnDragging = true;
+ }
+
+ if (flyBtnDragging) {
+ let newRight = flyBtnInitialRight - deltaX;
+ let newBottom = flyBtnInitialBottom - deltaY;
+
+ // Clamp to screen bounds
+ const btnWidth = flyToggleBtn.offsetWidth;
+ const btnHeight = flyToggleBtn.offsetHeight;
+ newRight = Math.max(0, Math.min(window.innerWidth - btnWidth, newRight));
+ newBottom = Math.max(0, Math.min(window.innerHeight - btnHeight, newBottom));
+
+ flyToggleBtn.style.right = newRight + 'px';
+ flyToggleBtn.style.bottom = newBottom + 'px';
+ }
+ }, { passive: false });
+
+ flyToggleBtn.addEventListener('touchend', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (flyBtnDragging) {
+ // Save position
+ const rect = flyToggleBtn.getBoundingClientRect();
+ localStorage.setItem('cheat-fly-pos', JSON.stringify({
+ right: window.innerWidth - rect.right,
+ bottom: window.innerHeight - rect.bottom
+ }));
+ } else {
+ // It was a tap, not a drag - toggle fly mode
+ if (airbreakConfigured) {
+ airbreakEnabled = !airbreakEnabled;
+
+ if (airbreakEnabled) {
+ const view = getView();
+ lockedZ = view.getFloat32(positionAddr + 8, true);
+ lockedHealth = view.getFloat32(healthAddr, true);
+ console.log(`[AirBreak Touch] ENABLED - Locked Z=${lockedZ.toFixed(2)}, HP=${lockedHealth.toFixed(2)}`);
+ }
+
+ document.getElementById('ce-airbreak-status').textContent =
+ airbreakEnabled ?
+ `FLYING! Z=${lockedZ.toFixed(1)} HP=${lockedHealth.toFixed(0)}` :
+ `Ready! [Tap FLY]`;
+ document.getElementById('ce-airbreak-status').style.color = airbreakEnabled ? '#ff0' : '#0f0';
+
+ updateTouchControlsVisibility();
+ }
+ }
+
+ flyBtnDragging = false;
+ }, { passive: false });
+
+ // Joystick touch handling
+ joystick.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ joystickActive = true;
+
+ const rect = joystick.getBoundingClientRect();
+ joystickCenterX = rect.left + rect.width / 2;
+ joystickCenterY = rect.top + rect.height / 2;
+
+ handleJoystickMove(e.touches[0]);
+ }, { passive: false });
+
+ joystick.addEventListener('touchmove', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (joystickActive) {
+ handleJoystickMove(e.touches[0]);
+ }
+ }, { passive: false });
+
+ joystick.addEventListener('touchend', (e) => {
+ e.preventDefault();
+ joystickActive = false;
+
+ // Reset knob position
+ joystickKnob.style.transform = 'translate(-50%, -50%)';
+
+ // Reset movement
+ keysPressed.w = false;
+ keysPressed.s = false;
+ keysPressed.a = false;
+ keysPressed.d = false;
+ }, { passive: false });
+
+ joystick.addEventListener('touchcancel', (e) => {
+ joystickActive = false;
+ joystickKnob.style.transform = 'translate(-50%, -50%)';
+ keysPressed.w = false;
+ keysPressed.s = false;
+ keysPressed.a = false;
+ keysPressed.d = false;
+ });
+
+ function handleJoystickMove(touch) {
+ let deltaX = touch.clientX - joystickCenterX;
+ let deltaY = touch.clientY - joystickCenterY;
+
+ // Clamp to radius
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ if (distance > joystickRadius) {
+ deltaX = (deltaX / distance) * joystickRadius;
+ deltaY = (deltaY / distance) * joystickRadius;
+ }
+
+ // Move knob
+ joystickKnob.style.transform = `translate(calc(-50% + ${deltaX}px), calc(-50% + ${deltaY}px))`;
+
+ // Apply movement (with deadzone)
+ keysPressed.w = deltaY < -deadzone;
+ keysPressed.s = deltaY > deadzone;
+ keysPressed.a = deltaX < -deadzone;
+ keysPressed.d = deltaX > deadzone;
+ }
+
+ // Up/Down buttons for vertical movement
+ upBtn.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ keysPressed.space = true;
+ }, { passive: false });
+
+ upBtn.addEventListener('touchend', (e) => {
+ e.preventDefault();
+ keysPressed.space = false;
+ }, { passive: false });
+
+ upBtn.addEventListener('touchcancel', () => {
+ keysPressed.space = false;
+ });
+
+ downBtn.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ keysPressed.shift = true;
+ }, { passive: false });
+
+ downBtn.addEventListener('touchend', (e) => {
+ e.preventDefault();
+ keysPressed.shift = false;
+ }, { passive: false });
+
+ downBtn.addEventListener('touchcancel', () => {
+ keysPressed.shift = false;
+ });
+
+ // Monitor airbreak state changes
+ const originalSetupAirbreak = setupAirbreak;
+ setupAirbreak = function() {
+ originalSetupAirbreak();
+ updateTouchControlsVisibility();
+ };
+ document.getElementById('ce-setup-airbreak').onclick = setupAirbreak;
+
+ // Update controls visibility periodically
+ setInterval(updateTouchControlsVisibility, 500);
+ }
+
})();
diff --git a/dist/modules/packages/en.js b/dist/modules/packages/en.js
index 2efbb99..c61f995 100644
--- a/dist/modules/packages/en.js
+++ b/dist/modules/packages/en.js
@@ -36,7 +36,7 @@ const DATA_PACKAGE = {
start: 7604416,
end: 33051462
}, {
- filename: "/vc-assets/local/audio/sfx.sdt",
+ filename: "/vc-assets/local/audio/sdt",
start: 33051462,
end: 33250282
}, {