Revert "fixing ers"

This reverts commit e520d9a1a3a11d830a2bab82b9161f4ddb900cad.
This commit is contained in:
KingMcDonalds
2025-11-06 00:20:51 -08:00
parent 499c7bb97b
commit c22243d4cc
9 changed files with 1102 additions and 0 deletions
Binary file not shown.
@@ -0,0 +1 @@
Make sure the resource name is jraxion_handlingeditor, it will break the resource if you rename it.
Binary file not shown.
+424
View File
@@ -0,0 +1,424 @@
Config = {}
-- Make sure the resource name is jraxion_handlingeditor, it will break the resource if you rename it.
Config.isAllowed = function(source) --server side. most frameworks already have this.
-- if you know what you are doing, edit server/server.lua to change the permission.
if IsPlayerAceAllowed(source, 'admin') or IsPlayerAceAllowed(source, 'command') then
return true
end
return false
end
Config.Precision = 10000.0
Config.Values = {
-- Physical
fMass = {
name = "Vehicle Mass",
type = "float",
change = 100.0,
min = 100.0,
max = 5000.0,
description = "Vehicle weight in kilograms. Affects collision physics."
},
fInitialDragCoeff = {
name = "Drag Coefficient",
type = "float",
change = 1,
min = 10.0,
max = 120.0,
description = "Aerodynamic drag coefficient. Higher values increase air resistance."
},
fPopUpLightRotation = {
name = "Pop-up Light Rotation",
type = "float",
change = 1.0,
min = 0.0,
max = 90.0,
description = "Angle of pop-up headlights when deployed"
},
fPercentSubmerged = {
name = "Submersion Percentage",
type = "float",
change = 5.0,
min = 0.0,
max = 100.0,
description = "Percentage of vehicle that must be submerged before sinking. Default is 85%."
},
vecCentreOfMassOffset = {
name = "Centre of Mass Offset",
type = "vector",
change = 0.1,
min = -2.0,
max = 2.0,
description = "Center of mass offset from vehicle origin",
components = {
x = {name = "X Offset", description = "Left/Right offset"},
y = {name = "Y Offset", description = "Front/Back offset"},
z = {name = "Z Offset", description = "Up/Down offset"}
}
},
vecInertiaMultiplier = {
name = "Inertia Multiplier",
type = "vector",
change = 0.1,
min = 0.0,
max = 3.0,
description = "Inertia multiplier for each axis",
components = {
x = {name = "X Multiplier", description = "Roll inertia"},
y = {name = "Y Multiplier", description = "Pitch inertia"},
z = {name = "Z Multiplier", description = "Yaw inertia"}
}
},
fSeatOffsetDistX = {
name = "Seat X Offset",
type = "float",
change = 0.1,
min = -2.0,
max = 2.0,
description = "Driver seat left/right offset"
},
fSeatOffsetDistY = {
name = "Seat Y Offset",
type = "float",
change = 0.1,
min = -2.0,
max = 2.0,
description = "Driver seat front/back offset"
},
fSeatOffsetDistZ = {
name = "Seat Z Offset",
type = "float",
change = 0.1,
min = -2.0,
max = 2.0,
description = "Driver seat up/down offset"
},
-- Transmission
fDriveBiasFront = {
name = "Drive Type",
type = "float",
change = 0.05,
min = 0.0,
max = 1.0,
description = "Drive type: 0.0 = RWD, 1.0 = FWD, 0.5 = balanced 4WD"
},
nInitialDriveGears = {
name = "Number of Gears",
type = "int",
change = 1,
min = 1,
max = 8,
description = "Number of forward gears in the transmission"
},
fInitialDriveForce = {
name = "Engine Power",
type = "float",
change = 0.1,
min = 0.01,
max = 2.0,
description = "Engine power multiplier. Higher values increase acceleration."
},
fDriveInertia = {
name = "Engine Rev Speed",
type = "float",
change = 1,
min = 0.01,
max = 2.0,
description = "Engine rev speed. Higher values make engine reach redline faster."
},
fClutchChangeRateScaleUpShift = {
name = "Upshift Speed",
type = "float",
change = 0.5,
min = 0.1,
max = 10.0,
description = "Upshift speed multiplier. Higher values = faster shifts."
},
fClutchChangeRateScaleDownShift = {
name = "Downshift Speed",
type = "float",
change = 0.5,
min = 0.1,
max = 10.0,
description = "Downshift speed multiplier. Higher values = faster shifts."
},
fInitialDriveMaxFlatVel = {
name = "Top Speed",
type = "float",
change = 5.0,
min = 0.0,
max = 500.0,
description = "Top speed at redline in highest gear (multiply by 0.82 for MPH)"
},
-- Brakes
fBrakeForce = {
name = "Brake Power",
type = "float",
change = 0.2,
min = 0.01,
max = 2.0,
description = "Brake power multiplier. Higher values increase braking force."
},
fBrakeBiasFront = {
name = "Brake Balance",
type = "float",
change = 0.1,
min = 0.0,
max = 1.0,
description = "Brake distribution: 0.0 = rear only, 1.0 = front only, 0.5 = balanced"
},
fHandBrakeForce = {
name = "Handbrake Power",
type = "float",
change = 0.2,
min = 0.0,
max = 5.0,
description = "Handbrake power multiplier. Higher values increase handbrake strength."
},
-- Steering
fSteeringLock = {
name = "Steering Angle",
type = "float",
change = 5.0,
min = 10.0,
max = 70.0,
description = "Maximum steering angle. Higher values increase turn radius."
},
-- Traction
fTractionCurveMax = {
name = "Cornering Grip",
type = "float",
change = 0.1,
min = 0.1,
max = 4.0,
description = "Maximum cornering grip multiplier"
},
fTractionCurveMin = {
name = "Acceleration Grip",
type = "float",
change = 0.1,
min = 0.1,
max = 4.0,
description = "Minimum acceleration/braking grip multiplier"
},
fTractionCurveLateral = {
name = "Lateral Grip",
type = "float",
change = 1.0,
min = 0.0,
max = 4.0,
description = "Lateral grip curve shape"
},
fTractionSpringDeltaMax = {
name = "Traction Height Loss",
type = "float",
change = 0.01,
min = 0.0,
max = 0.3,
description = "Height at which vehicle loses traction"
},
fLowSpeedTractionLossMult = {
name = "Burnout Effect",
type = "float",
change = 0.1,
min = 0.0,
max = 2.0,
description = "Low speed traction loss. Higher values increase burnout effect."
},
fCamberStiffnesss = {
name = "Drift Control",
type = "float",
change = 0.01,
min = -1.0,
max = 1.0,
description = "Drift grip modifier. Positive values maintain drift angle, negative values cause oversteer."
},
fTractionBiasFront = {
name = "Traction Balance",
type = "float",
change = 0.01,
min = 0.01,
max = 0.99,
description = "Traction distribution: 0.01 = rear bias, 0.99 = front bias, 0.5 = balanced"
},
fTractionLossMult = {
name = "Surface Grip Loss",
type = "float",
change = 1.0,
min = 0.0,
max = 2.0,
description = "Surface traction loss multiplier. Higher values reduce grip on all surfaces."
},
-- Suspension
fSuspensionForce = {
name = "Suspension Force",
type = "float",
change = 0.1,
min = 0.0,
max = 5.0,
description = "Suspension spring force"
},
fSuspensionCompDamp = {
name = "Compression Damping",
type = "float",
change = 0.1,
min = 0.0,
max = 5.0,
description = "Suspension compression damping"
},
fSuspensionReboundDamp = {
name = "Rebound Damping",
type = "float",
change = 0.1,
min = 0.0,
max = 5.0,
description = "Suspension rebound damping"
},
fSuspensionUpperLimit = {
name = "Upper Limit",
type = "float",
change = 0.01,
min = 0.0,
max = 0.5,
description = "Suspension upper movement limit"
},
fSuspensionLowerLimit = {
name = "Lower Limit",
type = "float",
change = 0.01,
min = -0.5,
max = 0.0,
description = "Suspension lower movement limit"
},
fSuspensionRaise = {
name = "Ride Height",
type = "float",
change = 0.01,
min = -0.2,
max = 0.2,
description = "Vehicle ride height adjustment"
},
fSuspensionBiasFront = {
name = "Suspension Balance",
type = "float",
change = 0.01,
min = 0.0,
max = 1.0,
description = "Suspension force distribution"
},
fAntiRollBarForce = {
name = "Anti-Roll Force",
type = "float",
change = 0.1,
min = 0.0,
max = 2.0,
description = "Anti-roll bar force"
},
fAntiRollBarBiasFront = {
name = "Anti-Roll Balance",
type = "float",
change = 0.01,
min = 0.0,
max = 1.0,
description = "Anti-roll bar force distribution"
},
fRollCentreHeightFront = {
name = "Front Roll Height",
type = "float",
change = 0.01,
min = 0.0,
max = 1.0,
description = "Front roll center height"
},
fRollCentreHeightRear = {
name = "Rear Roll Height",
type = "float",
change = 0.01,
min = 0.0,
max = 1.0,
description = "Rear roll center height"
},
-- Damage
fCollisionDamageMult = {
name = "Collision Damage",
type = "float",
change = 1.0,
min = 0.0,
max = 2.0,
description = "Collision damage multiplier"
},
fWeaponDamageMult = {
name = "Weapon Damage",
type = "float",
change = 1.0,
min = 0.0,
max = 2.0,
description = "Weapon damage multiplier"
},
fDeformationDamageMult = {
name = "Visual Damage",
type = "float",
change = 1.0,
min = 0.0,
max = 2.0,
description = "Visual damage deformation multiplier"
},
fEngineDamageMult = {
name = "Engine Damage",
type = "float",
change = 1.0,
min = 0.0,
max = 2.0,
description = "Engine damage multiplier"
},
-- Misc
fPetrolTankVolume = {
name = "Fuel Capacity",
type = "float",
change = 1.0,
min = 0.0,
max = 100.0,
description = "Fuel tank capacity"
},
fOilVolume = {
name = "Oil Capacity",
type = "float",
change = 1.0,
min = 0.0,
max = 10.0,
description = "Engine oil capacity"
},
nMonetaryValue = {
name = "Vehicle Value",
type = "int",
change = 1.0,
min = 0,
max = 1000000,
description = "Vehicle monetary value"
},
strModelFlags = {
name = "Model Flags",
type = "string",
description = "Vehicle model flags"
},
strHandlingFlags = {
name = "Handling Flags",
type = "string",
description = "Vehicle handling flags"
},
strDamageFlags = {
name = "Damage Flags",
type = "string",
description = "Vehicle damage flags"
}
}
@@ -0,0 +1,39 @@
fx_version 'cerulean'
game 'gta5'
lua54 'yes'
name 'jraxion_handlingeditor'
version '1.0.0'
description 'Handling Editor'
author 'JRaxion'
url 'https://jraxion.tebex.io/'
client_scripts {
'config.lua',
'client/client.lua'
}
server_scripts {
'config.lua',
'server/server.lua'
}
ui_page 'html/index.html'
files {
'html/index.html',
'html/main.js',
'html/*.js',
'handling.meta',
}
data_file 'HANDLING_FILE' 'handling.meta'
escrow_ignore {
'config.lua',
'server/server.lua',
}
dependency '/assetpacks'
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<CHandlingDataMgr>
<HandlingData>
<Item type="CHandlingData">
<handlingName>MONSTER</handlingName>
<fMass value="4000.000000" />
<fInitialDragCoeff value="28.000000" />
<fPercentSubmerged value="85.000000" />
<vecCentreOfMassOffset x="0.000000" y="0.000000" z="0.000000" />
<vecInertiaMultiplier x="1.000000" y="1.000000" z="1.000000" />
<fDriveBiasFront value="0.200000" />
<nInitialDriveGears value="2" />
<fInitialDriveForce value="2.000000" />
<fDriveInertia value="2.000000" />
<fClutchChangeRateScaleUpShift value="10.000000" />
<fClutchChangeRateScaleDownShift value="9.541000" />
<fInitialDriveMaxFlatVel value="500.000000" />
<fBrakeForce value="0.650000" />
<fBrakeBiasFront value="0.650000" />
<fHandBrakeForce value="0.300000" />
<fSteeringLock value="26.000000" />
<fTractionCurveMax value="2.533300" />
<fTractionCurveMin value="3.517800" />
<fTractionCurveLateral value="3.340600" />
<fTractionSpringDeltaMax value="0.130000" />
<fLowSpeedTractionLossMult value="1.000000" />
<fCamberStiffnesss value="0.000000" />
<fTractionBiasFront value="0.510000" />
<fTractionLossMult value="0.400000" />
<fSuspensionForce value="2.300000" />
<fSuspensionCompDamp value="1.200000" />
<fSuspensionReboundDamp value="4.800000" />
<fSuspensionUpperLimit value="0.400000" />
<fSuspensionLowerLimit value="-0.200000" />
<fSuspensionRaise value="-0.050000" />
<fSuspensionBiasFront value="0.500000" />
<fAntiRollBarForce value="0.600000" />
<fAntiRollBarBiasFront value="0.500000" />
<fRollCentreHeightFront value="0.400000" />
<fRollCentreHeightRear value="0.390000" />
<fCollisionDamageMult value="1.000000" />
<fWeaponDamageMult value="1.000000" />
<fDeformationDamageMult value="0.800000" />
<fEngineDamageMult value="1.500000" />
<fPetrolTankVolume value="80.000000" />
<fOilVolume value="10.000000" />
<fSeatOffsetDistX value="0.000000" />
<fSeatOffsetDistY value="0.080000" />
<fSeatOffsetDistZ value="-0.200000" />
<nMonetaryValue value="500000" />
<strModelFlags>2224008</strModelFlags>
<strHandlingFlags>20568081</strHandlingFlags>
<strDamageFlags>20</strDamageFlags>
<SubHandlingData>
<Item type="" />
<Item type="" />
<Item type="" />
</SubHandlingData>
</Item>
</HandlingData>
</CHandlingDataMgr>
@@ -0,0 +1,242 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>jr-handling</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--dark-bg: rgba(26, 26, 26, 0.95);
--darker-bg: rgba(20, 20, 20, 0.95);
--accent: #3498db;
--text: #ffffff;
--text-muted: #a0a0a0;
--border: #2c2c2c;
}
* {
font-family: "Montserrat", sans-serif;
}
body {
background: none !important;
color: var(--text);
}
.handling-container {
background: none;
padding: 25px;
border-radius: 15px;
}
.modal-content {
background-color: var(--darker-bg);
border: 1px solid var(--border);
}
.modal-header {
border-bottom: 1px solid var(--border);
padding: 1.5rem;
}
.modal-header .btn {
margin: 0 8px;
padding: 8px 16px;
font-weight: 500;
}
.search-bar {
padding: 1.5rem;
margin: 0;
}
.form-control {
background-color: var(--dark-bg);
border: 1px solid var(--border);
color: var(--text);
padding: 12px;
border-radius: 8px;
}
.form-control:focus {
background-color: var(--dark-bg);
border-color: var(--accent);
color: var(--text);
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.25);
}
.maxheight {
max-height: 70vh;
overflow-y: auto;
}
.table {
color: var(--text);
border-color: var(--border);
}
.table-dark {
background-color: var(--darker-bg);
}
.btn {
font-weight: 500;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.2s ease;
}
.btn-primary {
background-color: var(--accent);
border: none;
}
.btn-primary:hover {
background-color: #2980b9;
}
.btn-secondary {
background-color: var(--dark-bg);
border: 1px solid var(--border);
}
.tooltip-inner {
max-width: 300px;
text-align: left;
background-color: var(--darker-bg);
border: 1px solid var(--border);
padding: 10px;
font-size: 0.9rem;
color: var(--text);
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--dark-bg);
border-radius: 8px;
}
::-webkit-scrollbar-thumb {
background-color: var(--border);
border-radius: 8px;
border: 2px solid var(--dark-bg);
}
::-webkit-scrollbar-thumb:hover {
background-color: var(--text-muted);
}
#exportArea {
width: 100%;
height: 200px;
margin-top: 15px;
display: none;
background-color: var(--dark-bg);
color: var(--text);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px;
}
.input-group {
gap: 8px;
}
.input-group > * {
border-radius: 8px !important;
}
.dropdown-menu {
background-color: var(--dark-bg);
border: 1px solid var(--border);
padding: 8px;
max-height: 300px;
overflow-y: auto;
}
.dropdown-item {
color: var(--text);
padding: 8px 16px;
display: flex;
align-items: center;
gap: 8px;
}
.dropdown-item:hover {
background-color: var(--darker-bg);
color: var(--text);
}
.dropdown-item input[type="checkbox"] {
accent-color: var(--accent);
}
.flag-dropdown {
width: 100%;
margin-top: 8px;
}
.flag-dropdown .btn {
width: 100%;
text-align: left;
background: var(--dark-bg);
border: 1px solid var(--border);
color: var(--text);
}
.flag-dropdown .btn:hover,
.flag-dropdown .btn:focus {
background: var(--darker-bg);
border-color: var(--accent);
}
</style>
</head>
<body data-bs-theme="dark">
<div class="modal modal-lg handling-container fade" id="open-handling-modal" tabindex="-1" aria-labelledby="handlingEditor" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="handlingLabel">XA21</h5>
<button type="button" class="btn btn-primary" onclick="exportHandling()">Export Handling</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="search-bar">
<input type="text" id="searchBar" class="form-control" placeholder="Search" onkeyup="searchVehicles()">
</div>
<div class="modal-body maxheight">
<div class="table-responsive">
<table class="table table-bordered align-middle">
<thead class="table-dark">
<tr>
<th style="width: 40%">Name</th>
<th style="width: 30%">Original</th>
<th style="width: 30%">Current</th>
</tr>
</thead>
<tbody id="handling-data-accordion">
</tbody>
</table>
</div>
<textarea id="exportArea" class="form-control" readonly></textarea>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script defer src="main.js"></script>
</body>
</html>
@@ -0,0 +1,319 @@
const modals = {
openhandlingModal: false,
};
async function closeNUI() {
openhandlingModal.hide();
await fetch(`https://jraxion_handlingeditor/close`, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
});
}
function handleUIMessage(data) {
switch (data.type) {
case "openHandlingEditor":
openEditor(data);
break;
default:
console.log("No action received.");
}
}
function main() {
window.addEventListener("message", (event) => handleUIMessage(event.data));
openhandlingModal = new bootstrap.Modal(document.getElementById("open-handling-modal"));
document.querySelectorAll(".modal").forEach((modal) => {
modal.addEventListener("hidden.bs.modal", closeNUI);
});
}
main();
document.addEventListener('keydown', function(event) {
if (event.key === "Escape") {
closeNUI();
}
});
function updateValue(input, change, key) {
let currentValue;
let newValue;
if (input.dataset.type === "string") {
currentValue = input.value;
newValue = currentValue;
} else {
currentValue = parseFloat(input.value) || 0;
newValue = currentValue + change;
const min = parseFloat(input.dataset.min);
const max = parseFloat(input.dataset.max);
newValue = Math.max(min, Math.min(max, newValue));
if (input.dataset.type === "int") {
newValue = Math.round(newValue);
} else if (input.dataset.type === "float") {
newValue = parseFloat(newValue.toFixed(6));
}
}
input.value = newValue;
const slider = input.parentElement.querySelector('input[type="range"]');
if (slider) {
slider.value = newValue;
}
$.post('https://jraxion_handlingeditor/updateHandling', JSON.stringify({
key: key,
value: newValue
}));
}
function updateFlag(checkbox, flagType) {
const dropdownMenu = checkbox.closest('.dropdown-menu');
const allFlags = Array.from(dropdownMenu.querySelectorAll('input[type="checkbox"]'));
const flagValue = allFlags.reduce((acc, flag, index) => {
return acc + (flag.checked ? Math.pow(2, index) : 0);
}, 0).toString(16).toUpperCase();
$.post('https://jraxion_handlingeditor/updateHandling', JSON.stringify({
key: flagType,
value: flagValue
}));
const input = dropdownMenu.parentElement.previousElementSibling.querySelector('input[type="text"]');
input.value = flagValue;
const selectedCount = allFlags.filter(f => f.checked).length;
const dropdownButton = dropdownMenu.previousElementSibling;
dropdownButton.textContent = `Selected Flags (${selectedCount})`;
}
function updateFromSlider(slider) {
const input = slider.parentElement.querySelector('input[type="text"]');
const key = input.dataset.handlingKey;
const newValue = parseFloat(slider.value);
if (input.dataset.type === "int") {
input.value = Math.round(newValue);
} else {
input.value = parseFloat(newValue.toFixed(6));
}
$.post('https://jraxion_handlingeditor/updateHandling', JSON.stringify({
key: key,
value: input.value
}));
}
function exportHandling() {
const vehicleName = document.querySelector("#handlingLabel").innerHTML;
const rows = document.getElementById('handling-data-accordion').getElementsByTagName('tr');
const xmlHeader = '<?xml version="1.0" encoding="UTF-8"?>\n\n';
const xmlStart = '<CHandlingDataMgr>\n <HandlingData>\n <Item type="CHandlingData">\n';
const xmlEnd = ' </Item>\n </HandlingData>\n</CHandlingDataMgr>';
let xmlContent = ` <handlingName>${vehicleName}</handlingName>\n`;
const handlingValues = {};
Array.from(rows).forEach(row => {
const input = row.querySelector('input[type="text"]');
if (!input) return;
const key = input.dataset.handlingKey;
const value = input.value;
switch(input.dataset.type) {
case "float":
handlingValues[key] = parseFloat(value).toFixed(6);
break;
case "int":
handlingValues[key] = Math.round(value);
break;
case "string":
handlingValues[key] = value;
break;
}
});
handlingValues['vecCentreOfMassOffset'] = '0.000000 0.000000 0.000000';
handlingValues['vecInertiaMultiplier'] = '1.000000 1.000000 1.000000';
const propertyOrder = [
'fMass', 'fInitialDragCoeff', 'fPercentSubmerged',
'vecCentreOfMassOffset', 'vecInertiaMultiplier', 'fDriveBiasFront',
'nInitialDriveGears', 'fInitialDriveForce', 'fDriveInertia',
'fClutchChangeRateScaleUpShift', 'fClutchChangeRateScaleDownShift',
'fInitialDriveMaxFlatVel', 'fBrakeForce', 'fBrakeBiasFront',
'fHandBrakeForce', 'fSteeringLock', 'fTractionCurveMax',
'fTractionCurveMin', 'fTractionCurveLateral', 'fTractionSpringDeltaMax',
'fLowSpeedTractionLossMult', 'fCamberStiffnesss', 'fTractionBiasFront',
'fTractionLossMult', 'fSuspensionForce', 'fSuspensionCompDamp',
'fSuspensionReboundDamp', 'fSuspensionUpperLimit', 'fSuspensionLowerLimit',
'fSuspensionRaise', 'fSuspensionBiasFront', 'fAntiRollBarForce',
'fAntiRollBarBiasFront', 'fRollCentreHeightFront', 'fRollCentreHeightRear',
'fCollisionDamageMult', 'fWeaponDamageMult', 'fDeformationDamageMult',
'fEngineDamageMult', 'fPetrolTankVolume', 'fOilVolume', 'fSeatOffsetDistX',
'fSeatOffsetDistY', 'fSeatOffsetDistZ', 'nMonetaryValue', 'strModelFlags',
'strHandlingFlags', 'strDamageFlags', 'AIHandling'
];
propertyOrder.forEach(key => {
if (handlingValues[key] !== undefined) {
if (key.startsWith('vec')) {
const [x, y, z] = handlingValues[key].split(' ');
xmlContent += ` <${key} x="${x}" y="${y}" z="${z}" />\n`;
} else if (key === 'strModelFlags' || key === 'strHandlingFlags' || key === 'strDamageFlags') {
xmlContent += ` <${key}>${parseInt(handlingValues[key]).toString(16).toUpperCase()}</${key}>\n`;
} else {
xmlContent += ` <${key} value="${handlingValues[key]}" />\n`;
}
}
});
const subHandlingData = ` <SubHandlingData>
<Item type="" />
<Item type="" />
<Item type="" />
</SubHandlingData>\n`;
const finalXml = xmlHeader + xmlStart + xmlContent + subHandlingData + xmlEnd;
const exportArea = document.getElementById('exportArea');
exportArea.value = finalXml;
exportArea.style.display = 'block';
exportArea.select();
document.execCommand('copy');
}
const flagDefinitions = {
strModelFlags: [
"IS_VAN", "IS_BUS", "IS_LOW", "IS_BIG", "ABS_STD", "ABS_OPTION",
"ABS_ALT_STD", "ABS_ALT_OPTION", "NO_DOORS", "TANDEM_SEATS",
"SIT_IN_BOAT", "HAS_TRACKS", "NO_EXHAUST", "DOUBLE_EXHAUST",
"NO1FPS_LOOK_BEHIND", "CAN_ENTER_IF_NO_DOOR", "AXLE_F_TORSION",
"AXLE_F_SOLID", "AXLE_F_MCPHERSON", "ATTACH_PED_TO_BODYSHELL",
"AXLE_R_TORSION", "AXLE_R_SOLID", "AXLE_R_MCPHERSON",
"DONT_FORCE_GRND_CLEARANCE", "DONT_RENDER_STEER", "NO_WHEEL_BURST",
"INDESTRUCTIBLE", "DOUBLE_FRONT_WHEELS", "FORCE_FRONT_WHEEL_WIDTH",
"FORCE_REAR_WHEEL_WIDTH"
],
strHandlingFlags: [
"SMOOTH_COMPRESN", "REDUCED_MOD_MASS", "DISTRIB_FRONT_MASS",
"DISTRIB_REAR_MASS", "OFFROAD_ABILITY", "OFFROAD_ABILITY2",
"HALOGEN_LIGHTS", "PROC_REARWHEEL_1ST", "USE_MAXSP_LIMIT",
"LOW_RIDER", "STREET_RACER", "SWINGING_CHASSIS", "EXTREME_GRIP",
"INCREASED_GRAVITY", "VERTICAL_FLIGHT_MODE", "DISABLE_GROUND_CLEARANCE",
"RACING_HANDLING", "EMERGENCY_VEHICLE", "NO_HANDBRAKE",
"CVT", "ALT_EXT_WHEEL_BOUNDS_BEH", "DONT_RAISE_BOUNDS_AT_SPEED",
"NARROW_BOUNDS", "MEDIUM_BOUNDS", "WIDE_BOUNDS"
],
strDamageFlags: [
"DRIVER_SIDE_FRONT_DOOR", "DRIVER_SIDE_REAR_DOOR", "DRIVER_PASSENGER_SIDE_FRONT_DOOR",
"DRIVER_PASSENGER_SIDE_REAR_DOOR", "BONNET", "BOOT", "DRIVER_SIDE_FRONT_WINDOW",
"DRIVER_SIDE_REAR_WINDOW", "PASSENGER_SIDE_FRONT_WINDOW", "PASSENGER_SIDE_REAR_WINDOW",
"WINDSCREEN", "ENGINE", "ALL_WHEELS", "PETROL_TANK", "DOORS", "TRUNK", "WINDOWS",
"ALL_PANELS", "WHEELS", "EXHAUST", "RADIATOR", "SUSPENSION", "BRAKES", "GEARBOX",
"STEERING", "HEADLIGHTS", "TAILLIGHTS", "INDICATORS", "WIPERS", "MIRRORS"
]
};
function openEditor(data) {
document.querySelector("#handlingLabel").innerHTML = data.vehicleName;
let accordionContent = "";
const sortedEntries = Object.entries(data.data).sort((a, b) => a[0].localeCompare(b[0]));
for (let [i, dd] of sortedEntries) {
if (i.startsWith('vec')) {
continue;
}
const isNumeric = dd.type === "float" || dd.type === "int";
const isFlag = i === "strModelFlags" || i === "strHandlingFlags" || i === "strDamageFlags";
accordionContent += `
<tr>
<td>${dd.name}
<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="${dd.description}">
i
</button>
${isNumeric ? `<small class="d-block text-muted">Min: ${dd.min} | Max: ${dd.max}</small>` : ''}
</td>
<td>${data.originalvalues[i]}</td>
<td>
<div class="input-group">
${isNumeric ? `<button class="btn btn-outline-secondary" type="button" onclick="updateValue(this.parentElement.querySelector('input[type=text]'), -${dd.change}, '${i}')">-</button>` : ''}
<input type="text"
class="form-control"
value="${data.values[i]}"
data-min="${dd.min || 0}"
data-max="${dd.max || 0}"
data-type="${dd.type}"
data-handling-key="${i}"
onchange="updateValue(this, 0, '${i}')"
>
${isNumeric ? `<button class="btn btn-outline-secondary" type="button" onclick="updateValue(this.parentElement.querySelector('input[type=text]'), ${dd.change}, '${i}')">+</button>` : ''}
${isNumeric && !isFlag ? `
<input type="range"
class="form-range"
min="${dd.min}"
max="${dd.max}"
step="${dd.type === 'int' ? '1' : '0.000001'}"
value="${data.values[i]}"
oninput="updateFromSlider(this)"
>
` : ''}
</div>
${isFlag ? `
<div class="flag-dropdown dropdown">
<button class="btn dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Selected Flags (${(parseInt(data.values[i], 16).toString(2).match(/1/g) || []).length})
</button>
<div class="dropdown-menu">
${flagDefinitions[i].map((flag, index) => `
<div class="dropdown-item">
<input type="checkbox"
id="${i}_${index}"
${(parseInt(data.values[i], 16) & (1 << index)) ? 'checked' : ''}
onchange="updateFlag(this, '${i}')"
>
<label for="${i}_${index}">${flag}</label>
</div>
`).join('')}
</div>
</div>
` : ''}
</td>
</tr>
`;
}
document.querySelector("#handling-data-accordion").innerHTML = accordionContent;
openhandlingModal.show();
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
}
function searchVehicles() {
const searchInput = document.getElementById('searchBar').value.toLowerCase();
const rows = document.getElementById('handling-data-accordion').getElementsByTagName('tr');
for (let row of rows) {
const vehicleName = row.getElementsByTagName('td')[0].textContent.toLowerCase();
if (vehicleName.includes(searchInput)) {
row.style.display = '';
} else {
row.style.display = 'none';
}
}
}
@@ -0,0 +1,15 @@
while GetCurrentResourceName() ~= "jraxion_handlingeditor" do
Wait(2000)
print("Make sure the resource name is jraxion_handlingeditor, it will break the resource if you rename it.")
end
RegisterCommand('handlingeditor', function(source, args, rawCommand)
-- Add your permission system here.
if Config.isAllowed(source) then
TriggerClientEvent('jraxion_handlingeditor:openHandlingEditor', source)
else
print('No permission')
end
end, false)