Revert "fixing ers"
This reverts commit e520d9a1a3a11d830a2bab82b9161f4ddb900cad.
This commit is contained in:
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.
@@ -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)
|
||||
Reference in New Issue
Block a user