diff --git a/resources/modmenu/client.js b/resources/modmenu/client.js index 11df8da..61c1649 100644 --- a/resources/modmenu/client.js +++ b/resources/modmenu/client.js @@ -366,23 +366,6 @@ const itemDescriptions = { "veh_upgrade_remove_all": "🗑️ REMOVE ALL - Strip all upgrades from vehicle!", "veh_upgrades": "🔧 UPGRADES - Add NOS, hydraulics, wheels, spoilers and more!", - // Handling Editor descriptions - "handling_reset": "🔄 RESET - Restore all handling values to vehicle defaults!", - "handling_preset": "⚡ PRESET - Apply a pre-configured handling setup instantly!", - "handling_set": "🎛️ ADJUST - Modify this handling parameter value!", - "handling_traction_max": "🏎️ TRACTION MAX - Maximum grip your tyres can achieve!", - "handling_traction_min": "🏎️ TRACTION MIN - Minimum grip when sliding or braking!", - "handling_traction_bias": "⚖️ TRACTION BIAS - Front vs rear grip distribution!", - "handling_traction_loss": "💨 TRACTION LOSS - How easily tyres lose grip!", - "handling_susp_force": "🔧 SUSPENSION FORCE - Spring stiffness for bumps!", - "handling_susp_height": "📏 SUSPENSION HEIGHT - Ride height of the vehicle!", - "handling_susp_damp": "🎯 DAMPING - Suspension bounce control!", - "handling_power": "⚡ ENGINE POWER - Acceleration and torque!", - "handling_speed": "🚀 TOP SPEED - Maximum velocity limit!", - "handling_brake": "🛑 BRAKE FORCE - Stopping power of brakes!", - "handling_mass": "⚖️ MASS - Vehicle weight affects grip and momentum!", - "handling_com": "🎯 CENTER OF MASS - Affects roll and flip tendency!", - "handling_steering": "🎮 STEERING - How far wheels can turn!" }; // Font for text rendering (will be loaded on resource start) @@ -403,93 +386,6 @@ const menu = { // Player list cache let playerList = []; -// Vehicle handling values -let handlingMods = { - grip: 1.0, - acceleration: 1.0, - topSpeed: 1.0, - braking: 1.0 -}; - -// ============================================================================ -// LAYER B: PHYSICS EMULATION SYSTEM -// All handling effects are achieved through velocity/turnVelocity manipulation -// This bypasses the need for real handling.dat editing which is not supported -// ============================================================================ -let physicsEmulation = { - // Grip/Traction Assist State - gripAssistEnabled: true, - gripAssistStrength: 1.0, // 0-2, higher = more correction - - // Acceleration Boost State - accelBoostEnabled: false, - accelBoostMultiplier: 1.0, // 1.0 = normal, 2.0 = double - maxSpeedLimit: 60.0, // m/s (~216 km/h) - - // Braking State - brakeAssistEnabled: true, - brakeMultiplier: 1.0, // 1.0 = normal, 2.0 = stronger - - // Stability/Anti-Roll State - stabilityEnabled: true, - stabilityStrength: 1.0, // How much to dampen rotation - antiRollStrength: 0.5, // How much to resist flipping - - // Previous frame values for calculations - lastVelocity: null, - lastHeading: 0, - lastSpeed: 0, - lastSteerInput: 0, - - // Input detection (estimated from velocity changes) - isAccelerating: false, - isBraking: false, - isSteering: false, - steerDirection: 0 // -1 left, 0 center, 1 right -}; - -// ============================================================================ -// HANDLING EDITOR SYSTEM -// ============================================================================ -let handlingEditorActive = false; -let handlingEditorAnim = 0; -let handlingValues = { - // Traction & Grip - tractionCurveMax: 2.0, - tractionCurveMin: 1.8, - tractionBias: 0.5, - // Suspension - suspensionForce: 2.0, - suspensionCompDamp: 1.0, - suspensionReboundDamp: 1.2, - suspensionUpperLimit: 0.12, - suspensionLowerLimit: -0.10, - suspensionRaise: 0.0, - // Engine & Speed - driveForce: 0.25, - driveInertia: 1.0, - initialDriveMaxVel: 200.0, - brakeForce: 0.8, - // Mass & Weight - mass: 1500.0, - centreOfMassZ: 0.0, - // Handling feel - steeringLock: 35.0, - tractionLoss: 0.8 -}; - -// Store original values for reset -let originalHandlingValues = null; - -// Visual feedback colors based on values -let tyreGlowColors = { fr: {r:0,g:255,b:100}, fl: {r:0,g:255,b:100}, rr: {r:0,g:255,b:100}, rl: {r:0,g:255,b:100} }; -let engineGlow = { r: 255, g: 100, b: 0, intensity: 0 }; -let brakeGlow = { r: 255, g: 50, b: 0, intensity: 0 }; -let suspensionOffset = 0; -let carRotationAngle = 0; - -// Currently selected handling parameter for highlighting -let selectedHandlingParam = ""; // ============================================================================ // MENU DATA STRUCTURE @@ -1062,7 +958,6 @@ const menuData = { { label: "Flip Vehicle", action: "veh_flip" }, { label: "Vehicle Colors", action: "submenu", target: "veh_colors" }, { label: "Vehicle Upgrades", action: "submenu", target: "veh_upgrades" }, - { label: "Handling Editor", action: "submenu", target: "handlingEditor" }, { label: "--- Toggles ---", action: "none" }, { label: "God Mode", action: "toggle", target: "vehGodMode", state: false }, { label: "Nitro Boost", action: "veh_nitro" }, @@ -1128,198 +1023,6 @@ const menuData = { ] }, - handlingEditor: { - title: "HANDLING EDITOR", - items: [ - { label: "--- Presets ---", action: "none" }, - { label: "Reset to Default", action: "handling_reset" }, - { label: "Race Setup", action: "handling_preset", value: "race" }, - { label: "Off-Road Setup", action: "handling_preset", value: "offroad" }, - { label: "Low Rider", action: "handling_preset", value: "lowrider" }, - { label: "--- Traction & Grip ---", action: "none" }, - { label: "Traction Max", action: "submenu", target: "handling_traction_max" }, - { label: "Traction Min", action: "submenu", target: "handling_traction_min" }, - { label: "Traction Bias (F/R)", action: "submenu", target: "handling_traction_bias" }, - { label: "Traction Loss", action: "submenu", target: "handling_traction_loss" }, - { label: "--- Suspension ---", action: "none" }, - { label: "Suspension Force", action: "submenu", target: "handling_susp_force" }, - { label: "Suspension Height", action: "submenu", target: "handling_susp_height" }, - { label: "Suspension Damping", action: "submenu", target: "handling_susp_damp" }, - { label: "--- Engine & Speed ---", action: "none" }, - { label: "Engine Power", action: "submenu", target: "handling_power" }, - { label: "Top Speed", action: "submenu", target: "handling_speed" }, - { label: "Brake Force", action: "submenu", target: "handling_brake" }, - { label: "--- Weight & Balance ---", action: "none" }, - { label: "Vehicle Mass", action: "submenu", target: "handling_mass" }, - { label: "Center of Mass", action: "submenu", target: "handling_com" }, - { label: "--- Steering ---", action: "none" }, - { label: "Steering Lock", action: "submenu", target: "handling_steering" } - ] - }, - - handling_traction_max: { - title: "TRACTION MAX", - items: [ - { label: "Ice (0.5)", action: "handling_set", param: "tractionCurveMax", value: 0.5 }, - { label: "Very Low (1.0)", action: "handling_set", param: "tractionCurveMax", value: 1.0 }, - { label: "Low (1.5)", action: "handling_set", param: "tractionCurveMax", value: 1.5 }, - { label: "Default (2.0)", action: "handling_set", param: "tractionCurveMax", value: 2.0 }, - { label: "Medium (2.5)", action: "handling_set", param: "tractionCurveMax", value: 2.5 }, - { label: "High (3.0)", action: "handling_set", param: "tractionCurveMax", value: 3.0 }, - { label: "Very High (3.5)", action: "handling_set", param: "tractionCurveMax", value: 3.5 }, - { label: "Racing Slicks (4.0)", action: "handling_set", param: "tractionCurveMax", value: 4.0 }, - { label: "Glue (5.0)", action: "handling_set", param: "tractionCurveMax", value: 5.0 } - ] - }, - - handling_traction_min: { - title: "TRACTION MIN", - items: [ - { label: "Ice (0.3)", action: "handling_set", param: "tractionCurveMin", value: 0.3 }, - { label: "Very Low (0.8)", action: "handling_set", param: "tractionCurveMin", value: 0.8 }, - { label: "Low (1.2)", action: "handling_set", param: "tractionCurveMin", value: 1.2 }, - { label: "Default (1.8)", action: "handling_set", param: "tractionCurveMin", value: 1.8 }, - { label: "Medium (2.2)", action: "handling_set", param: "tractionCurveMin", value: 2.2 }, - { label: "High (2.8)", action: "handling_set", param: "tractionCurveMin", value: 2.8 }, - { label: "Very High (3.5)", action: "handling_set", param: "tractionCurveMin", value: 3.5 } - ] - }, - - handling_traction_bias: { - title: "TRACTION BIAS", - items: [ - { label: "Full Front (0.0)", action: "handling_set", param: "tractionBias", value: 0.0 }, - { label: "Front Heavy (0.3)", action: "handling_set", param: "tractionBias", value: 0.3 }, - { label: "Balanced (0.5)", action: "handling_set", param: "tractionBias", value: 0.5 }, - { label: "Rear Heavy (0.7)", action: "handling_set", param: "tractionBias", value: 0.7 }, - { label: "Full Rear (1.0)", action: "handling_set", param: "tractionBias", value: 1.0 } - ] - }, - - handling_traction_loss: { - title: "TRACTION LOSS", - items: [ - { label: "No Loss (0.0)", action: "handling_set", param: "tractionLoss", value: 0.0 }, - { label: "Minimal (0.4)", action: "handling_set", param: "tractionLoss", value: 0.4 }, - { label: "Default (0.8)", action: "handling_set", param: "tractionLoss", value: 0.8 }, - { label: "Loose (1.2)", action: "handling_set", param: "tractionLoss", value: 1.2 }, - { label: "Ice Mode (2.0)", action: "handling_set", param: "tractionLoss", value: 2.0 } - ] - }, - - handling_susp_force: { - title: "SUSPENSION FORCE", - items: [ - { label: "Soft (0.5)", action: "handling_set", param: "suspensionForce", value: 0.5 }, - { label: "Comfort (1.0)", action: "handling_set", param: "suspensionForce", value: 1.0 }, - { label: "Default (2.0)", action: "handling_set", param: "suspensionForce", value: 2.0 }, - { label: "Firm (3.0)", action: "handling_set", param: "suspensionForce", value: 3.0 }, - { label: "Sport (4.0)", action: "handling_set", param: "suspensionForce", value: 4.0 }, - { label: "Race (5.0)", action: "handling_set", param: "suspensionForce", value: 5.0 }, - { label: "Rock Solid (8.0)", action: "handling_set", param: "suspensionForce", value: 8.0 } - ] - }, - - handling_susp_height: { - title: "SUSPENSION HEIGHT", - items: [ - { label: "Slammed (-0.15)", action: "handling_set", param: "suspensionRaise", value: -0.15 }, - { label: "Low (-0.10)", action: "handling_set", param: "suspensionRaise", value: -0.10 }, - { label: "Lowered (-0.05)", action: "handling_set", param: "suspensionRaise", value: -0.05 }, - { label: "Default (0.0)", action: "handling_set", param: "suspensionRaise", value: 0.0 }, - { label: "Raised (0.05)", action: "handling_set", param: "suspensionRaise", value: 0.05 }, - { label: "High (0.10)", action: "handling_set", param: "suspensionRaise", value: 0.10 }, - { label: "Off-Road (0.15)", action: "handling_set", param: "suspensionRaise", value: 0.15 }, - { label: "Monster (0.25)", action: "handling_set", param: "suspensionRaise", value: 0.25 } - ] - }, - - handling_susp_damp: { - title: "SUSPENSION DAMPING", - items: [ - { label: "Bouncy (0.3)", action: "handling_set", param: "suspensionCompDamp", value: 0.3 }, - { label: "Soft (0.6)", action: "handling_set", param: "suspensionCompDamp", value: 0.6 }, - { label: "Default (1.0)", action: "handling_set", param: "suspensionCompDamp", value: 1.0 }, - { label: "Sport (1.5)", action: "handling_set", param: "suspensionCompDamp", value: 1.5 }, - { label: "Race (2.0)", action: "handling_set", param: "suspensionCompDamp", value: 2.0 }, - { label: "Stiff (3.0)", action: "handling_set", param: "suspensionCompDamp", value: 3.0 } - ] - }, - - handling_power: { - title: "ENGINE POWER", - items: [ - { label: "Weak (0.10)", action: "handling_set", param: "driveForce", value: 0.10 }, - { label: "Low (0.18)", action: "handling_set", param: "driveForce", value: 0.18 }, - { label: "Default (0.25)", action: "handling_set", param: "driveForce", value: 0.25 }, - { label: "Tuned (0.35)", action: "handling_set", param: "driveForce", value: 0.35 }, - { label: "Sport (0.45)", action: "handling_set", param: "driveForce", value: 0.45 }, - { label: "Super (0.60)", action: "handling_set", param: "driveForce", value: 0.60 }, - { label: "Hyper (0.80)", action: "handling_set", param: "driveForce", value: 0.80 }, - { label: "Rocket (1.20)", action: "handling_set", param: "driveForce", value: 1.20 } - ] - }, - - handling_speed: { - title: "TOP SPEED", - items: [ - { label: "Slow (100)", action: "handling_set", param: "initialDriveMaxVel", value: 100.0 }, - { label: "City (140)", action: "handling_set", param: "initialDriveMaxVel", value: 140.0 }, - { label: "Default (200)", action: "handling_set", param: "initialDriveMaxVel", value: 200.0 }, - { label: "Fast (250)", action: "handling_set", param: "initialDriveMaxVel", value: 250.0 }, - { label: "Super (300)", action: "handling_set", param: "initialDriveMaxVel", value: 300.0 }, - { label: "Hyper (400)", action: "handling_set", param: "initialDriveMaxVel", value: 400.0 }, - { label: "Insane (500)", action: "handling_set", param: "initialDriveMaxVel", value: 500.0 } - ] - }, - - handling_brake: { - title: "BRAKE FORCE", - items: [ - { label: "Weak (0.3)", action: "handling_set", param: "brakeForce", value: 0.3 }, - { label: "Low (0.5)", action: "handling_set", param: "brakeForce", value: 0.5 }, - { label: "Default (0.8)", action: "handling_set", param: "brakeForce", value: 0.8 }, - { label: "Strong (1.2)", action: "handling_set", param: "brakeForce", value: 1.2 }, - { label: "Race (1.6)", action: "handling_set", param: "brakeForce", value: 1.6 }, - { label: "Ceramic (2.0)", action: "handling_set", param: "brakeForce", value: 2.0 }, - { label: "Instant Stop (3.0)", action: "handling_set", param: "brakeForce", value: 3.0 } - ] - }, - - handling_mass: { - title: "VEHICLE MASS", - items: [ - { label: "Feather (500)", action: "handling_set", param: "mass", value: 500.0 }, - { label: "Light (1000)", action: "handling_set", param: "mass", value: 1000.0 }, - { label: "Default (1500)", action: "handling_set", param: "mass", value: 1500.0 }, - { label: "Heavy (2000)", action: "handling_set", param: "mass", value: 2000.0 }, - { label: "Truck (3000)", action: "handling_set", param: "mass", value: 3000.0 }, - { label: "Tank (5000)", action: "handling_set", param: "mass", value: 5000.0 } - ] - }, - - handling_com: { - title: "CENTER OF MASS", - items: [ - { label: "Very Low (-0.3)", action: "handling_set", param: "centreOfMassZ", value: -0.3 }, - { label: "Low (-0.15)", action: "handling_set", param: "centreOfMassZ", value: -0.15 }, - { label: "Default (0.0)", action: "handling_set", param: "centreOfMassZ", value: 0.0 }, - { label: "High (0.15)", action: "handling_set", param: "centreOfMassZ", value: 0.15 }, - { label: "Very High (0.3)", action: "handling_set", param: "centreOfMassZ", value: 0.3 } - ] - }, - - handling_steering: { - title: "STEERING LOCK", - items: [ - { label: "Tight (25)", action: "handling_set", param: "steeringLock", value: 25.0 }, - { label: "Default (35)", action: "handling_set", param: "steeringLock", value: 35.0 }, - { label: "Loose (45)", action: "handling_set", param: "steeringLock", value: 45.0 }, - { label: "Wide (55)", action: "handling_set", param: "steeringLock", value: 55.0 }, - { label: "Extreme (70)", action: "handling_set", param: "steeringLock", value: 70.0 } - ] - }, - veh_neons: { title: "NEON LIGHTS", items: [ @@ -1913,12 +1616,6 @@ function goBack() { colorEditorActive = false; } - // Deactivate handling editor when leaving handling menus - if (currentMenu === "handlingEditor" || currentMenu.startsWith("handling_")) { - handlingEditorActive = false; - selectedHandlingParam = ""; - } - if (menuStack.length > 0) { let prev = menuStack.pop(); currentMenu = prev.menu; @@ -2022,26 +1719,6 @@ function selectItem() { colorEditorActive = true; } - // Activate handling editor visualization when entering handling menus - if (item.target === "handlingEditor" || item.target.startsWith("handling_")) { - handlingEditorActive = true; - // Highlight the relevant parameter in the visualization - if (item.target.includes("traction")) { - selectedHandlingParam = "traction"; - } else if (item.target.includes("susp")) { - selectedHandlingParam = "suspension"; - } else if (item.target.includes("power") || item.target.includes("speed")) { - selectedHandlingParam = "engine"; - } else if (item.target.includes("brake")) { - selectedHandlingParam = "brakes"; - } else if (item.target.includes("mass") || item.target.includes("com")) { - selectedHandlingParam = "weight"; - } else if (item.target.includes("steering")) { - selectedHandlingParam = "steering"; - } else { - selectedHandlingParam = ""; - } - } } break; @@ -2629,39 +2306,6 @@ function selectItem() { showNotification("Theme: " + themes[item.value].name); break; - // ============================================================================ - // HANDLING EDITOR ACTIONS - // ============================================================================ - case "handling_set": - if (localPlayer && localPlayer.vehicle) { - handlingValues[item.param] = item.value; - selectedHandlingParam = item.param; - applyHandlingValue(item.param, item.value); - showNotification(item.param + ": " + item.value); - // Trigger visual feedback - updateHandlingVisuals(item.param, item.value); - } else { - showNotification("Get in a vehicle first!"); - } - break; - - case "handling_reset": - if (localPlayer && localPlayer.vehicle) { - resetHandlingToDefault(); - showNotification("Handling reset to default!"); - } else { - showNotification("Get in a vehicle first!"); - } - break; - - case "handling_preset": - if (localPlayer && localPlayer.vehicle) { - applyHandlingPreset(item.value); - showNotification("Applied: " + item.label); - } else { - showNotification("Get in a vehicle first!"); - } - break; } } @@ -4493,156 +4137,6 @@ addEventHandler("OnProcess", function(event) { } catch(e) {} } - // ============================================================================ - // LAYER B: PHYSICS EMULATION PROCESSING - // All handling effects achieved via velocity manipulation - // ============================================================================ - try { - let vel = veh.velocity; - let turnVel = veh.turnVelocity; - let heading = veh.heading || 0; - let speed = Math.sqrt(vel.x * vel.x + vel.y * vel.y); - let speedKmh = speed * 3.6; - - // Calculate forward direction vector from heading - let forwardX = -Math.sin(heading); - let forwardY = Math.cos(heading); - - // Calculate right direction vector (perpendicular to forward) - let rightX = Math.cos(heading); - let rightY = Math.sin(heading); - - // Decompose velocity into forward and sideways components - let forwardSpeed = vel.x * forwardX + vel.y * forwardY; - let sidewaysSpeed = vel.x * rightX + vel.y * rightY; - - // Detect steering input from turn velocity - let steerInput = turnVel.z || 0; - let isSteeringLeft = steerInput > 0.01; - let isSteeringRight = steerInput < -0.01; - physicsEmulation.isSteering = isSteeringLeft || isSteeringRight; - physicsEmulation.steerDirection = isSteeringLeft ? -1 : (isSteeringRight ? 1 : 0); - - // Detect acceleration/braking from speed changes - if (physicsEmulation.lastSpeed !== null) { - let speedDelta = speed - physicsEmulation.lastSpeed; - physicsEmulation.isAccelerating = speedDelta > 0.1 && forwardSpeed > 0; - physicsEmulation.isBraking = speedDelta < -0.2 || forwardSpeed < -1; - } - - // Save for next frame - physicsEmulation.lastSpeed = speed; - physicsEmulation.lastHeading = heading; - physicsEmulation.lastVelocity = vel; - - // ===== GRIP / TRACTION ASSIST ===== - if (physicsEmulation.gripAssistEnabled && speed > 2) { - // Calculate grip based on handling values - let gripFactor = (handlingValues.tractionCurveMax / 2.0) * physicsEmulation.gripAssistStrength; - gripFactor = Math.min(2.0, Math.max(0.1, gripFactor)); - - // Correct sideways velocity back toward forward direction - // Stronger at low speeds, weaker at high speeds (realism) - let speedFactor = Math.max(0.3, 1.0 - (speed / 40.0)); - let correctionStrength = gripFactor * speedFactor * 0.15; - - // Apply sideways velocity correction - if (Math.abs(sidewaysSpeed) > 0.5) { - let correction = -sidewaysSpeed * correctionStrength; - veh.velocity = new Vec3( - vel.x + rightX * correction, - vel.y + rightY * correction, - vel.z - ); - } - } - - // ===== ACCELERATION BOOST (Behavioral) ===== - if (handlingMods.acceleration > 1.0 && physicsEmulation.isAccelerating && speed > 1) { - let maxSpeed = physicsEmulation.maxSpeedLimit * handlingMods.topSpeed; - - // Only boost if below max speed - if (speed < maxSpeed) { - let boostFactor = (handlingMods.acceleration - 1.0) * 0.08; - // Add velocity along forward vector - let boostX = forwardX * boostFactor * Math.min(speed, 20); - let boostY = forwardY * boostFactor * Math.min(speed, 20); - - veh.velocity = new Vec3(vel.x + boostX, vel.y + boostY, vel.z); - } - } - - // ===== TOP SPEED CONTROL ===== - let maxSpeed = 35 * handlingMods.topSpeed; // Base ~35 m/s - if (speed > maxSpeed) { - // Smoothly reduce to max speed (not instant) - let factor = maxSpeed / speed; - factor = 0.95 + factor * 0.05; // Gradual approach - veh.velocity = new Vec3(vel.x * factor, vel.y * factor, vel.z); - } - // Speed boost when above normal threshold - else if (handlingMods.topSpeed > 1.0 && speed > 20 && speed < maxSpeed && physicsEmulation.isAccelerating) { - let pushFactor = (handlingMods.topSpeed - 1.0) * 0.015; - let pushX = forwardX * pushFactor * speed; - let pushY = forwardY * pushFactor * speed; - veh.velocity = new Vec3(vel.x + pushX, vel.y + pushY, vel.z); - } - - // ===== BRAKING BEHAVIOR ===== - if (physicsEmulation.isBraking && handlingMods.braking > 0 && speed > 1) { - // Smooth braking - reduce velocity magnitude - let brakeFactor = handlingMods.braking * 0.05; - let dampFactor = 1.0 - Math.min(0.15, brakeFactor); - - // Preserve directional stability while braking - let newSpeed = speed * dampFactor; - if (newSpeed > 0.5) { - let ratio = newSpeed / speed; - veh.velocity = new Vec3(vel.x * ratio, vel.y * ratio, vel.z); - } - } - - // ===== STABILITY / ANTI-ROLL ===== - if (physicsEmulation.stabilityEnabled && speed > 5) { - // Get current turn velocity - let currentTurn = veh.turnVelocity; - - // Dampen excessive rotation (X and Y axes = roll/pitch) - let stabilityFactor = physicsEmulation.stabilityStrength * 0.2; - let antiRollFactor = physicsEmulation.antiRollStrength * 0.3; - - // Cap extreme rotational velocities to prevent flipping - let maxRoll = 1.5 - (antiRollFactor * 0.5); - let dampedX = currentTurn.x; - let dampedY = currentTurn.y; - let dampedZ = currentTurn.z; - - // Anti-roll: prevent excessive X/Y rotation - if (Math.abs(dampedX) > maxRoll) { - dampedX = dampedX * (1 - antiRollFactor * 0.5); - } - if (Math.abs(dampedY) > maxRoll) { - dampedY = dampedY * (1 - antiRollFactor * 0.5); - } - - // At high speed, dampen all rotations for stability - if (speed > 25) { - let highSpeedDamp = 1 - (stabilityFactor * Math.min(1, (speed - 25) / 20)); - dampedX *= highSpeedDamp; - dampedY *= highSpeedDamp; - // Keep Z (yaw) mostly intact for steering feel - dampedZ *= (1 - stabilityFactor * 0.3); - } - - // Apply damped turn velocity - if (dampedX !== currentTurn.x || dampedY !== currentTurn.y || dampedZ !== currentTurn.z) { - veh.turnVelocity = new Vec3(dampedX, dampedY, dampedZ); - } - } - } catch(e) { - // Silent fail for physics processing - } - // Fly mode - WASD controls altitude if (toggleStates.flyMode) { try { @@ -4847,603 +4341,6 @@ addEventHandler("OnPedWeaponShoot", function(event, ped, weapon) { // INITIALIZATION // ============================================================================ -// ============================================================================ -// HANDLING EDITOR FUNCTIONS -// ============================================================================ - -// Apply a single handling value to the current vehicle -// LAYER B: All handling effects are achieved through Physics Emulation -// This does NOT modify real handling.dat values - instead it configures -// the physics emulation system to simulate the desired behavior -function applyHandlingValue(param, value) { - if (!localPlayer || !localPlayer.vehicle) return; - - let veh = localPlayer.vehicle; - - try { - switch(param) { - // ===== TRACTION & GRIP (Physics Emulation) ===== - case "tractionCurveMax": - // Controls grip assist strength in physics emulation - // Higher = more sideways velocity correction - physicsEmulation.gripAssistStrength = value / 2.0; - // Also try native if available (may work on some servers) - try { natives.setCarTraction(veh, value); } catch(e) {} - console.log("[Handling] Grip Assist Strength: " + physicsEmulation.gripAssistStrength.toFixed(2)); - break; - - case "tractionCurveMin": - // Minimum grip affects how much the car slides at low speeds - let combinedGrip = (handlingValues.tractionCurveMax + value) / 4.0; - physicsEmulation.gripAssistStrength = combinedGrip; - try { natives.setCarTraction(veh, combinedGrip * 2); } catch(e) {} - break; - - case "tractionBias": - // Front/rear grip distribution - affects stability - // Lower value = more front grip = understeer - // Higher value = more rear grip = oversteer - physicsEmulation.stabilityStrength = 1.0 + (0.5 - value); - physicsEmulation.antiRollStrength = 0.5 + (0.5 - value) * 0.3; - console.log("[Handling] Stability adjusted for bias: " + value.toFixed(2)); - break; - - case "tractionLoss": - // Higher = easier to lose traction - if (value > 1.0) { - physicsEmulation.gripAssistStrength *= (1.0 / value); - } - console.log("[Handling] Traction Loss: " + value.toFixed(2) + " - Grip reduced to: " + physicsEmulation.gripAssistStrength.toFixed(2)); - break; - - // ===== SUSPENSION (Physics Emulation + Visual) ===== - case "suspensionForce": - // Stiffness affects stability - stiffer = more stable at speed - physicsEmulation.stabilityStrength = value / 2.0; - suspensionOffset = (value - 2.0) * 0.02; - console.log("[Handling] Suspension stiffness -> Stability: " + physicsEmulation.stabilityStrength.toFixed(2)); - break; - - case "suspensionRaise": - // Try API method, fallback to visual only - try { - for (let wheelId = 0; wheelId < 4; wheelId++) { - veh.setSuspensionHeight(wheelId, value); - } - console.log("[Handling] Suspension height set: " + value); - } catch(e) { - // Visual feedback only - } - suspensionOffset = value; - // Higher ride height = less stable - physicsEmulation.antiRollStrength = Math.max(0.2, 0.5 - value); - break; - - case "suspensionCompDamp": - // Damping affects how quickly stability corrections happen - physicsEmulation.stabilityStrength *= (value / 1.0); - break; - - // ===== ENGINE & SPEED (Physics Emulation) ===== - case "driveForce": - // Engine power -> Acceleration boost multiplier - handlingMods.acceleration = value / 0.25; - physicsEmulation.accelBoostEnabled = (handlingMods.acceleration > 1.0); - physicsEmulation.accelBoostMultiplier = handlingMods.acceleration; - console.log("[Handling] Acceleration Boost: " + handlingMods.acceleration.toFixed(2) + "x"); - break; - - case "initialDriveMaxVel": - // Top speed -> Speed limit in physics emulation - handlingMods.topSpeed = value / 200.0; - physicsEmulation.maxSpeedLimit = value / 3.6; // Convert to m/s - console.log("[Handling] Max Speed: " + value + " km/h (" + physicsEmulation.maxSpeedLimit.toFixed(1) + " m/s)"); - break; - - case "brakeForce": - // Brake strength -> Braking multiplier - handlingMods.braking = value / 0.8; - physicsEmulation.brakeMultiplier = handlingMods.braking; - physicsEmulation.brakeAssistEnabled = (handlingMods.braking > 0); - console.log("[Handling] Brake Multiplier: " + handlingMods.braking.toFixed(2) + "x"); - break; - - // ===== WEIGHT & BALANCE (Physics Emulation) ===== - case "mass": - // Mass affects momentum and stability - // Try direct API first - try { - veh.mass = value; - console.log("[Handling] Mass set: " + value + " kg"); - } catch(e1) { - try { natives.setCarMass(veh, value); } catch(e2) {} - } - // Heavier = more stable, less responsive - let massFactor = value / 1500.0; - physicsEmulation.stabilityStrength *= massFactor; - physicsEmulation.gripAssistStrength *= (1 / Math.sqrt(massFactor)); - break; - - case "centreOfMassZ": - // Center of mass height affects roll tendency - // Higher = less stable, more likely to flip - physicsEmulation.antiRollStrength = Math.max(0.2, 0.5 - value * 2); - // Simulate through turn velocity dampening - if (value > 0.1) { - physicsEmulation.stabilityStrength *= (1 - value * 0.5); - } - console.log("[Handling] CoM Height: " + value + " -> Anti-Roll: " + physicsEmulation.antiRollStrength.toFixed(2)); - break; - - // ===== STEERING (Stored for reference) ===== - case "steeringLock": - // Steering angle is engine-controlled, store for display only - console.log("[Handling] Steering Lock: " + value + " degrees (visual only)"); - break; - } - } catch(e) { - console.log("[HandlingEditor] Error applying " + param + ": " + e); - } -} - -// Reset handling to default values -function resetHandlingToDefault() { - handlingValues = { - tractionCurveMax: 2.0, - tractionCurveMin: 1.8, - tractionBias: 0.5, - suspensionForce: 2.0, - suspensionCompDamp: 1.0, - suspensionReboundDamp: 1.2, - suspensionUpperLimit: 0.12, - suspensionLowerLimit: -0.10, - suspensionRaise: 0.0, - driveForce: 0.25, - driveInertia: 1.0, - initialDriveMaxVel: 200.0, - brakeForce: 0.8, - mass: 1500.0, - centreOfMassZ: 0.0, - steeringLock: 35.0, - tractionLoss: 0.8 - }; - - // Reset visual feedback - tyreGlowColors = { fr: {r:0,g:255,b:100}, fl: {r:0,g:255,b:100}, rr: {r:0,g:255,b:100}, rl: {r:0,g:255,b:100} }; - engineGlow.intensity = 0; - brakeGlow.intensity = 0; - suspensionOffset = 0; - selectedHandlingParam = ""; - - // ===== RESET PHYSICS EMULATION TO DEFAULTS ===== - physicsEmulation.gripAssistEnabled = true; - physicsEmulation.gripAssistStrength = 1.0; - physicsEmulation.accelBoostEnabled = false; - physicsEmulation.accelBoostMultiplier = 1.0; - physicsEmulation.maxSpeedLimit = 60.0; - physicsEmulation.brakeAssistEnabled = true; - physicsEmulation.brakeMultiplier = 1.0; - physicsEmulation.stabilityEnabled = true; - physicsEmulation.stabilityStrength = 1.0; - physicsEmulation.antiRollStrength = 0.5; - - // Apply defaults to vehicle using proper API - if (localPlayer && localPlayer.vehicle) { - let veh = localPlayer.vehicle; - - // Reset traction (try native) - try { - natives.setCarTraction(veh, 2.0); - } catch(e) {} - - // Reset mass using physical.mass property - try { - veh.mass = 1500.0; - } catch(e1) { - try { natives.setCarMass(veh, 1500.0); } catch(e2) {} - } - - // Reset suspension height - try { - for (let wheelId = 0; wheelId < 4; wheelId++) { - veh.setSuspensionHeight(wheelId, 0.0); - } - } catch(e) {} - - // Reset turn velocity - try { - veh.turnVelocity = new Vec3(0, 0, 0); - } catch(e) {} - - // Repair vehicle - try { natives.fixCar(veh); } catch(e) {} - } - - // Reset handling mods - handlingMods = { - grip: 1.0, - acceleration: 1.0, - topSpeed: 1.0, - braking: 1.0 - }; - - console.log("[Handling] Reset to defaults - Physics Emulation reset"); -} - -// Apply handling presets - Configures PHYSICS EMULATION for each setup -function applyHandlingPreset(preset) { - switch(preset) { - case "race": - // RACE: High grip, fast acceleration, high top speed, strong brakes - handlingValues.tractionCurveMax = 3.5; - handlingValues.tractionCurveMin = 3.0; - handlingValues.tractionLoss = 0.5; - handlingValues.suspensionForce = 4.0; - handlingValues.suspensionCompDamp = 2.0; - handlingValues.suspensionRaise = -0.05; - handlingValues.driveForce = 0.50; - handlingValues.initialDriveMaxVel = 300.0; - handlingValues.brakeForce = 1.5; - handlingValues.mass = 1200.0; - handlingValues.centreOfMassZ = -0.1; - - // Physics Emulation: Maximum grip and stability - physicsEmulation.gripAssistEnabled = true; - physicsEmulation.gripAssistStrength = 1.8; - physicsEmulation.stabilityStrength = 1.5; - physicsEmulation.antiRollStrength = 0.7; - physicsEmulation.maxSpeedLimit = 85.0; // ~300 km/h - break; - - case "offroad": - // OFFROAD: Good grip, high suspension, heavy, stable - handlingValues.tractionCurveMax = 2.5; - handlingValues.tractionCurveMin = 2.0; - handlingValues.tractionLoss = 0.6; - handlingValues.suspensionForce = 1.5; - handlingValues.suspensionCompDamp = 0.6; - handlingValues.suspensionRaise = 0.12; - handlingValues.driveForce = 0.35; - handlingValues.initialDriveMaxVel = 180.0; - handlingValues.brakeForce = 1.0; - handlingValues.mass = 2000.0; - handlingValues.centreOfMassZ = 0.1; - - // Physics Emulation: Good grip, soft response - physicsEmulation.gripAssistEnabled = true; - physicsEmulation.gripAssistStrength = 1.2; - physicsEmulation.stabilityStrength = 0.8; - physicsEmulation.antiRollStrength = 0.4; // More body roll allowed - physicsEmulation.maxSpeedLimit = 50.0; - break; - - case "lowrider": - // LOWRIDER: Slow, bouncy, low, cruising style - handlingValues.tractionCurveMax = 2.0; - handlingValues.tractionCurveMin = 1.8; - handlingValues.tractionLoss = 0.8; - handlingValues.suspensionForce = 0.8; - handlingValues.suspensionCompDamp = 0.4; - handlingValues.suspensionRaise = -0.12; - handlingValues.driveForce = 0.20; - handlingValues.initialDriveMaxVel = 150.0; - handlingValues.brakeForce = 0.6; - handlingValues.mass = 1800.0; - handlingValues.centreOfMassZ = -0.15; - - // Physics Emulation: Smooth, stable, not fast - physicsEmulation.gripAssistEnabled = true; - physicsEmulation.gripAssistStrength = 1.0; - physicsEmulation.stabilityStrength = 1.2; - physicsEmulation.antiRollStrength = 0.6; - physicsEmulation.maxSpeedLimit = 42.0; // ~150 km/h - break; - } - - // Apply all values through physics emulation system - for (let param in handlingValues) { - applyHandlingValue(param, handlingValues[param]); - } - - // Update handling mods based on preset - handlingMods.acceleration = handlingValues.driveForce / 0.25; - handlingMods.topSpeed = handlingValues.initialDriveMaxVel / 200.0; - handlingMods.braking = handlingValues.brakeForce / 0.8; - handlingMods.grip = handlingValues.tractionCurveMax / 2.0; - - // Update visuals - updateAllHandlingVisuals(); - console.log("[Handling] Applied preset: " + preset + " (Physics Emulation configured)"); -} - -// Update visual feedback based on parameter -function updateHandlingVisuals(param, value) { - switch(param) { - case "tractionCurveMax": - case "tractionCurveMin": - case "tractionLoss": - // Tyre color: Red = low grip, Green = high grip - let gripFactor = Math.min(1, Math.max(0, (value - 0.5) / 4.0)); - let r = Math.floor(255 * (1 - gripFactor)); - let g = Math.floor(255 * gripFactor); - tyreGlowColors.fr = tyreGlowColors.fl = tyreGlowColors.rr = tyreGlowColors.rl = {r: r, g: g, b: 50}; - break; - case "tractionBias": - // Front vs rear tyre colors - let frontGrip = 1 - value; - let rearGrip = value; - tyreGlowColors.fr = tyreGlowColors.fl = {r: Math.floor(255 * (1 - frontGrip)), g: Math.floor(255 * frontGrip), b: 50}; - tyreGlowColors.rr = tyreGlowColors.rl = {r: Math.floor(255 * (1 - rearGrip)), g: Math.floor(255 * rearGrip), b: 50}; - break; - case "driveForce": - // Engine glow intensity based on power - engineGlow.intensity = Math.min(1, value / 0.5); - engineGlow.r = 255; - engineGlow.g = Math.floor(100 + 100 * (1 - engineGlow.intensity)); - engineGlow.b = 0; - break; - case "brakeForce": - // Brake glow - brakeGlow.intensity = Math.min(1, value / 2.0); - break; - case "suspensionRaise": - suspensionOffset = value; - break; - case "mass": - // Heavier = darker shadow/glow underneath - break; - } -} - -// Update all visuals based on current handling values -function updateAllHandlingVisuals() { - updateHandlingVisuals("tractionCurveMax", handlingValues.tractionCurveMax); - updateHandlingVisuals("driveForce", handlingValues.driveForce); - updateHandlingVisuals("brakeForce", handlingValues.brakeForce); - updateHandlingVisuals("suspensionRaise", handlingValues.suspensionRaise); -} - -// Check if we're in a handling editor submenu -function isInHandlingEditor() { - return currentMenu === "handlingEditor" || - currentMenu.startsWith("handling_"); -} - -// ============================================================================ -// HANDLING EDITOR VISUALIZATION - Clean & Informative -// ============================================================================ - -// Render the handling editor visualization -addEventHandler("OnDrawnHUD", function(event) { - // Only render when in handling editor menus and menu is open - if (!menuOpen || !isInHandlingEditor()) { - handlingEditorAnim = Math.max(0, handlingEditorAnim - 0.15); - if (handlingEditorAnim <= 0) return; - } else { - handlingEditorAnim = Math.min(1, handlingEditorAnim + 0.12); - } - - let theme = getTheme(); - let alpha = Math.floor(255 * handlingEditorAnim); - - // Panel position (right side of screen, below status panel) - let panelX = 500; - let panelY = 120; - let panelW = 380; - let panelH = 520; - - // Slide in animation from right - let slideOffset = (1 - handlingEditorAnim) * 150; - panelX += slideOffset; - - // ===== PANEL BACKGROUND ===== - let bgColor = toColour(15, 15, 20, Math.floor(235 * handlingEditorAnim)); - drawRect(panelX, panelY, panelW, panelH, bgColor); - - // Border - let borderColor = toColour(theme.primary.r, theme.primary.g, theme.primary.b, Math.floor(alpha * 0.8)); - drawRect(panelX, panelY, panelW, 2, borderColor); - drawRect(panelX, panelY + panelH - 2, panelW, 2, borderColor); - drawRect(panelX, panelY, 2, panelH, borderColor); - drawRect(panelX + panelW - 2, panelY, 2, panelH, borderColor); - - // ===== HEADER ===== - let headerBg = toColour(25, 25, 35, alpha); - drawRect(panelX + 2, panelY + 2, panelW - 4, 35, headerBg); - - let titleColor = toColour(255, 255, 255, alpha); - drawText("HANDLING TUNER", panelX + 12, panelY + 10, titleColor, 16); - - // Vehicle status - let statusText = "NO VEHICLE"; - let statusColor = toColour(255, 80, 80, alpha); - if (localPlayer && localPlayer.vehicle) { - statusText = "ACTIVE"; - statusColor = toColour(80, 255, 80, alpha); - } - drawText(statusText, panelX + panelW - 70, panelY + 12, statusColor, 11); - - // ===== TOP-DOWN CAR DIAGRAM ===== - let carCenterX = panelX + panelW / 2; - let carCenterY = panelY + 130; - let carW = 70; - let carH = 120; - let carX = carCenterX - carW / 2; - let carY = carCenterY - carH / 2; - - // Suspension height visual offset (subtle) - let suspOffset = suspensionOffset * 30; - carY += suspOffset; - - // Car body outline - let bodyOutline = toColour(60, 60, 70, alpha); - drawRect(carX - 1, carY - 1, carW + 2, carH + 2, bodyOutline); - - // Car body - let bodyColor = toColour(50, 50, 60, alpha); - drawRect(carX, carY, carW, carH, bodyColor); - - // Windshield - let glassColor = toColour(80, 100, 120, alpha); - drawRect(carX + 10, carY + 25, carW - 20, 25, glassColor); - drawRect(carX + 12, carY + carH - 35, carW - 24, 18, glassColor); - - // ===== WHEELS WITH GRIP INDICATOR ===== - let wheelW = 18; - let wheelH = 30; - let wheels = [ - { x: carX - wheelW + 3, y: carY + 12, color: tyreGlowColors.fl, label: "FL" }, - { x: carX + carW - 3, y: carY + 12, color: tyreGlowColors.fr, label: "FR" }, - { x: carX - wheelW + 3, y: carY + carH - 12 - wheelH, color: tyreGlowColors.rl, label: "RL" }, - { x: carX + carW - 3, y: carY + carH - 12 - wheelH, color: tyreGlowColors.rr, label: "RR" } - ]; - - for (let i = 0; i < wheels.length; i++) { - let w = wheels[i]; - - // Wheel background - let wheelBg = toColour(25, 25, 30, alpha); - drawRect(w.x, w.y, wheelW, wheelH, wheelBg); - - // Grip indicator bar on wheel (colored based on traction) - let gripBarH = Math.floor(wheelH * 0.8); - let gripColor = toColour(w.color.r, w.color.g, w.color.b, alpha); - drawRect(w.x + 2, w.y + (wheelH - gripBarH) / 2, 4, gripBarH, gripColor); - - // Wheel outline - let wheelOutline = toColour(80, 80, 90, alpha); - drawRect(w.x, w.y, wheelW, 1, wheelOutline); - drawRect(w.x, w.y + wheelH - 1, wheelW, 1, wheelOutline); - } - - // ===== TRACTION BIAS ARROWS ===== - let biasY = carCenterY; - let frontBias = 1 - handlingValues.tractionBias; - let rearBias = handlingValues.tractionBias; - - // Front bias indicator (above car) - let frontArrowColor = toColour(100, 200, 100, Math.floor(alpha * frontBias)); - drawRect(carCenterX - 15, carY - 15, 30, 3, frontArrowColor); - drawText(Math.floor(frontBias * 100) + "%", carCenterX - 12, carY - 28, frontArrowColor, 9); - - // Rear bias indicator (below car) - let rearArrowColor = toColour(100, 200, 100, Math.floor(alpha * rearBias)); - drawRect(carCenterX - 15, carY + carH + 12, 30, 3, rearArrowColor); - drawText(Math.floor(rearBias * 100) + "%", carCenterX - 12, carY + carH + 18, rearArrowColor, 9); - - // Center of mass indicator - let comY = carCenterY + (handlingValues.centreOfMassZ * 50); - let comColor = toColour(255, 200, 0, alpha); - drawRect(carCenterX - 4, comY - 4, 8, 8, comColor); - - // ===== DATA TABLE ===== - let tableX = panelX + 15; - let tableY = panelY + 210; - let labelCol = toColour(160, 160, 170, alpha); - let valueCol = toColour(255, 255, 255, alpha); - let rowH = 22; - - // Section: Traction - let sectionColor = toColour(theme.accent.r, theme.accent.g, theme.accent.b, alpha); - drawText("TRACTION", tableX, tableY, sectionColor, 11); - tableY += 18; - - drawText("Grip Level:", tableX, tableY, labelCol, 10); - drawText(handlingValues.tractionCurveMax.toFixed(2), tableX + 140, tableY, valueCol, 10); - let gripDesc = handlingValues.tractionCurveMax < 1.5 ? "Slippery" : handlingValues.tractionCurveMax < 2.5 ? "Normal" : handlingValues.tractionCurveMax < 3.5 ? "High" : "Maximum"; - drawText(gripDesc, tableX + 200, tableY, toColour(tyreGlowColors.fr.r, tyreGlowColors.fr.g, tyreGlowColors.fr.b, alpha), 10); - tableY += rowH; - - drawText("Traction Loss:", tableX, tableY, labelCol, 10); - drawText(handlingValues.tractionLoss.toFixed(2), tableX + 140, tableY, valueCol, 10); - let lossDesc = handlingValues.tractionLoss < 0.5 ? "Stable" : handlingValues.tractionLoss < 1.0 ? "Normal" : "Loose"; - drawText(lossDesc, tableX + 200, tableY, labelCol, 10); - tableY += rowH; - - drawText("Bias (F/R):", tableX, tableY, labelCol, 10); - drawText(Math.floor(frontBias * 100) + "/" + Math.floor(rearBias * 100), tableX + 140, tableY, valueCol, 10); - tableY += rowH + 8; - - // Section: Engine - drawText("ENGINE", tableX, tableY, sectionColor, 11); - tableY += 18; - - drawText("Power:", tableX, tableY, labelCol, 10); - drawText(handlingValues.driveForce.toFixed(2), tableX + 140, tableY, valueCol, 10); - let powerPct = Math.floor((handlingValues.driveForce / 1.2) * 100); - let powerColor = powerPct > 100 ? toColour(255, 150, 0, alpha) : valueCol; - drawText(powerPct + "%", tableX + 200, tableY, powerColor, 10); - tableY += rowH; - - drawText("Top Speed:", tableX, tableY, labelCol, 10); - drawText(handlingValues.initialDriveMaxVel.toFixed(0) + " km/h", tableX + 140, tableY, valueCol, 10); - tableY += rowH; - - drawText("Brake Force:", tableX, tableY, labelCol, 10); - drawText(handlingValues.brakeForce.toFixed(2), tableX + 140, tableY, valueCol, 10); - let brakePct = Math.floor((handlingValues.brakeForce / 3.0) * 100); - drawText(brakePct + "%", tableX + 200, tableY, valueCol, 10); - tableY += rowH + 8; - - // Section: Suspension - drawText("SUSPENSION", tableX, tableY, sectionColor, 11); - tableY += 18; - - drawText("Height:", tableX, tableY, labelCol, 10); - let heightDesc = handlingValues.suspensionRaise < -0.08 ? "Slammed" : handlingValues.suspensionRaise < -0.02 ? "Low" : handlingValues.suspensionRaise < 0.02 ? "Stock" : handlingValues.suspensionRaise < 0.08 ? "Raised" : "Lifted"; - drawText(heightDesc, tableX + 140, tableY, valueCol, 10); - drawText((handlingValues.suspensionRaise >= 0 ? "+" : "") + handlingValues.suspensionRaise.toFixed(2), tableX + 200, tableY, labelCol, 10); - tableY += rowH; - - drawText("Stiffness:", tableX, tableY, labelCol, 10); - let stiffDesc = handlingValues.suspensionForce < 1.5 ? "Soft" : handlingValues.suspensionForce < 3.0 ? "Normal" : handlingValues.suspensionForce < 5.0 ? "Firm" : "Race"; - drawText(stiffDesc, tableX + 140, tableY, valueCol, 10); - drawText(handlingValues.suspensionForce.toFixed(1), tableX + 200, tableY, labelCol, 10); - tableY += rowH + 8; - - // Section: Weight - drawText("WEIGHT", tableX, tableY, sectionColor, 11); - tableY += 18; - - drawText("Mass:", tableX, tableY, labelCol, 10); - drawText(handlingValues.mass.toFixed(0) + " kg", tableX + 140, tableY, valueCol, 10); - let massDesc = handlingValues.mass < 1000 ? "Light" : handlingValues.mass < 2000 ? "Normal" : handlingValues.mass < 3500 ? "Heavy" : "Tank"; - drawText(massDesc, tableX + 200, tableY, labelCol, 10); - tableY += rowH; - - drawText("CoM Height:", tableX, tableY, labelCol, 10); - let comDesc = handlingValues.centreOfMassZ < -0.1 ? "Low" : handlingValues.centreOfMassZ < 0.1 ? "Center" : "High"; - drawText(comDesc, tableX + 140, tableY, valueCol, 10); - tableY += rowH + 8; - - // ===== LIVE VEHICLE DATA ===== - if (localPlayer && localPlayer.vehicle) { - let veh = localPlayer.vehicle; - drawText("LIVE DATA", tableX, tableY, sectionColor, 11); - tableY += 18; - - try { - let vel = veh.velocity; - let speed = Math.sqrt(vel.x * vel.x + vel.y * vel.y) * 3.6; // m/s to km/h - drawText("Speed:", tableX, tableY, labelCol, 10); - drawText(speed.toFixed(0) + " km/h", tableX + 140, tableY, toColour(0, 200, 255, alpha), 10); - tableY += rowH; - } catch(e) {} - - try { - let health = veh.health; - drawText("Health:", tableX, tableY, labelCol, 10); - let healthColor = health > 700 ? toColour(0, 255, 0, alpha) : health > 300 ? toColour(255, 200, 0, alpha) : toColour(255, 50, 50, alpha); - drawText(health.toFixed(0), tableX + 140, tableY, healthColor, 10); - } catch(e) {} - } - - // ===== FOOTER ===== - let footerY = panelY + panelH - 22; - let footerColor = toColour(100, 100, 110, alpha); - drawText("Changes apply in real-time", tableX, footerY, footerColor, 9); -}); - addEventHandler("OnResourceStart", function(event, resource) { console.log("[ModMenu] Client loaded - Press F5 to open menu"); });