├── html └── src │ ├── images │ ├── template.png │ ├── Ellipse 687.png │ ├── policelogo.png │ ├── Ellipse 3.svg │ └── Ellipse 687.svg │ ├── fonts │ ├── Gilroy-Bold.ttf │ ├── Gilroy-Medium.ttf │ ├── Gilroy-Regular.ttf │ ├── Gilroy-Semibold.ttf │ └── Gilroy-Extrabold.ttf │ ├── style │ ├── Heading-Pro-Wide-Heavy-Italic-trial.ttf │ └── style.css │ └── scripts │ ├── main.js │ └── vuex.global.js ├── fxmanifest.lua ├── server ├── functions.lua ├── mode.lua ├── main.lua └── presets.lua ├── tuning.sql ├── client ├── presets.lua ├── main.lua ├── mode.lua └── functions.lua ├── README.md └── shared ├── locales.lua └── config.lua /html/src/images/template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lod-Resources/ld-tunertablet/HEAD/html/src/images/template.png -------------------------------------------------------------------------------- /html/src/fonts/Gilroy-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lod-Resources/ld-tunertablet/HEAD/html/src/fonts/Gilroy-Bold.ttf -------------------------------------------------------------------------------- /html/src/fonts/Gilroy-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lod-Resources/ld-tunertablet/HEAD/html/src/fonts/Gilroy-Medium.ttf -------------------------------------------------------------------------------- /html/src/images/Ellipse 687.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lod-Resources/ld-tunertablet/HEAD/html/src/images/Ellipse 687.png -------------------------------------------------------------------------------- /html/src/images/policelogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lod-Resources/ld-tunertablet/HEAD/html/src/images/policelogo.png -------------------------------------------------------------------------------- /html/src/fonts/Gilroy-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lod-Resources/ld-tunertablet/HEAD/html/src/fonts/Gilroy-Regular.ttf -------------------------------------------------------------------------------- /html/src/fonts/Gilroy-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lod-Resources/ld-tunertablet/HEAD/html/src/fonts/Gilroy-Semibold.ttf -------------------------------------------------------------------------------- /html/src/fonts/Gilroy-Extrabold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lod-Resources/ld-tunertablet/HEAD/html/src/fonts/Gilroy-Extrabold.ttf -------------------------------------------------------------------------------- /html/src/style/Heading-Pro-Wide-Heavy-Italic-trial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lod-Resources/ld-tunertablet/HEAD/html/src/style/Heading-Pro-Wide-Heavy-Italic-trial.ttf -------------------------------------------------------------------------------- /html/src/images/Ellipse 3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /html/src/images/Ellipse 687.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | game 'gta5' 3 | Version '1.1.5' 4 | author 'l0d.' 5 | 6 | shared_scripts { 7 | 'shared/config.lua', 8 | 'shared/locales.lua', 9 | '@ox_lib/init.lua', 10 | } 11 | 12 | client_scripts { 13 | 'client/*.*', 14 | 'shared/config.lua', 15 | } 16 | 17 | server_scripts { 18 | '@oxmysql/lib/MySQL.lua', 19 | 'server/*.*', 20 | 'shared/config.lua', 21 | } 22 | 23 | ui_page "html/index.html" 24 | 25 | files { 26 | 'html/index.html', 27 | 'html/src/fonts/*.*', 28 | 'html/src/images/*.*', 29 | 'html/src/scripts/*.*', 30 | 'html/src/style/*.*', 31 | } 32 | 33 | escrow_ignore { 34 | 'shared/config.lua', 35 | 'shared/locales.lua' 36 | } 37 | 38 | 39 | lua54 'yes' 40 | 41 | -------------------------------------------------------------------------------- /server/functions.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | function _L(key, ...) 4 | local lang = Config.Language or "en" 5 | local result = Locales.Languages[lang][key] or Locales.Languages["en"][key] or key 6 | 7 | 8 | local args = {...} 9 | if #args > 0 then 10 | for i, v in ipairs(args) do 11 | result = result:gsub("{" .. (i-1) .. "}", tostring(v)) 12 | end 13 | end 14 | 15 | return result 16 | end 17 | 18 | 19 | function ExecuteSQL(query, params) 20 | local IsBusy = true 21 | local result = nil 22 | exports.oxmysql:execute(query, params, function(data) 23 | result = data 24 | IsBusy = false 25 | end) 26 | while IsBusy do 27 | Citizen.Wait(0) 28 | end 29 | return result 30 | end 31 | 32 | 33 | -------------------------------------------------------------------------------- /tuning.sql: -------------------------------------------------------------------------------- 1 | -- -------------------------------------------------------- 2 | -- Sunucu: 127.0.0.1 3 | -- Sunucu sürümü: 10.4.28-MariaDB - mariadb.org binary distribution 4 | -- Sunucu İşletim Sistemi: Win64 5 | -- HeidiSQL Sürüm: 11.3.0.6295 6 | -- -------------------------------------------------------- 7 | 8 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 9 | /*!40101 SET NAMES utf8 */; 10 | /*!50503 SET NAMES utf8mb4 */; 11 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 12 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 13 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 14 | 15 | -- tablo yapısı dökülüyor qbcoreframework_c53310.ld_tuning 16 | CREATE TABLE IF NOT EXISTS `ld_tuning` ( 17 | `id` int(11) NOT NULL AUTO_INCREMENT, 18 | `plate` varchar(50) DEFAULT NULL, 19 | `defaultData` longtext DEFAULT NULL, 20 | `currentData` longtext DEFAULT NULL, 21 | `activeData` longtext DEFAULT NULL, 22 | `tunerChip` int(11) DEFAULT 0, 23 | KEY `id` (`id`) 24 | ) ENGINE=InnoDB AUTO_INCREMENT=165 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; 25 | 26 | -- qbcoreframework_c53310.ld_tuning: ~0 rows (yaklaşık) tablosu için veriler indiriliyor 27 | /*!40000 ALTER TABLE `ld_tuning` DISABLE KEYS */; 28 | /*!40000 ALTER TABLE `ld_tuning` ENABLE KEYS */; 29 | 30 | /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; 31 | /*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */; 32 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 33 | /*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */; 34 | -------------------------------------------------------------------------------- /server/mode.lua: -------------------------------------------------------------------------------- 1 | RegisterServerEvent('ld-tunertablet:CreateTableData', function(plate, data, dataName) 2 | if dataName == "IsSportModOn" then 3 | IsSportModOn[plate] = true 4 | oldSportModData[plate] = data 5 | elseif dataName == "DriftMode" then 6 | DriftMode[plate] = data 7 | elseif dataName == "TuningData" then 8 | TuningData[plate] = data 9 | elseif dataName == "CurrentVehicleData" then 10 | CurrentVehicleData[plate] = data 11 | elseif dataName == "CloseSportMod" then 12 | IsSportModOn[plate] = false 13 | elseif dataName == "NormalMod" then 14 | IsSportModOn[plate] = false 15 | DriftMode[plate] = false 16 | end 17 | end) 18 | 19 | RegisterServerEvent('ld-tunertablet:OpenNormalMod') 20 | AddEventHandler('ld-tunertablet:OpenNormalMod', function(plate) 21 | IsSportModOn[plate] = false 22 | DriftMode[plate] = false 23 | end) 24 | 25 | Citizen.CreateThread(function() 26 | lib.callback.register('ld-tunertablet:GetData', function(source, cb, plate, dataName) 27 | if dataName == "TuningData" then 28 | cb(TuningData[plate]) 29 | elseif dataName == "CurrentVehicleData" then 30 | while not (CurrentVehicleData[plate]) do 31 | Citizen.Wait(0) 32 | end 33 | cb(CurrentVehicleData[plate]) 34 | elseif dataName == "IsSportModOn" then 35 | cb(IsSportModOn[plate]) 36 | elseif dataName == "DriftMode" then 37 | cb(DriftMode[plate]) 38 | elseif dataName == "exOldData" then 39 | cb(IsSportModOn[plate], oldSportModData[plate]) 40 | elseif dataName == "ActiveData" then 41 | while not (ActiveMode[plate]) do 42 | Citizen.Wait(0) 43 | end 44 | cb(ActiveMode[plate]) 45 | end 46 | end) 47 | end) 48 | 49 | 50 | -------------------------------------------------------------------------------- /client/presets.lua: -------------------------------------------------------------------------------- 1 | -- Presets 2 | 3 | RegisterNUICallback("GET_CUSTOMS_DATA", function(data) 4 | local ped = PlayerPedId() 5 | local vehicle = GetVehiclePedIsUsing(ped) 6 | if vehicle == 0 then 7 | lib.notify({title = _L("error"), description = _L("no_vehicle_found"), type = 'error'}) 8 | return 9 | end 10 | local plate = GetVehicleNumberPlateText(vehicle) 11 | local dataName = "currentData" 12 | lib.callback('ld-tunertablet:sqlDta', false, function(Datalar) 13 | SendNUIMessage({ 14 | action = "GET_CURRENT_DATA", 15 | GetCurrentData = Datalar or {}, 16 | }) 17 | end, plate, dataName) 18 | end) 19 | 20 | RegisterNUICallback("DELETE_PRESET", function(data) 21 | local ped = PlayerPedId() 22 | local vehicle = GetVehiclePedIsUsing(ped) 23 | local plate = GetVehicleNumberPlateText(vehicle) 24 | 25 | TriggerServerEvent("ld-tunertablet:deleteCurrentData", data, plate) 26 | lib.notify({title = _L("success"), description = _L("preset_deleted"), type = 'success'}) 27 | end) 28 | 29 | 30 | RegisterNUICallback("GET_XML_DATA", function(data) 31 | local ped = PlayerPedId() 32 | local vehicle = GetVehiclePedIsUsing(ped) 33 | local plate = GetVehicleNumberPlateText(vehicle) 34 | if not data or not data.vehicleData then 35 | lib.notify({title = _L("xml_error"), description = _L("invalid_vehicle_data"), type = 'error'}) 36 | return 37 | end 38 | if not data.vehicleData.AdvancedConfigurationData then 39 | lib.notify({title = _L("xml_data"), description = _L("loaded_config"), type = 'success'}) 40 | return 41 | end 42 | setAdvancedData(vehicle, data, true, nil) 43 | 44 | TriggerServerEvent("ld-tunertablet:ActiveModeData", plate, data.vehicleData.AdvancedConfigurationData, false) 45 | lib.notify({title = _L("success"), description = _L("xml_data"), type = 'success'}) 46 | end) 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /html/src/style/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Gilroy-Light"; 3 | src: url("../fonts/Gilroy-Light.ttf") format("opentype"); 4 | } 5 | @font-face { 6 | font-family: "Gilroy-Regular"; 7 | src: url("../fonts/Gilroy-Regular.ttf") format("opentype"); 8 | } 9 | @font-face { 10 | font-family: "Gilroy-Medium"; 11 | src: url("../fonts/Gilroy-Medium.ttf") format("opentype"); 12 | } 13 | @font-face { 14 | font-family: "Gilroy-Bold"; 15 | src: url("../fonts/Gilroy-Bold.ttf") format("opentype"); 16 | } 17 | 18 | @font-face { 19 | font-family: "Gilroy-Semibold"; 20 | src: url("../fonts/Gilroy-Semibold.ttf") format("opentype"); 21 | } 22 | @font-face { 23 | font-family: "Gilroy-Thin"; 24 | src: url("../fonts/Gilroy-Thin.otf") format("opentype"); 25 | } 26 | 27 | *{ 28 | transition: all 0.3s ease; 29 | } 30 | 31 | body { 32 | overflow: hidden; 33 | } 34 | ::-webkit-scrollbar-track { border-radius: 0px; } ::-webkit-scrollbar { width: 0px; height: 0px; } ::-webkit-scrollbar-thumb { border-radius: 0px; background-color: transparent; } 35 | 36 | 37 | 38 | .group:hover .svg-menu path { 39 | fill-opacity: 50; 40 | animation: bounce-right 1s infinite; 41 | } 42 | @keyframes bounce-right { 43 | 0%, 100% { 44 | transform: translateX(-3px); 45 | } 46 | 50% { 47 | transform: translateX(3px); 48 | } 49 | } 50 | @keyframes bounce-right { 51 | 0%, 100% { 52 | transform: translateX(-3px); 53 | } 54 | 50% { 55 | transform: translateX(3px); 56 | } 57 | } 58 | 59 | .range { 60 | 61 | -webkit-appearance: none; 62 | background: rgba(255,255,255,0.02); 63 | overflow: hidden; 64 | } 65 | 66 | 67 | ::-webkit-slider-thumb { 68 | -webkit-appearance: none; 69 | width: .9208vw; 70 | height: 2vh; 71 | border-radius: .1042vw; 72 | background: linear-gradient(90deg, #FFF 0%, #1C202E 100%); 73 | box-shadow: -2000px 0 0 2000px #fff; 74 | } 75 | 76 | .stt { 77 | stroke-dasharray: 0 200; 78 | } -------------------------------------------------------------------------------- /server/main.lua: -------------------------------------------------------------------------------- 1 | 2 | IsSportModOn, oldSportModData, DriftMode, TuningData, CurrentVehicleData, ActiveMode, Core = {}, {}, {}, {}, {}, {}, nil 3 | 4 | AddEventHandler('onResourceStart', function(resourceName) 5 | if (GetCurrentResourceName() ~= resourceName) then 6 | return 7 | end 8 | Wait(2000) 9 | print(("[^9LD-TUNERTABLET^7] ^3For support if you have any problems^7: https://discord.gg/P92aXCShVP ")) 10 | end) 11 | 12 | 13 | RegisterNetEvent("ld-tunertablet:createData:NewSave", function(newData, plate) 14 | local data = ExecuteSQL('SELECT * FROM `ld_tuning` WHERE plate = @plate', {["@plate"] = plate}) 15 | if next(data) == nil or next(data) == "[]" then 16 | return 17 | else 18 | local currentData = json.decode(data[1].currentData) 19 | 20 | if currentData == nil then 21 | currentData = {} 22 | end 23 | local newDataName = newData["dataname"] 24 | 25 | local newDataEntry = { 26 | Data = newData, 27 | name = newDataName 28 | } 29 | local newDataIndex = nil 30 | for index, entry in ipairs(currentData) do 31 | if entry.name == newDataName then 32 | newDataIndex = index 33 | break 34 | end 35 | end 36 | 37 | if newDataIndex then 38 | 39 | table.remove(currentData, newDataIndex) 40 | else 41 | 42 | table.insert(currentData, newDataEntry) 43 | end 44 | 45 | ExecuteSQL('UPDATE `ld_tuning` SET currentData = @currentData WHERE plate = @plate', { 46 | ["@currentData"] = json.encode(currentData), 47 | ["@plate"] = plate 48 | }, function(rowsChanged) 49 | if newDataIndex then 50 | TriggerClientEvent('ox_lib:notify', source, {title = _L('success'),description = _L('preset_removed'),type = 'success'}) 51 | else 52 | TriggerClientEvent('ox_lib:notify', source, {title = _L('success'),description = _L('preset_saved'),type = 'success'}) 53 | end 54 | end) 55 | end 56 | end) 57 | 58 | 59 | 60 | RegisterNetEvent("ld-tunertablet:createData", function(plate, vehdata) 61 | GetData = ExecuteSQL('SELECT * FROM `ld_tuning` WHERE plate = @plate', {["@plate"] = plate}) 62 | ActiveMode[plate] = vehdata 63 | if next(GetData) == nil or next(GetData) == "[]" then 64 | ExecuteSQL("INSERT INTO ld_tuning (plate, defaultData) VALUES (@plate, @defaultData)", { 65 | ['@plate'] = plate, 66 | ['@defaultData'] = json.encode(vehdata) 67 | }) 68 | 69 | ExecuteSQL("UPDATE ld_tuning SET tunerChip = @tunerChip, plate = @plate WHERE plate = @plate", { 70 | ['@plate'] = plate, 71 | ['@tunerChip'] = 1 72 | }) 73 | end 74 | end) 75 | 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ld-tunertablet 2 | ## Introduction 3 | Tuner Pro is a comprehensive vehicle customization system for FiveM servers that brings real-world tuning mechanics to your roleplay experience. This powerful script allows players to fine-tune every aspect of their vehicle’s performance, from engine output to suspension dynamics, creating truly personalized driving experiences. 4 | 5 | Whether you’re a casual driver looking to improve your vehicle’s everyday performance or a professional racer seeking that competitive edge, our Tuner Chip provides the tools you need in an intuitive, tablet-based interface. 6 | 7 | Developed with both realism and user-friendliness in mind, this script brings authentic automotive tuning to your FiveM server. 8 | 9 | https://youtu.be/h-7TRwZKgJ8 10 | 11 | ## Key Features 12 | ### :rocket: Comprehensive Customization 13 | **Engine Tuning: **Adjust power output, throttle response, and maximum speed 14 | **Transmission Configuration: **Modify gear ratios, shift speeds, and power distribution 15 | **Suspension Setup: **Fine-tune ride height, compression, and rebound for optimal handling 16 | **Brake Balance:** Perfect your braking performance with adjustable brake bias and force 17 | **Traction Control:** Dial in the perfect grip for any driving style or surface condition 18 | ### :floppy_disk: Preset System 19 | **Save & Load:** Create unlimited custom presets for different driving scenarios 20 | **Ready-Made Options:** Quick-select from pre-configured setups like Drift Mode, Sport Mode, and Eco Mode 21 | **Share Configurations:** Export and import tuning setups as XML files to share with friends 22 | ### :video_game: User-Friendly Interface 23 | **Tablet-Based Menu:** Intuitive interface accessible through a simple command (/tunertablet) 24 | **Simple & Advanced Modes:** Choose between user-friendly basic options or detailed parameter control 25 | **Visual Feedback: **Real-time visual indicators of how your changes affect vehicle performance 26 | ### :wrench: Technical Excellence 27 | **Real Handling Physics:** Modifications directly affect GTA’s native handling system for authentic results 28 | **Performance Testing:** Built-in test tool to measure and compare performance metrics 29 | **Minimal Resource Usage:** Optimized code ensures minimal impact on server performance 30 | **Compatibility:** Works with all standard GTA vehicles and most custom vehicle add-ons 31 | 32 | ## Driving Modes 33 | ### Drift Mode 34 | Transform any vehicle into a drift machine with specialized handling characteristics that make sliding around corners easy and controllable. 35 | 36 | ### Sport Mode 37 | Enhance performance with optimized power delivery, responsive throttle, and tightened handling for an aggressive driving experience. 38 | 39 | ### Eco Mode 40 | Reset your vehicle to factory settings with balanced performance characteristics for everyday driving. 41 | 42 | ## Advanced Tuning Categories 43 | The Advanced Configuration menu provides granular control over 8 distinct vehicle systems: 44 | 45 | **Aero: **Manage aerodynamic properties affecting high-speed stability 46 | **Chassis: **Adjust fundamental vehicle characteristics like weight and center of mass 47 | **Engine: **Fine-tune power delivery and response characteristics 48 | **Transmission**: Configure gear ratios and shifting behavior 49 | **Brakes**: Perfect stopping power and distribution 50 | **Traction**: Dial in grip levels for different driving styles 51 | **Suspension**: Create the ideal ride height and damping setup 52 | **Anti-roll**: Minimize body roll for sharper cornering 53 | 54 | > With over 30 adjustable parameters, you’ll have unprecedented control over your vehicle’s behavior! 55 | 56 | **Download :** https://lod.tebex.io/category/2942031 57 | 58 | # Copyright 59 | Copyright © 2025 Lod Resources https://github.com/lod9 60 | 61 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 62 | 63 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 64 | 65 | You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/. 66 | -------------------------------------------------------------------------------- /shared/locales.lua: -------------------------------------------------------------------------------- 1 | Locales = {} 2 | Locales.Languages = { 3 | ["en"] = { 4 | -- Notification Titles 5 | ["default"] = "Default active mode applied for plate:", 6 | ["success"] = "Success", 7 | ["info"] = "Info", 8 | ["warning"] = "Warning", 9 | ["tuning_tablet"] = "Tuner Tablet", 10 | ["drift_mode"] = "Drift Mode", 11 | ["sport_mode"] = "Sport Mode", 12 | ["eco_mode"] = "Eco Mode", 13 | ["normal_mode"] = "Normal Mode", 14 | ["normal_mode_error"] = "Normal Mode Error", 15 | ["settings_saved"] = "Settings Saved", 16 | ["advanced_settings"] = "Advanced Settings", 17 | ["default_settings"] = "Default Settings", 18 | ["test_tool"] = "Test Tool", 19 | ["test_tool_error"] = "Test Tool Error", 20 | ["test_started"] = "Test Started", 21 | ["test_milestone"] = "Test Milestone", 22 | ["test_complete"] = "Test Complete", 23 | ["test_reset"] = "Test Reset", 24 | ["preset_deleted"] = "Preset Deleted", 25 | ["xml_error"] = "XML Error", 26 | ["xml_data"] = "XML Data Applied", 27 | ["error"] = "Error", 28 | ["need_vehicle"] = "You need to be in a vehicle", 29 | ["need_driver_seat"] = "You must be in the driver's seat", 30 | ["mode_enabled"] = "Activated", 31 | ["mode_disabled"] = "Deactivated", 32 | ["settings_applied"] = "Settings applied successfully", 33 | ["no_previous_data"] = "No previous data found", 34 | ["restored_factory"] = "Restored factory settings", 35 | ["press_g_start"] = "Press G to start the test", 36 | ["vehicle_stationary"] = "Vehicle must be stationary", 37 | ["test_in_progress"] = "Performance test in progress", 38 | ["zero_to_sixty"] = "0-60 km/h: {0}s", 39 | ["zero_to_hundred"] = "0-100 km/h: {0}s", 40 | ["quarter_mile"] = "Quarter Mile: {0}s", 41 | ["half_mile"] = "Half Mile: {0}s", 42 | ["press_delete"] = "Press DELETE to reset the test", 43 | ["press_g_restart"] = "Press G to start a new test", 44 | ["preset_removed"] = "Successfully removed preset", 45 | ["no_vehicle_found"] = "No vehicle found!", 46 | ["invalid_vehicle_data"] = "Invalid vehicle data", 47 | ["missing_advanced_config"] = "Missing advanced configuration data", 48 | ["loaded_config"] = "Successfully loaded configuration", 49 | ["simple_settings_applied"] = "Simple settings applied successfully", 50 | ["drift_mode_on"] = "Drift Mode Activated", 51 | ["drift_mode_off"] = "Drift Mode Deactivated", 52 | ["sport_mode_on"] = "Sport Mode Activated", 53 | ["sport_mode_off"] = "Sport Mode Deactivated", 54 | ["eco_mode_on"] = "Eco Mode Activated", 55 | ["eco_mode_off"] = "Eco Mode Deactivated", 56 | ["tuning_restore"] = "Tuning Restored", 57 | ["factory_settings"] = "Factory settings restored", 58 | ["preset_saved"] = "Preset Saved", 59 | ["preset_applied"] = "Preset Applied", 60 | ["advanced_tuning"] = "Advanced Tuning", 61 | ["measure_complete"] = "Measurement Complete", 62 | ["invalid_preset"] = "Invalid Preset", 63 | ["missing_data"] = "Missing Configuration Data", 64 | ["welcome"] = "Welcome", 65 | ["ucreatedpresets"] = "Your Created Presets", 66 | ["vehiclestats"] = "Vehicle Stats", 67 | ["acceleration"] = "Acceleration", 68 | ["speed"] = "Speed", 69 | ["handling"] = "Traction", 70 | ["braking"] = "Braking", 71 | ["oiltemp"] = "Fuel Level", 72 | ["dashtemp"] = "Engine Health", 73 | ["watertemp"] = "Oil Level", 74 | ["enginetemp"] = "Engine Temp", 75 | ["confirmtext"] = "You are responsible for any modifications to your vehicle, please do not do this if you are not sure.", 76 | ["metrics"] = "Metrics", 77 | ["metricsdescription"] = "This editor makes an effort of translating some handling items to real world measurements, depicted in red on them. For transparency and ease of fact-checking, this section documents and details the math used to get the Ingame metrics.", 78 | ["ucreatedpresetsdesc"] = "You can easily access or delete the settings you have edited and saved from the specially developed configuration page." 79 | 80 | 81 | }, 82 | 83 | } -------------------------------------------------------------------------------- /server/presets.lua: -------------------------------------------------------------------------------- 1 | Citizen.CreateThread(function() 2 | 3 | lib.callback.register('ld-tunertablet:sqlDta', function(source, plate, dataName) 4 | if not dataName then 5 | -- print("dataName is nil!") 6 | return nil 7 | end 8 | 9 | local data = ExecuteSQL('SELECT * FROM `ld_tuning` WHERE plate = @plate', {["@plate"] = plate}) 10 | 11 | if data and #data > 0 then 12 | -- print("SQL Data Exists:", json.encode(data[1])) 13 | else 14 | -- print("No Data Found for plate:", plate) 15 | return nil 16 | end 17 | 18 | if dataName == "DefaultData" then 19 | return data[1].defaultData and json.decode(data[1].defaultData) or nil 20 | elseif dataName == "currentData" then 21 | return data[1].currentData and json.decode(data[1].currentData) or nil 22 | else 23 | TriggerClientEvent('ox_lib:notify', source, {title = _L('warning'),description = _L('invalid_preset'),type = 'warning'}) 24 | -- print(" Invalid dataName received:", dataName) 25 | return nil 26 | end 27 | end) 28 | end) 29 | 30 | RegisterNetEvent("ld-tunertablet:deleteCurrentData", function(jsdata, plate) 31 | local data = ExecuteSQL('SELECT * FROM `ld_tuning` WHERE plate = @plate', {["@plate"] = plate}) 32 | 33 | if not data or #data == 0 then 34 | -- TriggerClientEvent('ox_lib:notify', source, {title = _L('warning'),description = _L('invalid_preset'),type = 'warning'}) 35 | return 36 | end 37 | 38 | local currentData = data[1].currentData 39 | local jsonData = json.decode(currentData) 40 | 41 | if not jsonData or #jsonData == 0 then 42 | -- print("No presets found in currentData") 43 | return 44 | end 45 | 46 | local targetIndex = nil 47 | 48 | for idx, entry in ipairs(jsonData) do 49 | if entry.Data.dataname == jsdata.vehicleData.title then 50 | targetIndex = idx 51 | break 52 | end 53 | end 54 | 55 | if targetIndex then 56 | table.remove(jsonData, targetIndex) 57 | local newJsonData = json.encode(jsonData) 58 | 59 | ExecuteSQL('UPDATE `ld_tuning` SET currentData = @newData WHERE plate = @plate', { 60 | ["@newData"] = newJsonData, 61 | ["@plate"] = plate 62 | }, function(rowsChanged) 63 | if rowsChanged > 0 then 64 | TriggerClientEvent('ox_lib:notify', source, {title = _L('success'),description = _L('preset_deleted'),type = 'sucess'}) 65 | else 66 | print("Error updating ld_tuning.") 67 | end 68 | end) 69 | else 70 | -- print("No matching preset found for: " .. jsdata.vehicleData.title) 71 | TriggerClientEvent('ox_lib:notify', source, {title = _L('warning'),description = _L('invalid_preset'),type = 'warning'}) 72 | end 73 | end) 74 | 75 | 76 | RegisterNetEvent("ld-tunertablet:ActiveModeData", function(plate, activedata, bool) 77 | if activedata ~= nil then 78 | local activeDataToSave 79 | 80 | if bool then 81 | activeDataToSave = activedata["vehicleData"] 82 | else 83 | activeDataToSave = activedata 84 | end 85 | 86 | ExecuteSQL('UPDATE `ld_tuning` SET activeData = @activeData WHERE plate = @plate', { 87 | ['@plate'] = plate, 88 | ['@activeData'] = json.encode(activeDataToSave) 89 | }) 90 | ActiveMode[plate] = activeDataToSave 91 | else 92 | local data = ExecuteSQL('SELECT * FROM `ld_tuning` WHERE plate = @plate', {["@plate"] = plate}) 93 | if data and data[1] and data[1].defaultData then 94 | local newdata = json.decode(data[1].defaultData) 95 | ExecuteSQL('UPDATE `ld_tuning` SET activeData = @activeData WHERE plate = @plate', { 96 | ['@plate'] = plate, 97 | ['@activeData'] = json.encode(newdata) 98 | }) 99 | 100 | ActiveMode[plate] = newdata 101 | 102 | TriggerClientEvent('ox_lib:notify', source, {title = _L('success'),description = _L('default'),type = 'success'}) 103 | else 104 | print("❌ No default data found for plate:", plate) 105 | TriggerClientEvent('ox_lib:notify', source, {title = _L('warning'),description = _L('invalid_preset'),type = 'warning'}) 106 | end 107 | end 108 | end) 109 | -------------------------------------------------------------------------------- /client/main.lua: -------------------------------------------------------------------------------- 1 | 2 | driftMod, oldSportMod, SportModOn, Core = {}, {}, {}, nil 3 | 4 | 5 | function _L(key, ...) 6 | local lang = Config.Language or "en" 7 | local result = Locales.Languages[lang][key] or Locales.Languages["en"][key] or key 8 | 9 | 10 | local args = {...} 11 | if #args > 0 then 12 | for i, v in ipairs(args) do 13 | result = result:gsub("{" .. (i-1) .. "}", tostring(v)) 14 | end 15 | end 16 | 17 | return result 18 | end 19 | 20 | 21 | RegisterCommand("tunertablet", function() 22 | local ped = PlayerPedId() 23 | local vehicle = GetVehiclePedIsIn(ped, false) 24 | 25 | if vehicle == 0 then 26 | lib.notify({title = _L("tuning_tablet"), description = _L("need_vehicle"), type = 'error'}) 27 | return 28 | end 29 | 30 | 31 | if GetPedInVehicleSeat(vehicle, -1) ~= ped then 32 | lib.notify({title = _L("tuning_tablet"), description = _L("need_driver_seat"), type = 'error'}) 33 | return 34 | end 35 | 36 | OpenUI() 37 | end, false) 38 | 39 | 40 | function OpenUI() 41 | local ped = PlayerPedId() 42 | local vehicle = GetVehiclePedIsUsing(ped) 43 | local vehicleDatas = { 44 | currentVehicleName = GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)), 45 | vehicleHash = GetEntityModel(vehicle), 46 | currentVehiclePlate = GetVehicleNumberPlateText(vehicle), 47 | currentVehicleAcceleration = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fInitialDriveForce') * 10, 48 | currentVehicleBraking = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fBrakeForce') * 10, 49 | currentVehicleTraction = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fTractionCurveMax') * 10, 50 | currentVehicleTopSpeed = GetVehicleMaxSpeed(vehicle), 51 | currentVehicleEngineTemp = GetVehicleEngineTemperature(vehicle), 52 | currentVehicleFuelLevel = GetVehicleFuelLevel(vehicle), 53 | currentVehicleOilLevel = GetVehicleOilLevel(vehicle), 54 | currentVehicleHealth = GetVehicleEngineHealth(vehicle), 55 | } 56 | 57 | if IsPedInAnyVehicle(ped) then 58 | local getData = GetVehData(vehicle) 59 | TriggerServerEvent("ld-tunertablet:createData", GetVehicleNumberPlateText(vehicle), getData) 60 | SetNuiFocus(true, true) 61 | SendNUIMessage({ 62 | action = "SHOW_INTERFACE", 63 | vehicleDatas = vehicleDatas, 64 | handlingData = GetVehData(vehicle), 65 | Locales = Locales.Languages[Config.Language], 66 | AdvancedConfiguration = Config.Configurables["Advanced"], 67 | SimpleConfiguration = Config.Configurables["Simple"], 68 | readyPresets = Config.ReadyPresets, 69 | Pages = Config.Pages["Main_Pages"], 70 | subPages = Config.Pages["Sub_Pages"], 71 | }) 72 | else 73 | lib.notify({title = _L("tuning_tablet"), description = _L("need_vehicle"), type = 'error'}) 74 | end 75 | end 76 | 77 | 78 | exports('OpenUI', OpenUI) 79 | 80 | 81 | RegisterNUICallback('exit', function() 82 | SetNuiFocus(false, false) 83 | end) 84 | 85 | 86 | RegisterNUICallback("GET_ADVANCED_DATA",function() 87 | local ped = PlayerPedId() 88 | local veh = GetVehiclePedIsUsing(ped) 89 | SendNUIMessage({ 90 | action = "GET_VEHICLE_DATA", 91 | vehicleData = GetVehData(veh), 92 | }) 93 | end) 94 | 95 | RegisterNUICallback("SAVE_SIMPLE_SETTINGS", function(data) 96 | local ped = PlayerPedId() 97 | local veh = GetVehiclePedIsUsing(ped) 98 | setVehData(veh, data) 99 | TriggerServerEvent("ld-tunertablet:CreateTableData", GetVehicleNumberPlateText(veh), data, "TuningData") 100 | lib.notify({title = _L("settings_saved"), description = _L("simple_settings_applied"), type = 'success'}) 101 | end) 102 | 103 | RegisterNUICallback("SAVE_ADVANCED_SETTINGS", function(data) 104 | local ped = PlayerPedId() 105 | local veh = GetVehiclePedIsUsing(ped) 106 | local plate = GetVehicleNumberPlateText(veh) 107 | TriggerServerEvent("ld-tunertablet:createData:NewSave", data, plate, data.dataname) 108 | setAdvancedData(veh, data, false, nil) 109 | TriggerServerEvent("ld-tunertablet:ActiveModeData", plate, data, true) 110 | lib.notify({title = _L("advanced_settings"), description = _L("settings_applied"), type = 'success'}) 111 | end) 112 | 113 | RegisterNUICallback("DEFAULT_BACK", function () 114 | local ped = PlayerPedId() 115 | local veh = GetVehiclePedIsUsing(ped) 116 | local plate = GetVehicleNumberPlateText(veh) 117 | DefaultAdvancedData(veh, plate) 118 | TriggerServerEvent("ld-tunertablet:ActiveModeData", plate) 119 | lib.notify({title = _L("default_settings"), description = _L("restored_factory"), type = 'info'}) 120 | end) 121 | 122 | RegisterNUICallback("TEST_TOOL", function() 123 | while true do 124 | local playerPed = PlayerPedId() 125 | local vehicle = GetVehiclePedIsIn(playerPed, false) 126 | 127 | if vehicle == 0 then 128 | lib.notify({title = _L("test_tool_error"), description = _L("need_vehicle"), type = 'error'}) 129 | return 130 | end 131 | SetNuiFocus(false, false) 132 | 133 | lib.notify({title = _L("test_tool"), description = _L("press_g_start"), type = 'info'}) 134 | 135 | while true do 136 | Wait(0) 137 | 138 | if IsControlJustReleased(0, 194) then 139 | 140 | SendNUIMessage({ 141 | action = "TIMER", 142 | status = 2 143 | }) 144 | OpenUI() 145 | return 146 | end 147 | 148 | if GetEntitySpeed(vehicle) * 3.6 > 1 then 149 | if IsControlJustReleased(0, 47) then 150 | lib.notify({title = _L("test_tool_error"), description = _L("vehicle_stationary"), type = 'error'}) 151 | 152 | end 153 | else 154 | if IsControlJustReleased(0, 47) then 155 | break 156 | end 157 | end 158 | end 159 | 160 | local startTime = GetGameTimer() 161 | local startCoords = GetEntityCoords(vehicle) 162 | local quarterMile = 402.336 -- 163 | local halfMile = 804.672 164 | local reached100, reached60, reachedQuarter, reachedHalf = false, false, false, false 165 | local zeroToHundredTime, zeroToSixtyTime, quarterMileTime, halfMileTime 166 | SendNUIMessage({ 167 | action = "TIMER", 168 | status = 1 169 | }) 170 | lib.notify({title = _L("test_started"), description = _L("test_in_progress"), type = 'success'}) 171 | 172 | local testActive = true 173 | while testActive do 174 | Wait(0) 175 | if IsControlJustReleased(0, 194) then 176 | SendNUIMessage({ 177 | action = "TIMER", 178 | status = 2 179 | }) 180 | OpenUI() 181 | return 182 | end 183 | if IsControlJustReleased(0, 20) then 184 | SendNUIMessage({ 185 | action = "TIMER", 186 | status = 2 187 | }) 188 | lib.notify({title = _L("test_reset"), description = _L("press_g_restart"), type = 'info'}) 189 | break 190 | end 191 | local currentSpeed = GetEntitySpeed(vehicle) * 3.6 192 | local currentTime = GetGameTimer() - startTime 193 | local currentCoords = GetEntityCoords(vehicle) 194 | local distance = #(currentCoords - startCoords) 195 | 196 | local updated = false 197 | 198 | if not reached100 and currentSpeed >= 100 then 199 | zeroToHundredTime = currentTime / 1000 200 | reached100 = true 201 | updated = true 202 | lib.notify({title = _L("test_milestone"), description = _L("zero_to_hundred", zeroToHundredTime), type = 'info'}) 203 | end 204 | 205 | if not reached60 and currentSpeed >= 60 then 206 | zeroToSixtyTime = currentTime / 1000 207 | reached60 = true 208 | updated = true 209 | lib.notify({title = _L("test_milestone"), description = _L("zero_to_sixty", zeroToSixtyTime), type = 'info'}) 210 | end 211 | 212 | if not reachedQuarter and distance >= quarterMile then 213 | quarterMileTime = currentTime / 1000 214 | reachedQuarter = true 215 | updated = true 216 | lib.notify({title = _L("test_milestone"), description = _L("quarter_mile", quarterMileTime), type = 'info'}) 217 | end 218 | 219 | if not reachedHalf and distance >= halfMile then 220 | halfMileTime = currentTime / 1000 221 | reachedHalf = true 222 | updated = true 223 | lib.notify({title = _L("test_milestone"), description = _L("half_mile", halfMileTime), type = 'info'}) 224 | end 225 | 226 | if updated then 227 | SendNUIMessage({ 228 | action = "GET_TOOL", 229 | reach100 = zeroToHundredTime or "N/A", 230 | reach60 = zeroToSixtyTime or "N/A", 231 | reachquart = quarterMileTime or "N/A", 232 | reachhalf = halfMileTime or "N/A" 233 | }) 234 | end 235 | 236 | if reached100 and reached60 and reachedQuarter and reachedHalf then 237 | testActive = false 238 | end 239 | end 240 | 241 | SendNUIMessage({ 242 | action = "GET_TOOL", 243 | reach100 = zeroToHundredTime or "N/A", 244 | reach60 = zeroToSixtyTime or "N/A", 245 | reachquart = quarterMileTime or "N/A", 246 | reachhalf = halfMileTime or "N/A" 247 | }) 248 | SendNUIMessage({ 249 | action = "TIMER", 250 | status = 0 251 | }) 252 | lib.notify({title = _L("test_complete"), description = _L("press_delete"), type = 'success'}) 253 | 254 | 255 | end 256 | end) 257 | 258 | 259 | 260 | 261 | 262 | -------------------------------------------------------------------------------- /client/mode.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | RegisterNuiCallback("CHANGE_MODE", function(data) 4 | mode = data.mode 5 | local vehicle = GetVehiclePedIsIn(PlayerPedId(), false) 6 | 7 | if IsPedInAnyVehicle(PlayerPedId()) and GetPedInVehicleSeat(vehicle, -1) == PlayerPedId() then 8 | local plate = GetVehicleNumberPlateText(vehicle) 9 | 10 | if mode == "Drift Mode" then 11 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate, false, "NormalMod") 12 | SportModOn[plate] = false 13 | if not driftMod[plate] then 14 | driftMod[plate] = true 15 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate, true, "DriftMode") 16 | lib.notify({title = _L("drift_mode"), description = _L("mode_enabled"), type = 'success'}) 17 | else 18 | driftMod[plate] = false 19 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate, false, "DriftMode") 20 | lib.notify({title = _L("drift_mode"), description = _L("mode_disabled"), type = 'error'}) 21 | end 22 | elseif mode == "Sport Mode" then 23 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate, false, "NormalMod") 24 | driftMod[plate] = false 25 | if not SportModOn[plate] then 26 | SportModOn[plate] = true 27 | SportModOnFunction(vehicle, plate) 28 | lib.notify({title = _L("sport_mode"), description = _L("mode_enabled"), type = 'success'}) 29 | else 30 | SportModOn[plate] = false 31 | lib.callback('ld-tunertablet:GetData',false, function(dataone) 32 | if dataone == nil or dataone == false then 33 | SportModOnFunction(vehicle, plate) 34 | else 35 | NormalMode(vehicle, plate, oldSportMod[plate]) 36 | end 37 | end, plate, "exOldData") 38 | lib.notify({title = _L("sport_mode"), description = _L("mode_disabled"), type = 'error'}) 39 | end 40 | elseif mode == "Eco Mode" then 41 | driftMod[plate] = false 42 | SportModOn[plate] = false 43 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate, false, "NormalMod") 44 | NormalMode(vehicle, plate, oldSportMod[plate]) 45 | lib.notify({title = _L("eco_mode"), description = _L("mode_enabled"), type = 'success'}) 46 | 47 | end 48 | end 49 | end) 50 | 51 | 52 | function SportModOnFunction(vehicle, plate) 53 | local oldfInitialDriveMaxFlatVelData = GetVehicleHandlingFloat(vehicle, "CHandlingData", "fInitialDriveMaxFlatVel") 54 | local oldffDriveInertiaData = GetVehicleHandlingFloat(vehicle, "CHandlingData", "fDriveInertia") 55 | local oldfClutchChangeRateScaleUpShiftData = GetVehicleHandlingFloat(vehicle, "CHandlingData", "fClutchChangeRateScaleUpShift") 56 | local oldfClutchChangeRateScaleDownShiftData = GetVehicleHandlingFloat(vehicle, "CHandlingData", "fClutchChangeRateScaleDownShift") 57 | oldSportMod[plate] = { 58 | oldfInitialDriveMaxFlatVel = oldfInitialDriveMaxFlatVelData, 59 | oldffDriveInertia = oldfInitialDriveMaxFlatVelData, 60 | oldfClutchChangeRateScaleUpShift = oldfInitialDriveMaxFlatVelData, 61 | oldfClutchChangeRateScaleDownShift = oldfInitialDriveMaxFlatVelData, 62 | } 63 | 64 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fInitialDriveMaxFlatVel', oldfInitialDriveMaxFlatVelData + (oldfInitialDriveMaxFlatVelData / Config.Modes["SportModeSettings"]["fInitialDriveMaxFlatVel"])) 65 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fDriveInertia', Config.Modes["SportModeSettings"]["fDriveInertia"]) 66 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleUpShift', oldfClutchChangeRateScaleUpShiftData * Config.Modes["SportModeSettings"]["fClutchChangeRateScaleUpShift"]) 67 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleDownShift', oldfClutchChangeRateScaleDownShiftData * Config.Modes["SportModeSettings"]["fClutchChangeRateScaleDownShift"]) 68 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate, oldSportMod[plate], "IsSportModOn") 69 | lib.notify({title = _L("sport_mode"), description = _L("settings_applied"), type = 'success'}) 70 | end 71 | 72 | function NormalMode(vehicle, plate, olddata) 73 | if olddata == nil then 74 | lib.notify({title = _L("normal_mode_error"), description = _L("no_previous_data"), type = 'error'}) 75 | return 76 | else 77 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fInitialDriveMaxFlatVel', olddata.oldfInitialDriveMaxFlatVel) 78 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fDriveInertia', olddata.oldffDriveInertia) 79 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleUpShift', olddata.oldfClutchChangeRateScaleUpShift) 80 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleDownShift', olddata.oldfClutchChangeRateScaleDownShift) 81 | end 82 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate , false, "CloseSportMod") 83 | lib.notify({title = _L("normal_mode"), description = _L("settings_applied"), type = 'success'}) 84 | end 85 | 86 | local lastVehicle = nil 87 | Citizen.CreateThread(function() 88 | Citizen.Wait(1000) 89 | 90 | while true do 91 | Citizen.Wait(1000) 92 | 93 | local vehicle = GetVehiclePedIsIn(PlayerPedId(), false) 94 | 95 | if IsPedInAnyVehicle(PlayerPedId()) and GetPedInVehicleSeat(vehicle, -1) == PlayerPedId() then 96 | local plate = GetVehicleNumberPlateText(vehicle) 97 | 98 | if vehicle ~= lastVehicle then 99 | lastVehicle = vehicle 100 | Wait(200) 101 | local activebool = false 102 | lib.callback('ld-tunertablet:GetData',false, function(activedata) 103 | Wait(1000) 104 | if activedata ~= nil then 105 | local ped = PlayerPedId() 106 | local veh = GetVehiclePedIsUsing(ped) 107 | activebool = true 108 | setAdvancedData(veh, activedata, nil, true) 109 | end 110 | end, plate, "ActiveData") 111 | Wait(200) 112 | 113 | lib.callback('ld-tunertablet:GetData',false, function(driftModData) 114 | Wait(1000) 115 | if driftModData == true then 116 | if driftMod[plate] == false or driftMod[plate] == nil then 117 | driftMod[plate] = true 118 | end 119 | elseif driftModData == false then 120 | if driftMod[plate] == true or driftMod[plate] == nil then 121 | driftMod[plate] = false 122 | end 123 | end 124 | end, plate, "DriftMode") 125 | 126 | Wait(200) 127 | lib.callback('ld-tunertablet:GetData',false, function(SportModOnData) 128 | Wait(1000) 129 | if SportModOnData == true then 130 | if SportModOn[plate] == false or SportModOn[plate] == nil then 131 | SportModOn[plate] = true 132 | end 133 | elseif SportModOnData == false then 134 | if SportModOn[plate] == true then 135 | lib.callback('ld-tunertablet:GetData',false, function(SportModeBool, olddata) 136 | if SportModeBool == nil or SportModeBool == true then 137 | if oldSportMod[plate] == nil or oldSportMod[plate] == false then 138 | olddata = olddata 139 | else 140 | olddata = oldSportMod[plate] 141 | end 142 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fInitialDriveMaxFlatVel', olddata.oldfInitialDriveMaxFlatVel) 143 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fDriveInertia', olddata.oldffDriveInertia) 144 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleUpShift', olddata.oldfClutchChangeRateScaleUpShift) 145 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleDownShift', olddata.oldfClutchChangeRateScaleDownShift) 146 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate , false, "CloseSportMod") 147 | end 148 | 149 | end, plate, "exOldData") 150 | SportModOn[plate] = false 151 | end 152 | end 153 | end, plate, "IsSportModOn") 154 | 155 | Wait(200) 156 | lib.callback('ld-tunertablet:GetData',false, function(tuningdata) 157 | Wait(1000) 158 | if tuningdata ~= nil then 159 | setVehData(vehicle, tuningdata) 160 | end 161 | end, plate, "TuningData") 162 | Wait(200) 163 | lib.callback('ld-tunertablet:GetData',false, function(currentdata) 164 | Wait(1000) 165 | if currentdata ~= nil then 166 | if not activebool then 167 | local ped = PlayerPedId() 168 | local veh = GetVehiclePedIsUsing(ped) 169 | setAdvancedData(veh, currentdata, nil, true) 170 | end 171 | 172 | end 173 | end, plate, "CurrentVehicleData") 174 | 175 | end 176 | else 177 | lastVehicle = nil 178 | end 179 | end 180 | end) 181 | 182 | Citizen.CreateThread(function () 183 | while true do 184 | Citizen.Wait(sleep) 185 | sleep = 3000 186 | local vehicle = GetVehiclePedIsIn(PlayerPedId(), false) 187 | 188 | if IsPedInAnyVehicle(PlayerPedId()) and GetPedInVehicleSeat(vehicle, -1) == PlayerPedId() then 189 | sleep = 1500 190 | local plate = GetVehicleNumberPlateText(vehicle) 191 | if Config.Modes["SpeedType"] then 192 | speed = math.ceil(GetEntitySpeed(vehicle) * 2.236936) 193 | else 194 | speed = math.ceil(GetEntitySpeed(vehicle) * 3.6) 195 | end 196 | if driftMod[plate] then 197 | sleep = 0 198 | if IsControlPressed(1, 21) then 199 | if speed <= tonumber(Config.Modes["DriftModeSpeedLimit"]) then 200 | SetVehicleReduceGrip(vehicle, true) 201 | end 202 | elseif IsControlJustReleased(1, 21) then 203 | SetVehicleReduceGrip(vehicle, false) 204 | end 205 | 206 | end 207 | end 208 | 209 | end 210 | end) 211 | 212 | Citizen.CreateThread(function () 213 | while true do 214 | Citizen.Wait(0) 215 | local vehicle = GetVehiclePedIsIn(PlayerPedId(), false) 216 | 217 | if IsPedInAnyVehicle(PlayerPedId()) and GetPedInVehicleSeat(vehicle, -1) == PlayerPedId() then 218 | local plate = GetVehicleNumberPlateText(vehicle) 219 | Wait(1000) 220 | if SportModOn[plate] then 221 | local oldfInitialDriveMaxFlatVelData = GetVehicleHandlingFloat(vehicle, "CHandlingData", "fInitialDriveMaxFlatVel") 222 | local oldfClutchChangeRateScaleUpShiftData = GetVehicleHandlingFloat(vehicle, "CHandlingData", "fClutchChangeRateScaleUpShift") 223 | local oldfClutchChangeRateScaleDownShiftData = GetVehicleHandlingFloat(vehicle, "CHandlingData", "fClutchChangeRateScaleDownShift") 224 | 225 | oldSportMod[plate] = { 226 | oldfInitialDriveMaxFlatVel = oldfInitialDriveMaxFlatVelData, 227 | oldffDriveInertia = oldfInitialDriveMaxFlatVelData, 228 | oldfClutchChangeRateScaleUpShift = oldfInitialDriveMaxFlatVelData, 229 | oldfClutchChangeRateScaleDownShift = oldfInitialDriveMaxFlatVelData, 230 | } 231 | 232 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fInitialDriveMaxFlatVel', oldfInitialDriveMaxFlatVelData + (oldfInitialDriveMaxFlatVelData / 100 * 20)) 233 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fDriveInertia', 2.000000) 234 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleUpShift', oldfClutchChangeRateScaleUpShiftData * 8) 235 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleDownShift', oldfClutchChangeRateScaleDownShiftData * 8) 236 | break 237 | else 238 | lib.callback('ld-tunertablet:GetData',false, function(dataone, olddata) 239 | if dataone == nil or dataone == false then 240 | return 241 | else 242 | olddata = oldSportMod[plate] 243 | if olddata == nil then return end 244 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fInitialDriveMaxFlatVel', olddata.oldfInitialDriveMaxFlatVel) 245 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fDriveInertia', olddata.oldffDriveInertia) 246 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleUpShift', olddata.oldfClutchChangeRateScaleUpShift) 247 | SetVehicleHandlingField(vehicle, 'CHandlingData', 'fClutchChangeRateScaleDownShift', olddata.oldfClutchChangeRateScaleDownShift) 248 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate , false, "CloseSportMod") 249 | end 250 | 251 | end, plate, "exOldData") 252 | SportModOn[plate] = false 253 | end 254 | end 255 | 256 | end 257 | end) 258 | 259 | -------------------------------------------------------------------------------- /shared/config.lua: -------------------------------------------------------------------------------- 1 | Config = {} 2 | 3 | Config.Language = "en" -- just en if u want add more lang from locales.lua 4 | 5 | Config.Menu = { 6 | ["Commands"] = { 7 | ["Tuner"] = "tunertablet", 8 | }, 9 | } 10 | 11 | Config.Modes = { 12 | ["SpeedType"] = "KMH", 13 | ["DriftModeSpeedLimit"] = 50, 14 | ["SportModeSettings"] = { 15 | ["fInitialDriveMaxFlatVel"] = 100 * 20, 16 | ["fDriveInertia"] = 2.000000, 17 | ["fClutchChangeRateScaleUpShift"] = 8, 18 | ["fClutchChangeRateScaleDownShift"] = 8, 19 | } 20 | } 21 | 22 | Config.Pages = { 23 | ["Main_Pages"] = { 24 | { title = 'Home', description = 'Start screen with quick access to your tunerchip settings. You can start tuning your vehicle here.' }, 25 | { title = 'Presets', description = 'Load or create new presets for your vehicle. Customize and save configurations for quick access.' }, 26 | { title = 'Simple Configuration', description = 'Easily configure basic settings for your vehicle. A user-friendly and quick editing interface.' }, 27 | { title = 'Advanced Configuration', description = 'Fine-tune advanced settings like engine performance and suspension for optimal results.' }, 28 | { title = 'Test Tool', description = 'One-to-one to measure your vehicles performance' }, 29 | { title = 'XML', description = 'Export or import your vehicle settings as XML files for easy sharing or backup purposes.' } 30 | }, 31 | ["Sub_Pages"] = { 32 | { title = 'Aero', description = 'This set of values represent the vehicle body shape, and mostly affect top speed behavior.' }, 33 | { title = 'Chassis', description = 'This set of values represent the vehicle body itself, and will play a big role on how the vehicle behaves overall.' }, 34 | { title = 'Engine', description = 'The moving force of your vehicle, this set of values govern details about how different bits of the engine will behave. Works in close relationswhip with Transmission.' }, 35 | { title = 'Transmission', description = 'Together with the Engine, the transmission settings define the power output of your vehicle.' }, 36 | { title = 'Brakes', description = 'Brakes are one of the main defining factors of the performance of your vehicle. This value should complement both the vehicles power and traction, so as to keep a coherent balance. Unless you have reasons for it not to.' }, 37 | { title = 'Traction', description = 'Traction is the main limiter of power and braking ability, and has to be able to handle both; otherwise your vehicle may suffer wheelspin/wheel lock. However, these can also be treated as features and not defects, being part of the vehicles personality.' }, 38 | { title = 'Suspension', description = 'Stiffer, softer, loose, tight. This section governs how your car floats above its wheels.' }, 39 | { title = 'Anti-roll', description = 'The main goal of the Anti-roll Bars is to prevent the car from leaning too much when taking a corner. Do keep in mind that the wheels the cars leans into, receive more grip from the pressure, while the wheels on the other side lose traction.' } 40 | } 41 | } 42 | 43 | Config.ReadyPresets = { 44 | { title = 'Drift Mode', description = 'In Drift mode, you can drift by pressing the support button you set.' }, 45 | { title = 'Eco Mode', description = 'Eco Mode has your cars standard settings and you can select this mode if you want to return it back.' }, 46 | { title = 'Sport Mode', description = 'Sport Mode sophisticatedly indexes your vehicle to the ideal speed mode' } 47 | } 48 | 49 | Config.Configurables = { 50 | ["Simple"] = { 51 | {title = 'Boost', description = 'Also called engine power, it dictates the target acceleration the engine is aiming for, measured in G-Forces. Wheel grip may not be able to cope with it, however.', minValue = 0, maxValue = 0.5, step = 0.005, modal = 'fInitialDriveForce', fInitialDriveForce = 0}, 52 | {title = 'Acceleration', description = 'How responsive the engine revs will be to throttle control. It is measured in Higher values will result in faster RPM acceleration and deceleration, while lower values will result in more sluggish RPMs.', minValue = 0, maxValue = 3, step = 0.001, modal = 'fDriveInertia', fDriveInertia = 0}, 53 | {title = 'Gear Change', description = 'The vehicle will take 0.5s to shift from gear to gear.', minValue = 0, maxValue = 10, step = 0.25, modal = 'fClutchChangeRateScaleUpShift', fClutchChangeRateScaleUpShift = 0}, 54 | {title = 'Breaking', description = 'Usually, the best balance is between 0.55 and 0.7 for best braking capabilities, as it accounts for the weight transfer that ensues when braking. (60% Front - 40% Rear)', minValue = 0, maxValue = 1, step = 0.025, modal = 'fBrakeBiasFront', fBrakeBiasFront = 0}, 55 | {title = 'Drivetrain', description = 'Defines how the power from fInitialDriveForce is distributed between the axles. 0.0 implies a fully RWD setup, with will only deliver power to the rear wheels.', minValue = 0, maxValue = 1, step = 0.05, modal = 'fDriveBiasFront', fDriveBiasFront = 0}, 56 | }, 57 | ["Advanced"] = { 58 | {subCategory = "Aero", title = 'Downforce', description = 'Downforce is a way to gain grip at speed, and can be increased by Spoilers. This car will generate 0.14Gs of additional grip at 60mph.', minValue = 0, maxValue = 5, step = 0.05, modal = 'fDownforceModifier', fDownforceModifier = 0}, 59 | {subCategory = "Aero", title = 'Air Resistance', description = 'Higher resistance means an eariler perceived loss of power at higher speed, resulting in a lower top speed, as the engine cannot overpower this force. Lower air resistance allows the vehicle to travel faster on the same power.', minValue = 0, maxValue = 20, step = 0.5, modal = 'fInitialDragCoeff', fInitialDragCoeff = 0}, 60 | 61 | -- Chassis 62 | {subCategory = "Chassis", title = 'Mass', description = 'Measured in Kg, mass is only responsible for the interaction between entities. Lets say its the vehicles pushing force.', minValue = 0, maxValue = 15000, step = 1, modal = 'fMass', fMass = 0}, 63 | {subCategory = "Chassis", title = 'Center of Mass', description = 'This editor is not able to edit the Center Of Mass offsets yet.', minValue = -2, maxValue = 2, step = 1, modal = 'vecCentreOfMassOffset', vecCentreOfMassOffset = 0}, 64 | {subCategory = "Chassis", title = 'Rotational Inertia', description = 'This editor is not able to edit the Rotational Inertia offsets yet.', minValue = -2, maxValue = 2, step = 1, modal = 'vecInertiaMultiplier', vecInertiaMultiplier = 0}, 65 | 66 | -- Engine 67 | {subCategory = "Engine", title = 'Engine/Acceleration', description = 'Also called engine power, it dictates the target acceleration the engine is aiming for, measured in G-Forces. Wheel grip may not be able to cope with it, however.', minValue = 0, maxValue = 0.5, step = 0.005, modal = 'fInitialDriveForce', fInitialDriveForce = 0}, 68 | {subCategory = "Engine", title = 'Drive inertia', description = 'How responsive the engine revs will be to throttle control. It is measured in Higher values will result in faster RPM acceleration and deceleration, while lower values will result in more sluggish RPMs.', minValue = 0, maxValue = 3, step = 0.001, modal = 'fDriveInertia', fDriveInertia = 0}, 69 | {subCategory = "Engine", title = 'Top Speed', description = 'Maximum engine top speed. Over this speed, the engine power will degrade greatly. Keep in mind that gearing will stretch over this length.', minValue = 0, maxValue = 200, step = 1, modal = 'fInitialDriveMaxFlatVel', fInitialDriveMaxFlatVel = 0}, 70 | 71 | -- Transmission 72 | {subCategory = "Transmission", title = 'Nº of Gears', description = 'As gears modulate the fInitialDriveForce up until fInitialDriveMaxFlatVel, keepin a reasonably number of gears for your top speed is reccomended. Remember Transmission upgrades add one gear total.', minValue = 0, maxValue = 6, step = 1, modal = 'nInitialDriveGears', nInitialDriveGears = 0}, 73 | {subCategory = "Transmission", title = 'Up Shift times', description = '', minValue = 0, maxValue = 10, step = 0.25, modal = 'fClutchChangeRateScaleUpShift', fClutchChangeRateScaleUpShift = 0}, 74 | {subCategory = "Transmission", title = 'Down Shift times', description = '', minValue = 0, maxValue = 10, step = 0.25, modal = 'fClutchChangeRateScaleDownShift', fClutchChangeRateScaleDownShift = 0}, 75 | {subCategory = "Transmission", title = 'Power Bias', description = '', minValue = 0, maxValue = 1, step = 0.05, modal = 'fDriveBiasFront', fDriveBiasFront = 0}, 76 | 77 | -- Brakes 78 | {subCategory = "Brakes", title = 'Brake Strength', description = 'How many Gs of deceleration are applied to each wheel. fTractionCurveMax and fBrakeForce are closely related. Assuming perfect balance, a fourth of brake is enough to make each wheel lockup.', minValue = 0.1, maxValue = 1, step = 0.001, modal = 'fBrakeForce', fBrakeForce = 0}, 79 | {subCategory = "Brakes", title = 'Brake Bias', description = 'Distribution of the brake strength between the axles. Usually, the best balance is between 0.55 and 0.7 for best braking capabilities, as it accounts for the weight transfer that ensues when braking.', minValue = 0, maxValue = 1, step = 0.025, modal = 'fBrakeBiasFront', fBrakeBiasFront = 0}, 80 | {subCategory = "Brakes", title = 'Handbrake Strength', description = 'Similar to fBrakeForce, but is only applied to the rear axle(s).', minValue = 0, maxValue = 1, step = 0.05, modal = 'fHandBrakeForce', fHandBrakeForce = 0}, 81 | 82 | -- Traction 83 | {subCategory = "Traction", title = 'Tire Grip Curve Max', description = 'In V, grip is represented as how much the vehicles tires are able to accelerate or decelerate the cars body, as a whole.', minValue = 0, maxValue = 3, step = 0.05, modal = 'fTractionCurveMax', fTractionCurveMax = 0}, 84 | {subCategory = "Traction", title = 'Tire Grip Curve Min', description = 'Similar to fBrakeForce, but is only applied to the rear axle(s).', minValue = 0, maxValue = 3, step = 0.05, modal = 'fTractionCurveMin', fTractionCurveMin = 0}, 85 | {subCategory = "Traction", title = 'Traction Curve', description = 'Slide angle at which the car will enjoy the best grip available. The vehicle will tend to stay below half of this value. It is reccomended to keep it at default 22.5 on most vehicles, though Sports and Supercars can have it as low as 18º if you so desire, making them stay more straight. Over 24º is not reccomended save for the slidiest cars, like old muscles.', minValue = 10, maxValue = 30, step = 0.5, modal = 'fTractionCurveLateral', fTractionCurveLateral = 0}, 86 | {subCategory = "Traction", title = 'Tire Grip Bias', description = 'This value perfectly describes situations where the front and rear axles have different kinds of wheels, be it the compounds are different, or the tire width is different. Very useful for dragsters and high performance cars who come with changes like these.', minValue = 0, maxValue = 1, step = 0.005, modal = 'fTractionBiasFront', fTractionBiasFront = 0}, 87 | {subCategory = "Traction", title = 'Offroad Traction Loss', description = 'How exaggerated the traction loss is for this vehicle. A value of 1.0 makes the car lose grip on each surface as expected by the game. Below 1.0 you lose less grip than normal, over 1.0 you lose more grip than normal.', minValue = 0, maxValue = 1.5, step = 0.001, modal = 'fTractionLossMult', fTractionLossMult = 0}, 88 | {subCategory = "Traction", title = 'Low Speed Burnout Mult', description = 'How exaggerated the fake burnout griploss is for this vehicle.', minValue = 0, maxValue = 2, step = 0.05, modal = 'fLowSpeedTractionLossMult', fLowSpeedTractionLossMult = 0}, 89 | {subCategory = "Traction", title = 'Max Steer Angle', description = 'Maximum steering angle for the vehicle.', minValue = 0, maxValue = 50, step = 0.05, modal = 'fSteeringLock', fSteeringLock = 0}, 90 | 91 | -- Suspension 92 | {subCategory = "Suspension", title = 'Spring Strength', description = 'Spring strength.', minValue = 0, maxValue = 5, step = 0.001, modal = 'fSuspensionForce', fSuspensionForce = 0}, 93 | {subCategory = "Suspension", title = 'Spring Comp Dampen Strength', description = '', minValue = 0, maxValue = 3, step = 0.05, modal = 'fSuspensionCompDamp', fSuspensionCompDamp = 0}, 94 | {subCategory = "Suspension", title = 'Spring Rebound Dampen Strength', description = 'How strongly the spring strength is dampened when compressing or decompressing.', minValue = 0, maxValue = 3, step = 0.05, modal = 'fSuspensionReboundDamp', fSuspensionReboundDamp = 0}, 95 | {subCategory = "Suspension", title = 'Compression/Decompression Upper Limits', description = '', minValue = 0, maxValue = 1, step = 0.005, modal = 'fSuspensionUpperLimit', fSuspensionUpperLimit = 0}, 96 | {subCategory = "Suspension", title = 'Compression/Decompression Lower Limits', description = 'Compression upper and lower limits, in meters. Yeah, use centimeters.', minValue = -1, maxValue = 0, step = 0.005, modal = 'fSuspensionLowerLimit', fSuspensionLowerLimit = 0}, 97 | {subCategory = "Suspension", title = 'Suspension Raise', description = 'In meters, this raises or lowers the natural stance of the vehicle.', minValue = -1, maxValue = 1, step = 0.05, modal = 'fSuspensionRaise', fSuspensionRaise = 0}, 98 | {subCategory = "Suspension", title = 'Strength Bias', description = 'Spring strength distribution between the axles of the vehicle.', minValue = 0, maxValue = 1, step = 0.005, modal = 'fSuspensionBiasFront', fSuspensionBiasFront = 0}, 99 | 100 | -- Anti-roll 101 | {subCategory = "Anti-roll", title = 'Antiroll Strength', description = 'How strongly the antiroll bars try to keep the vehicle from leaning.', minValue = 0, maxValue = 2, step = 0.001, modal = 'fAntiRollBarForce', fAntiRollBarForce = 0}, 102 | {subCategory = "Anti-roll", title = 'Strength Bias', description = 'Distribution of the antiroll bar strength between the car axles.', minValue = 0, maxValue = 1, step = 0.05, modal = 'fAntiRollBarBiasFront', fAntiRollBarBiasFront = 0}, 103 | {subCategory = "Anti-roll", title = 'Rollcentre - Front', description = 'Relative to the model bottom, defines where the pivot point is. This is used for leaning.', minValue = 0, maxValue = 1, step = 0.001, modal = 'fRollCentreHeightFront', fRollCentreHeightFront = 0}, 104 | {subCategory = "Anti-roll", title = 'Rollcentre - Back', description = 'Relative to the model bottom, defines where the pivot point is. This is used for leaning.', minValue = 0, maxValue = 1, step = 0.05, modal = 'fRollCentreHeightRear', fRollCentreHeightRear = 0} 105 | } 106 | } -------------------------------------------------------------------------------- /client/functions.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | LastEngineMultiplier = 1.0 4 | function setVehData(veh, data) 5 | if not DoesEntityExist(veh) or not data then return nil end 6 | 7 | 8 | local settings = {} 9 | if data.SimpleConfiguration then 10 | for _, config in ipairs(data.SimpleConfiguration) do 11 | settings[config.modal] = config[config.modal] 12 | end 13 | end 14 | 15 | 16 | if settings.fInitialDriveForce then 17 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveForce", tonumber(settings.fInitialDriveForce) + 0.0) 18 | end 19 | 20 | if settings.fDriveInertia then 21 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveInertia", tonumber(settings.fDriveInertia) + 0.0) 22 | end 23 | 24 | if settings.fClutchChangeRateScaleUpShift then 25 | SetVehicleHandlingFloat(veh, "CHandlingData", "fClutchChangeRateScaleUpShift", tonumber(settings.fClutchChangeRateScaleUpShift) + 0.0) 26 | end 27 | 28 | if settings.fDriveBiasFront then 29 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveBiasFront", tonumber(settings.fDriveBiasFront) + 0.0) 30 | end 31 | 32 | if settings.fBrakeBiasFront then 33 | SetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeBiasFront", tonumber(settings.fBrakeBiasFront) + 0.0) 34 | end 35 | end 36 | 37 | function setAdvancedData(veh, data, bool, three) 38 | local newdata = {} 39 | 40 | 41 | if three then 42 | if data.vehicleData ~= nil then 43 | newdata = data.vehicleData 44 | else 45 | DefaultAdvancedData(veh, GetVehicleNumberPlateText(veh), data) 46 | return 47 | end 48 | else 49 | if bool then 50 | newdata = data.vehicleData.AdvancedConfigurationData 51 | else 52 | newdata = data.AdvancedConfigurationData 53 | end 54 | end 55 | 56 | 57 | if not DoesEntityExist(veh) or not newdata then return nil end 58 | 59 | 60 | local vehicleData = {} 61 | 62 | for _, v in ipairs(newdata) do 63 | vehicleData[v.modal] = v[v.modal] or v.value or v.fMass or v.fInitialDriveForce or v.fBrakeForce 64 | end 65 | 66 | 67 | TriggerServerEvent("ld-tunertablet:CreateTableData", GetVehicleNumberPlateText(veh), vehicleData, "CurrentVehicleData") 68 | 69 | 70 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDownforceModifier", tonumber(vehicleData.fDownforceModifier) + 0.0) 71 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDragCoeff", tonumber(vehicleData.fInitialDragCoeff) + 0.0) 72 | SetVehicleHandlingFloat(veh, "CHandlingData", "fMass", tonumber(vehicleData.fMass) + 0.0) 73 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveForce", tonumber(vehicleData.fInitialDriveForce) + 0.0) 74 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveInertia", tonumber(vehicleData.fDriveInertia) + 0.0) 75 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveMaxFlatVel", tonumber(vehicleData.fInitialDriveMaxFlatVel) + 0.0) 76 | SetVehicleHandlingInt(veh, "CHandlingData", "nInitialDriveGears", tonumber(vehicleData.nInitialDriveGears)) 77 | SetVehicleHandlingFloat(veh, "CHandlingData", "fClutchChangeRateScaleUpShift", tonumber(vehicleData.fClutchChangeRateScaleUpShift) + 0.0) 78 | SetVehicleHandlingFloat(veh, "CHandlingData", "fClutchChangeRateScaleDownShift", tonumber(vehicleData.fClutchChangeRateScaleDownShift) + 0.0) 79 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveBiasFront", tonumber(vehicleData.fDriveBiasFront) + 0.0) 80 | SetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeForce", tonumber(vehicleData.fBrakeForce) + 0.0) 81 | SetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeBiasFront", tonumber(vehicleData.fBrakeBiasFront) + 0.0) 82 | SetVehicleHandlingFloat(veh, "CHandlingData", "fHandBrakeForce", tonumber(vehicleData.fHandBrakeForce) + 0.0) 83 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveMax", tonumber(vehicleData.fTractionCurveMax) + 0.0) 84 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveMin", tonumber(vehicleData.fTractionCurveMin) + 0.0) 85 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveLateral", tonumber(vehicleData.fTractionCurveLateral) + 0.0) 86 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionBiasFront", tonumber(vehicleData.fTractionBiasFront) + 0.0) 87 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionLossMult", tonumber(vehicleData.fTractionLossMult) + 0.0) 88 | SetVehicleHandlingFloat(veh, "CHandlingData", "fLowSpeedTractionLossMult", tonumber(vehicleData.fLowSpeedTractionLossMult) + 0.0) 89 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSteeringLock", tonumber(vehicleData.fSteeringLock) + 0.0) 90 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionForce", tonumber(vehicleData.fSuspensionForce)) 91 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionCompDamp", tonumber(vehicleData.fSuspensionCompDamp) + 0.0) 92 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionReboundDamp", tonumber(vehicleData.fSuspensionReboundDamp) + 0.0) 93 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionUpperLimit", tonumber(vehicleData.fSuspensionUpperLimit) + 0.0) 94 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionLowerLimit", tonumber(vehicleData.fSuspensionLowerLimit) + 0.0) 95 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionRaise", tonumber(vehicleData.fSuspensionRaise) + 0.0) 96 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionBiasFront", tonumber(vehicleData.fSuspensionBiasFront) + 0.0) 97 | SetVehicleHandlingFloat(veh, "CHandlingData", "fAntiRollBarForce", tonumber(vehicleData.fAntiRollBarForce) + 0.0) 98 | SetVehicleHandlingFloat(veh, "CHandlingData", "fAntiRollBarBiasFront", tonumber(vehicleData.fAntiRollBarBiasFront) + 0.0) 99 | SetVehicleHandlingFloat(veh, "CHandlingData", "fRollCentreHeightFront", tonumber(vehicleData.fRollCentreHeightFront) + 0.0) 100 | SetVehicleHandlingFloat(veh, "CHandlingData", "fRollCentreHeightRear", tonumber(vehicleData.fRollCentreHeightRear) + 0.0) 101 | end 102 | 103 | 104 | 105 | function DefaultAdvancedData(veh,plate, GelenData) 106 | if GelenData ~= nil then 107 | data = GelenData 108 | if data ~= nil then 109 | local tonum = tonumber(data.nInitialDriveGears) 110 | if not DoesEntityExist(veh) or not data then return nil end 111 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDownforceModifier", tonumber(data.fDownforceModifier) + 0.0 ) 112 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDragCoeff", tonumber(data.fInitialDragCoeff) + 0.0 ) 113 | -- CHASIS 114 | SetVehicleHandlingFloat(veh, "CHandlingData", "fMass", tonumber(data.fMass) + 0.0 ) 115 | -- ENGINE 116 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveForce", tonumber(data.fInitialDriveForce) + 0.0 ) 117 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveInertia", tonumber(data.fDriveInertia) + 0.0 ) 118 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveMaxFlatVel", tonumber(data.fInitialDriveMaxFlatVel) + 0.0 ) 119 | -- TRANSMISSION 120 | SetVehicleHandlingInt(veh, "CHandlingData", "nInitialDriveGears", math.floor(tonum)) -- İnteger 121 | SetVehicleHandlingFloat(veh, "CHandlingData", "fClutchChangeRateScaleUpShift", tonumber(data.fClutchChangeRateScaleUpShift) + 0.0 ) 122 | SetVehicleHandlingFloat(veh, "CHandlingData", "fClutchChangeRateScaleDownShift", tonumber(data.fClutchChangeRateScaleDownShift) + 0.0 ) 123 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveBiasFront", tonumber(data.fDriveBiasFront)) 124 | -- BRAKE 125 | SetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeForce", tonumber(data.fBrakeForce) + 0.0 ) 126 | SetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeBiasFront", tonumber(data.fBrakeBiasFront) + 0.0 ) 127 | SetVehicleHandlingFloat(veh, "CHandlingData", "fHandBrakeForce", tonumber(data.fHandBrakeForce) + 0.0 ) 128 | -- TRACTION 129 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveMax", tonumber(data.fTractionCurveMax) + 0.0 ) 130 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveMin", tonumber(data.fTractionCurveMin) + 0.0 ) 131 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveLateral", tonumber(data.fTractionCurveLateral) + 0.0 ) 132 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionBiasFront", tonumber(data.fTractionBiasFront) + 0.0 ) 133 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionLossMult", tonumber(data.fTractionLossMult) + 0.0 ) 134 | SetVehicleHandlingFloat(veh, "CHandlingData", "fLowSpeedTractionLossMult", tonumber(data.fLowSpeedTractionLossMult) + 0.0 ) 135 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSteeringLock", tonumber(data.fSteeringLock) + 0.0 ) 136 | -- SUSPENSION 137 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionForce", tonumber(data.fSuspensionForce)) 138 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionCompDamp", tonumber(data.fSuspensionCompDamp) + 0.0 ) 139 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionReboundDamp", tonumber(data.fSuspensionReboundDamp) + 0.0 ) 140 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionUpperLimit", tonumber(data.fSuspensionUpperLimit) + 0.0 ) 141 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionLowerLimit", tonumber(data.fSuspensionLowerLimit) + 0.0 ) 142 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionRaise", tonumber(data.fSuspensionRaise) + 0.0 ) 143 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionBiasFront", tonumber(data.fSuspensionBiasFront) + 0.0 ) 144 | -- ANTIROLL 145 | SetVehicleHandlingFloat(veh, "CHandlingData", "fAntiRollBarForce", tonumber(data.fAntiRollBarForce) + 0.0 ) 146 | SetVehicleHandlingFloat(veh, "CHandlingData", "fAntiRollBarBiasFront", tonumber(data.fAntiRollBarBiasFront) + 0.0 ) 147 | SetVehicleHandlingFloat(veh, "CHandlingData", "fRollCentreHeightFront", tonumber(data.fRollCentreHeightFront) + 0.0 ) 148 | SetVehicleHandlingFloat(veh, "CHandlingData", "fRollCentreHeightRear", tonumber(data.fRollCentreHeightRear) + 0.0 ) 149 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate, data, "CurrentVehicleData") 150 | end 151 | else 152 | lib.callback('ld-tunertablet:sqlDta', false, function(data) 153 | 154 | if data ~= nil then 155 | local tonum = tonumber(data.nInitialDriveGears) 156 | if not DoesEntityExist(veh) or not data then return nil end 157 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDownforceModifier", tonumber(data.fDownforceModifier) + 0.0 ) 158 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDragCoeff", tonumber(data.fInitialDragCoeff) + 0.0 ) 159 | -- CHASIS 160 | SetVehicleHandlingFloat(veh, "CHandlingData", "fMass", tonumber(data.fMass) + 0.0 ) 161 | -- ENGINE 162 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveForce", tonumber(data.fInitialDriveForce) + 0.0 ) 163 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveInertia", tonumber(data.fDriveInertia) + 0.0 ) 164 | SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveMaxFlatVel", tonumber(data.fInitialDriveMaxFlatVel) + 0.0 ) 165 | -- TRANSMISSION 166 | SetVehicleHandlingInt(veh, "CHandlingData", "nInitialDriveGears", math.floor(tonum)) -- İnteger 167 | SetVehicleHandlingFloat(veh, "CHandlingData", "fClutchChangeRateScaleUpShift", tonumber(data.fClutchChangeRateScaleUpShift) + 0.0 ) 168 | SetVehicleHandlingFloat(veh, "CHandlingData", "fClutchChangeRateScaleDownShift", tonumber(data.fClutchChangeRateScaleDownShift) + 0.0 ) 169 | SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveBiasFront", tonumber(data.fDriveBiasFront)) 170 | -- BRAKE 171 | SetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeForce", tonumber(data.fBrakeForce) + 0.0 ) 172 | SetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeBiasFront", tonumber(data.fBrakeBiasFront) + 0.0 ) 173 | SetVehicleHandlingFloat(veh, "CHandlingData", "fHandBrakeForce", tonumber(data.fHandBrakeForce) + 0.0 ) 174 | -- TRACTION 175 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveMax", tonumber(data.fTractionCurveMax) + 0.0 ) 176 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveMin", tonumber(data.fTractionCurveMin) + 0.0 ) 177 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveLateral", tonumber(data.fTractionCurveLateral) + 0.0 ) 178 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionBiasFront", tonumber(data.fTractionBiasFront) + 0.0 ) 179 | SetVehicleHandlingFloat(veh, "CHandlingData", "fTractionLossMult", tonumber(data.fTractionLossMult) + 0.0 ) 180 | SetVehicleHandlingFloat(veh, "CHandlingData", "fLowSpeedTractionLossMult", tonumber(data.fLowSpeedTractionLossMult) + 0.0 ) 181 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSteeringLock", tonumber(data.fSteeringLock) + 0.0 ) 182 | -- SUSPENSION 183 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionForce", tonumber(data.fSuspensionForce)) 184 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionCompDamp", tonumber(data.fSuspensionCompDamp) + 0.0 ) 185 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionReboundDamp", tonumber(data.fSuspensionReboundDamp) + 0.0 ) 186 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionUpperLimit", tonumber(data.fSuspensionUpperLimit) + 0.0 ) 187 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionLowerLimit", tonumber(data.fSuspensionLowerLimit) + 0.0 ) 188 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionRaise", tonumber(data.fSuspensionRaise) + 0.0 ) 189 | SetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionBiasFront", tonumber(data.fSuspensionBiasFront) + 0.0 ) 190 | -- ANTIROLL 191 | SetVehicleHandlingFloat(veh, "CHandlingData", "fAntiRollBarForce", tonumber(data.fAntiRollBarForce) + 0.0 ) 192 | SetVehicleHandlingFloat(veh, "CHandlingData", "fAntiRollBarBiasFront", tonumber(data.fAntiRollBarBiasFront) + 0.0 ) 193 | SetVehicleHandlingFloat(veh, "CHandlingData", "fRollCentreHeightFront", tonumber(data.fRollCentreHeightFront) + 0.0 ) 194 | SetVehicleHandlingFloat(veh, "CHandlingData", "fRollCentreHeightRear", tonumber(data.fRollCentreHeightRear) + 0.0 ) 195 | TriggerServerEvent("ld-tunertablet:CreateTableData", plate, data, "CurrentVehicleData") 196 | end 197 | end, plate, "DefaultData") 198 | end 199 | 200 | end 201 | 202 | 203 | local VehicleDefaultData = {} 204 | 205 | function GetVehData(veh) 206 | VehicleDefaultData = {} 207 | VehicleDefaultData.fDownforceModifier = GetVehicleHandlingFloat(veh, "CHandlingData", "fDownforceModifier") 208 | VehicleDefaultData.fInitialDragCoeff = GetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDragCoeff") 209 | -- CHASIS 210 | VehicleDefaultData.fMass = GetVehicleHandlingFloat(veh, "CHandlingData", "fMass") 211 | VehicleDefaultData.vecCentreOfMassOffset = GetVehicleHandlingVector(veh, "CHandlingData", "vecCentreOfMassOffset") -- Vector 212 | VehicleDefaultData.vecInertiaMultiplier = GetVehicleHandlingVector(veh, "CHandlingData", "vecInertiaMultiplier") -- Vector 213 | -- ENGINE 214 | VehicleDefaultData.fInitialDriveForce = GetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveForce") 215 | VehicleDefaultData.fDriveInertia = GetVehicleHandlingFloat(veh, "CHandlingData", "fDriveInertia") 216 | VehicleDefaultData.fInitialDriveMaxFlatVel = GetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveMaxFlatVel") 217 | -- TRANSMISSION 218 | VehicleDefaultData.nInitialDriveGears = GetVehicleHandlingInt(veh, "CHandlingData", "nInitialDriveGears") -- İnteger 219 | VehicleDefaultData.fClutchChangeRateScaleUpShift = GetVehicleHandlingFloat(veh, "CHandlingData", "fClutchChangeRateScaleUpShift") 220 | VehicleDefaultData.fClutchChangeRateScaleDownShift = GetVehicleHandlingFloat(veh, "CHandlingData", "fClutchChangeRateScaleDownShift") 221 | VehicleDefaultData.fDriveBiasFront = GetVehicleHandlingFloat(veh, "CHandlingData", "fDriveBiasFront") 222 | -- BRAKE 223 | VehicleDefaultData.fBrakeForce = GetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeForce") 224 | VehicleDefaultData.fBrakeBiasFront = GetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeBiasFront") 225 | VehicleDefaultData.fHandBrakeForce = GetVehicleHandlingFloat(veh, "CHandlingData", "fHandBrakeForce") 226 | -- TRACTION 227 | VehicleDefaultData.fTractionCurveMax = GetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveMax") 228 | VehicleDefaultData.fTractionCurveMin = GetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveMin") 229 | VehicleDefaultData.fTractionCurveLateral = GetVehicleHandlingFloat(veh, "CHandlingData", "fTractionCurveLateral") 230 | VehicleDefaultData.fTractionBiasFront= GetVehicleHandlingFloat(veh, "CHandlingData", "fTractionBiasFront") 231 | VehicleDefaultData.fTractionLossMult = GetVehicleHandlingFloat(veh, "CHandlingData", "fTractionLossMult") 232 | VehicleDefaultData.fLowSpeedTractionLossMult = GetVehicleHandlingFloat(veh, "CHandlingData", "fLowSpeedTractionLossMult") 233 | VehicleDefaultData.fSteeringLock = GetVehicleHandlingFloat(veh, "CHandlingData", "fSteeringLock") 234 | -- SUSPENSION 235 | VehicleDefaultData.fSuspensionForce = GetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionForce") 236 | VehicleDefaultData.fSuspensionCompDamp = GetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionCompDamp") 237 | VehicleDefaultData.fSuspensionReboundDamp = GetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionReboundDamp") 238 | VehicleDefaultData.fSuspensionUpperLimit = GetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionUpperLimit") 239 | VehicleDefaultData.fSuspensionLowerLimit = GetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionLowerLimit") 240 | VehicleDefaultData.fSuspensionRaise = GetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionRaise") 241 | VehicleDefaultData.fSuspensionBiasFront = GetVehicleHandlingFloat(veh, "CHandlingData", "fSuspensionBiasFront") 242 | -- ANTIROLL 243 | VehicleDefaultData.fAntiRollBarForce = GetVehicleHandlingFloat(veh, "CHandlingData", "fAntiRollBarForce") 244 | VehicleDefaultData.fAntiRollBarBiasFront = GetVehicleHandlingFloat(veh, "CHandlingData", "fAntiRollBarBiasFront") 245 | VehicleDefaultData.fRollCentreHeightFront = GetVehicleHandlingFloat(veh, "CHandlingData", "fRollCentreHeightFront") 246 | VehicleDefaultData.fRollCentreHeightRear = GetVehicleHandlingFloat(veh, "CHandlingData", "fRollCentreHeightRear") 247 | return VehicleDefaultData 248 | end 249 | -------------------------------------------------------------------------------- /html/src/scripts/main.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | const store = Vuex.createStore({ 6 | state: {}, 7 | mutations: {}, 8 | actions: {} 9 | }); 10 | 11 | const app = Vue.createApp({ 12 | data: () => ({ 13 | showUI: false, 14 | extrasUI: false, 15 | policeUI: false, 16 | recorderUI: false, 17 | presetInput: false, 18 | currentPage: "Home", 19 | currentPageDescription: "", 20 | currentSubPage: "Aero", 21 | currentSubPageDescription: "Fine-tune advanced settings like engine performance and suspension for optimal results.", 22 | currentVehicleName: "Annis Elegy", 23 | currentVehiclePlate: "CKN117X", 24 | currentVehicleAcceleration: 40, 25 | currentVehicleTopSpeed: 50, 26 | currentVehicleTraction: 20, 27 | currentVehicleBraking: 80, 28 | currentVehicleFuelLevel: 20, 29 | currentVehicleHealth: 20, 30 | currentVehicleOilLevel: 20, 31 | currentVehicleEngineTemp: 20, 32 | confirmChanges: false, 33 | currentTime: "12:00", 34 | showSubPages: false, 35 | XMLData: "", 36 | Pages: [ 37 | // { title: 'Home', description: 'Start screen with quick access to your tunerchip settings. You can start tuning your vehicle here.' }, 38 | // { title: 'Presets', description: 'Load or create new presets for your vehicle. Customize and save configurations for quick access.' }, 39 | // { title: 'Simple Configuration', description: 'Easily configure basic settings for your vehicle. A user-friendly and quick editing interface.' }, 40 | // { title: 'Advanced Configuration', description: 'Fine-tune advanced settings like engine performance and suspension for optimal results.' }, 41 | // { title: 'Test Tool', description: 'One-to-one to measure your vehicles performance' }, 42 | // { title: 'XML', description: 'Export or import your vehicle settings as XML files for easy sharing or backup purposes.' } 43 | ], 44 | subPages: [ 45 | // { title: 'Aero', description: 'This set of values represent the vehicle body shape, and mostly affect top speed behavior.' }, 46 | // { title: 'Chassis', description: 'This set of values represent the vehicle body itself, and will play a big role on how the vehicle behaves overall.' }, 47 | // { title: 'Engine', description: 'The moving force of your vehicle, this set of values govern details about how different bits of the engine will behave. Works in close relationswhip with Transmission.' }, 48 | // { title: 'Transmission', description: 'Together with the Engine, the transmission settings define the power output of your vehicle.' }, 49 | // { title: 'Brakes', description: 'Brakes are one of the main defining factors of the performance of your vehicle. This value should complement both the vehicles power and traction, so as to keep a coherent balance. Unless you have reasons for it not to.' }, 50 | // { title: 'Traction', description: 'Traction is the main limiter of power and braking ability, and has to be able to handle both; otherwise your vehicle may suffer wheelspin/wheel lock. However, these can also be treated as features and not defects, being part of the vehicles personality.' }, 51 | // { title: 'Suspension', description: 'Stiffer, softer, loose, tight. This section governs how your car floats above its wheels.' }, 52 | // { title: 'Anti-roll', description: 'The main goal of the Anti-roll Bars is to prevent the car from leaning too much when taking a corner. Do keep in mind that the wheels the cars leans into, receive more grip from the pressure, while the wheels on the other side lose traction.' }, 53 | ], 54 | readyPresets: [ 55 | // { title: 'Drift Mode', description: 'In Drift mode, you can drift by pressing the support button you set.' }, 56 | // { title: 'Eco Mode', description: 'Eco Mode has your cars standard settings and you can select this mode if you want to return it back.' }, 57 | // { title: 'Sport Mode', description: 'Sport Mode sophisticatedly indexes your vehicle to the ideal speed mode' }, 58 | ], 59 | Presets: [ 60 | 61 | ], 62 | SimpleConfiguration: [ 63 | // {title: 'Boost', description: 'Also called engine power, it dictates the target acceleration the engine is aiming for, measured in G-Forces. Wheel grip may not be able to cope with it, however.', minValue: 0, maxValue: 0.5, step: 0.005, modal: 'fInitialDriveForce', fInitialDriveForce: 0}, 64 | // {title: 'Acceleration', description: 'How responsive the engine revs will be to throttle control. It is measured in Higher values will result in faster RPM acceleration and deceleration, while lower values will result in more sluggish RPMs.', minValue: 0, maxValue: 3, step: 0.001 , modal: 'fDriveInertia', fDriveInertia: 0}, 65 | // {title: 'Gear Change', description: 'The vehicle will take 0.5s to shift from gear to gear.', minValue: 0, maxValue: 10, step: 0.25 , modal: 'fClutchChangeRateScaleUpShift', fClutchChangeRateScaleUpShift: 0}, 66 | // {title: 'Breaking', description: 'Usually, the best balance is between 0.55 and 0.7 for best braking capabilities, as it accounts for the weight transfer that ensues when braking. (60% Front - 40% Rear)', minValue: 0, maxValue: 1, step: 0.025 , modal: 'fBrakeBiasFront', fBrakeBiasFront: 0}, 67 | // {title: 'Drivetrain', description: 'Defines how the power from fInitialDriveForce is distributed between the axles. 0.0 implies a fully RWD setup, with will only deliver power to the rear wheels.', minValue: 0, maxValue: 1, step: 0.05 , modal: 'fDriveBiasFront', fDriveBiasFront: 0}, 68 | ], 69 | 70 | AdvancedConfiguration: [ 71 | // Aero 72 | // {subCategory: "Aero", title: 'Downforce', description: 'Downforce is a way to gain grip at speed, and can be increased by Spoilers. This car will generate 0.14Gs of additional grip at 60mph.', minValue: 0, maxValue: 5, step: 0.05, modal: 'fDownforceModifier', fDownforceModifier: 0}, 73 | // {subCategory: "Aero", title: 'Air Resistance', description: 'Higher resistance means an eariler perceived loss of power at higher speed, resulting in a lower top speed, as the engine cannot overpower this force. Lower air resistance allows the vehicle to travel faster on the same power.', minValue: 0, maxValue: 20, step: 0.5, modal: 'fInitialDragCoeff', fInitialDragCoeff: 0}, 74 | // // Chassis 75 | // {subCategory: "Chassis", title: 'Mass', description: 'Measured in Kg, mass is only responsible for the interaction between entities. Lets say its the vehicles pushing force.', minValue: 0, maxValue: 15000, step: 1, modal: 'fMass', fMass: 0}, 76 | // {subCategory: "Chassis", title: 'Center of Mass', description: 'This editor is not able to edit the Center Of Mass offsets yet.', minValue: -2, maxValue: 2, step: 1, modal: 'vecCentreOfMassOffset', vecCentreOfMassOffset: 0}, 77 | // {subCategory: "Chassis", title: 'Rotational Inertia', description: 'This editor is not able to edit the Rotational Inertia offsets yet.', minValue: -2, maxValue: 2, step: 1, modal: 'vecInertiaMultiplier', vecInertiaMultiplier: 0}, 78 | // // Engine 79 | // {subCategory: "Engine", title: 'Engine/Acceleration', description: 'Also called engine power, it dictates the target acceleration the engine is aiming for, measured in G-Forces. Wheel grip may not be able to cope with it, however.', minValue: 0, maxValue: 0.5, step: 0.005, modal: 'fInitialDriveForce', fInitialDriveForce: 0}, 80 | // {subCategory: "Engine", title: 'Drive inertia', description: 'How responsive the engine revs will be to throttle control. It is measured in Higher values will result in faster RPM acceleration and deceleration, while lower values will result in more sluggish RPMs.', minValue: 0, maxValue: 3, step: 0.001, modal: 'fDriveInertia', fDriveInertia: 0}, 81 | // {subCategory: "Engine", title: 'Top Speed', description: 'Maximum engine top speed. Over this speed, the engine power will degrade greatly. Keep in mind that gearing will stretch over this length.', minValue: 0, maxValue: 200, step: 1, modal: 'fInitialDriveMaxFlatVel', fInitialDriveMaxFlatVel: 0}, 82 | // // Transmission 83 | // {subCategory: "Transmission", title: 'Nº of Gears', description: 'As gears modulate the fInitialDriveForce up until fInitialDriveMaxFlatVel, keepin a reasonably number of gears for your top speed is reccomended. Remember Transmission upgrades add one gear total.', minValue: 0, maxValue: 6, step: 1, modal: 'nInitialDriveGears', nInitialDriveGears: 0}, 84 | // {subCategory: "Transmission", title: 'Up Shift times', description: '', minValue: 0, maxValue: 10, step: 0.25, modal: 'fClutchChangeRateScaleUpShift', fClutchChangeRateScaleUpShift: 0}, 85 | // {subCategory: "Transmission", title: 'Down Shift times', description: '', minValue: 0, maxValue: 10, step: 0.25, modal: 'fClutchChangeRateScaleDownShift', fClutchChangeRateScaleDownShift: 0}, 86 | // {subCategory: "Transmission", title: 'Power Bias', description: '', minValue: 0, maxValue: 1, step: 0.05, modal: 'fDriveBiasFront', fDriveBiasFront: 0}, 87 | 88 | // // Brakes 89 | // {subCategory: "Brakes", title: 'Brake Strength', description: 'How many Gs of deceleration are applied to each wheel. fTractionCurveMax and fBrakeForce are closely related. Assuming perfect balance, a fourth of brake is enough to make each wheel lockup.', minValue: 0.1, maxValue: 1, step: 0.001, modal: 'fBrakeForce', fBrakeForce: 0}, 90 | // {subCategory: "Brakes", title: 'Brake Bias', description: 'Distribution of the brake strength between the axles. Usually, the best balance is between 0.55 and 0.7 for best braking capabilities, as it accounts for the weight transfer that ensues when braking.', minValue: 0, maxValue: 1, step: 0.025, modal: 'fBrakeBiasFront', fBrakeBiasFront: 0}, 91 | // {subCategory: "Brakes", title: 'Handbrake Strength', description: 'Similar to fBrakeForce, but is only applied to the rear axle(s).', minValue: 0, maxValue: 1, step: 0.05, modal: 'fHandBrakeForce', fHandBrakeForce: 0}, 92 | // // Traction 93 | // {subCategory: "Traction", title: 'Tire Grip Curve Max', description: 'In V, grip is represented as how much the vehicles tires are able to accelerate or decelerate the cars body, as a whole.', minValue: 0, maxValue: 3, step: 0.05, modal: 'fTractionCurveMax', fTractionCurveMax: 0}, 94 | // {subCategory: "Traction", title: 'Tire Grip Curve Min', description: 'Similar to fBrakeForce, but is only applied to the rear axle(s).', minValue: 0, maxValue: 3, step: 0.05, modal: 'fTractionCurveMin', fTractionCurveMin: 0}, 95 | // {subCategory: "Traction", title: 'Traction Curve', description: 'Slide angle at which the car will enjoy the best grip available. The vehicle will tend to stay below half of this value. It is reccomended to keep it at default 22.5 on most vehicles, though Sports and Supercars can have it as low as 18º if you so desire, making them stay more straight. Over 24º is not reccomended save for the slidiest cars, like old muscles.', minValue: 10, maxValue: 30, step: 0.5, modal: 'fTractionCurveLateral', fTractionCurveLateral: 0}, 96 | // {subCategory: "Traction", title: 'Tire Grip Bias', description: 'This value perfectly describes situations where the front and rear axles have different kinds of wheels, be it the compounds are different, or the tire width is different. Very useful for dragsters and high performance cars who come with changes like these.', minValue: 0, maxValue: 1, step: 0.005, modal: 'fTractionBiasFront', fTractionBiasFront: 0}, 97 | // {subCategory: "Traction", title: 'Offroad Traction Loss', description: 'How exaggerated the traction loss is for this vehicle. A value of 1.0 makes the car lose grip on each surface as expected by the game. Below 1.0 you lose less grip than normal, over 1.0 you lose more grip than normal.', minValue: 0, maxValue: 1.5, step: 0.001, modal: 'fTractionLossMult', fTractionLossMult: 0}, 98 | // {subCategory: "Traction", title: 'Low Speed Burnout Mult', description: 'How exaggerated the fake burnout griploss is for this vehicle.', minValue: 0, maxValue: 2, step: 0.05, modal: 'fLowSpeedTractionLossMult', fLowSpeedTractionLossMult: 0}, 99 | // {subCategory: "Traction", title: 'Max Steer Angle', description: 'Maximum steering angle for the vehicle.', minValue: 0, maxValue: 50, step: 0.05, modal: 'fSteeringLock', fSteeringLock: 0}, 100 | // // Suspension 101 | // {subCategory: "Suspension", title: 'Spring Strength', description: 'Spring strength.', minValue: 0, maxValue: 5, step: 0.001, modal: 'fSuspensionForce', fSuspensionForce: 0}, 102 | // {subCategory: "Suspension", title: 'Spring Comp Dampen Strength', description: '', minValue: 0, maxValue: 3, step: 0.05, modal: 'fSuspensionCompDamp', fSuspensionCompDamp: 0}, 103 | // {subCategory: "Suspension", title: 'Spring Rebound Dampen Strength', description: 'How strongly the spring strength is dampened when compressing or decompressing.', minValue: 0, maxValue: 3, step: 0.05, modal: 'fSuspensionReboundDamp', fSuspensionReboundDamp: 0}, 104 | // {subCategory: "Suspension", title: 'Compression/Decompression Upper Limits', description: '', minValue: 0, maxValue: 1, step: 0.005, modal: 'fSuspensionUpperLimit', fSuspensionUpperLimit: 0}, 105 | // {subCategory: "Suspension", title: 'Compression/Decompression Lower Limits', description: 'Compression upper and lower limits, in meters. Yeah, use centimeters.', minValue: -1, maxValue: 0, step: 0.005, modal: 'fSuspensionLowerLimit', fSuspensionLowerLimit: 0}, 106 | // {subCategory: "Suspension", title: 'Suspension Raise', description: 'In meters, this raises or lowers the natural stance of the vehicle.', minValue: -1, maxValue: 1, step: 0.05, modal: 'fSuspensionRaise', fSuspensionRaise: 0}, 107 | // {subCategory: "Suspension", title: 'Strength Bias', description: 'Spring strength distribution between the axles of the vehicle.', minValue: 0, maxValue: 1, step: 0.005, modal: 'fSuspensionBiasFront', fSuspensionBiasFront: 0}, 108 | // // Anti-roll 109 | // {subCategory: "Anti-roll", title: 'Antiroll Strength', description: 'How strongly the antiroll bars try to keep the vehicle from leaning.', minValue: 0, maxValue: 2, step: 0.001, modal: 'fAntiRollBarForce', fAntiRollBarForce: 0}, 110 | // {subCategory: "Anti-roll", title: 'Strength Bias', description: 'Distribution of the antiroll bar strength between the car axles.', minValue: 0, maxValue: 1, step: 0.05, modal: 'fAntiRollBarBiasFront', fAntiRollBarBiasFront: 0}, 111 | // {subCategory: "Anti-roll", title: 'Rollcentre - Front', description: 'Relative to the model bottom, defines where the pivot point is. This is used for leaning.', minValue: 0, maxValue: 1, step: 0.001, modal: 'fRollCentreHeightFront', fRollCentreHeightFront: 0}, 112 | // {subCategory: "Anti-roll", title: 'Rollcentre - Back', description: 'Relative to the model bottom, defines where the pivot point is. This is used for leaning.', minValue: 0, maxValue: 1, step: 0.05, modal: 'fRollCentreHeightRear', fRollCentreHeightRear: 0}, 113 | ], 114 | Content: ``, 115 | Locales: { 116 | // welcome: "Welcome", 117 | // ucreatedpresets: "Your Created Presets", 118 | // vehiclestats: "Vehicle Stats", 119 | // acceleration: "Acceleration", 120 | // speed: "Speed", 121 | // handling: "Traction", 122 | // braking: "Braking", 123 | // oiltemp: "Fuel Level", 124 | // dashtemp: "Engine Health", 125 | // watertemp: "Oil Level", 126 | // enginetemp: "Engine Temp", 127 | // confirmtext: "You are responsible for any modifications to your vehicle, please do not do this if you are not sure.", 128 | // metrics: "Metrics", 129 | // metricsdescription: "This editor makes an effort of translating some handling items to real world measurements, depicted in red on them. For transparency and ease of fact-checking, this section documents and details the math used to get the Ingame metrics.", 130 | // ucreatedpresetsdesc: "You can easily access or delete the settings you have edited and saved from the specially developed configuration page." 131 | }, 132 | currentData: [], 133 | customsPageData: [], 134 | presetName: "", 135 | reachHundred: 0, 136 | reachQuarterMile: 0, 137 | reachSixty: 0, 138 | reachHalfMile: 0, 139 | counter: 0, 140 | timer: null, 141 | }), 142 | 143 | methods: { 144 | resetSettings() { 145 | postNUI("DEFAULT_BACK") 146 | setTimeout(() => { 147 | this.tuningTablet = true 148 | postNUI('GET_ADVANCED_DATA',{}) 149 | }, 300); 150 | }, 151 | applySettingsSimple() { 152 | postNUI('SAVE_SIMPLE_SETTINGS',{ 153 | SimpleConfiguration : this.SimpleConfiguration, 154 | } 155 | ) 156 | 157 | }, 158 | applySettingsAdvanced() { 159 | postNUI('SAVE_ADVANCED_SETTINGS',{ 160 | AdvancedConfigurationData : this.AdvancedConfiguration, 161 | dataname : this.presetName 162 | } 163 | ) 164 | this.extrasUI = false 165 | this.presetInput = false 166 | postNUI('GET_CUSTOMS_DATA', {}); 167 | this.currentPage == "Presets" 168 | }, 169 | 170 | 171 | openInput() { 172 | this.extrasUI = true 173 | this.presetInput = true 174 | }, 175 | applySettingsPreview() { 176 | }, 177 | selectReadyPreset(title) { 178 | postNUI('CHANGE_MODE',{ 179 | mode : title, 180 | } 181 | ) 182 | this.selectedCustomPreset = title; 183 | this.selectedReadyPreset = null; 184 | }, 185 | deletePreset(title, index) { 186 | const selectedData = this.Presets.find(preset => preset.title === title); 187 | if (selectedData) { 188 | postNUI('DELETE_PRESET', { 189 | vehicleData: selectedData 190 | }); 191 | } 192 | this.currentPage = "Presets"; 193 | 194 | this.Presets.splice(index, 1); 195 | this.currentPage = "Presets"; 196 | }, 197 | InsertXML(title) { 198 | const selectedData = this.Presets.find(preset => preset.title === title); 199 | 200 | if (!selectedData) { 201 | console.error("❌ Selected preset not found for title:", title); 202 | return; 203 | } 204 | const vehicleData = { 205 | dataname: selectedData.title, 206 | AdvancedConfigurationData: selectedData.AdvancedConfigurationData || [] 207 | }; 208 | postNUI('GET_XML_DATA', { 209 | vehicleData: vehicleData 210 | }); 211 | postNUI('GET_ADVANCED_DATA') 212 | postNUI('GET_CUSTOMS_DATA',{}) 213 | this.currentPage = "Presets"; 214 | }, 215 | // Update Methods 216 | updateHandlingData(handlingData) { 217 | this.SimpleConfiguration.forEach((item) => { 218 | if (handlingData[item.modal] !== undefined) { 219 | item[item.modal] = handlingData[item.modal]; 220 | } 221 | }); 222 | this.AdvancedConfiguration.forEach((item) => { 223 | if (handlingData[item.modal] !== undefined) { 224 | item[item.modal] = handlingData[item.modal]; 225 | } 226 | }); 227 | }, 228 | // UI Methods 229 | toggleConfirm() { 230 | this.confirmChanges = !this.confirmChanges; 231 | }, 232 | increaseValue(item) { 233 | item[item.modal] = Math.min(item[item.modal] + item.step, item.maxValue); 234 | }, 235 | decreaseValue(item) { 236 | item[item.modal] = Math.max(item[item.modal] - item.step, item.minValue); 237 | }, 238 | onInputChange(item, modal) { 239 | item[modal] = parseFloat(item[modal]); 240 | }, 241 | updateTime() { 242 | const now = new Date(); 243 | const hours = now.getHours(); 244 | const minutes = now.getMinutes().toString().padStart(2, '0'); 245 | const seconds = now.getSeconds().toString().padStart(2, '0'); 246 | const ampm = hours >= 12 ? 'PM' : 'AM'; 247 | const formattedHours = hours % 12 || 12; 248 | this.currentTime = `${formattedHours}:${minutes}:${seconds} ${ampm}`; 249 | }, 250 | updatePage(page) { 251 | this.currentPage = page.title; 252 | this.currentPageDescription = page.description; 253 | if (this.currentPage == "Presets") { 254 | 255 | postNUI('GET_CUSTOMS_DATA',{}) 256 | } else if (this.currentPage == "Advanced Configuration") { 257 | postNUI('GET_ADVANCED_DATA',{}) 258 | postNUI('GET_CUSTOMS_DATA',{}) 259 | } else if (this.currentPage == "Test Tool") { 260 | this.extrasUI = true 261 | this.recorderUI = true 262 | this.policeUI = false 263 | this.presetInput= false 264 | this.showUI = false 265 | this.currentPage = "Home" 266 | postNUI('TEST_TOOL') 267 | 268 | } 269 | }, 270 | updateSubPage(subpage) { 271 | this.currentPage = "Advanced Configuration"; 272 | this.currentSubPage = subpage.title; 273 | this.currentSubPageDescription = subpage.description; 274 | 275 | }, 276 | hideSubPages() { 277 | this.showSubPages = false; 278 | }, 279 | copyHandling() { 280 | const textArea = document.createElement('textarea'); 281 | textArea.value = this.generatedXML; 282 | document.body.appendChild(textArea); 283 | textArea.select(); 284 | document.execCommand('copy'); 285 | document.body.removeChild(textArea); 286 | }, 287 | downloadHandling() { 288 | if (!this.generatedXML.trim()) { 289 | 290 | return; 291 | } 292 | 293 | const file = new Blob([this.generatedXML], { type: "text/xml" }); 294 | const url = URL.createObjectURL(file); 295 | 296 | if (typeof window.invokeNative !== "undefined") { 297 | window.invokeNative("openUrl", url); // FiveM tarayıcısında URL aç 298 | } else { 299 | 300 | const a = document.createElement("a"); 301 | a.href = url; 302 | a.download = "handling.xml"; // Dosya adı 303 | document.body.appendChild(a); 304 | a.click(); 305 | document.body.removeChild(a); 306 | URL.revokeObjectURL(url); 307 | } 308 | }, 309 | exitUI() { 310 | postNUI("exit"); 311 | this.showUI = false 312 | }, 313 | startTimer() { 314 | if (this.timer) return; 315 | this.counter = 0; 316 | this.timer = setInterval(() => { 317 | this.counter++; 318 | if (this.reachHalfMile > 0) { 319 | this.stopTimer(); 320 | } 321 | }, 1000); 322 | }, 323 | stopTimer() { 324 | if (this.timer) { 325 | clearInterval(this.timer); 326 | this.timer = null; 327 | } 328 | }, 329 | resetTimer() { 330 | this.stopTimer(); 331 | this.counter = 0; 332 | } 333 | }, 334 | 335 | 336 | computed: { 337 | filteredConfiguration() { 338 | return this.AdvancedConfiguration.filter( 339 | (item) => item.subCategory === this.currentSubPage 340 | ); 341 | }, 342 | filteredConfigurations() { 343 | return this.AdvancedConfiguration.filter(item => item.subCategory === this.currentSubPage); 344 | }, 345 | advancedConfigMap() { 346 | return this.AdvancedConfiguration.reduce((acc, item) => { 347 | acc[item.modal] = item[item.modal]; 348 | return acc; 349 | }, {}); 350 | }, 351 | generatedXML() { 352 | return ` 353 | 354 | 355 | 356 | 357 | ${this.currentVehicleName} 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 440010 405 | 20000 406 | 0 407 | AVERAGE 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | `; 417 | } 418 | }, 419 | 420 | mounted() { 421 | this.updateTime(); 422 | setInterval(this.updateTime, 1000); 423 | const defaultPage = this.Pages.find(page => page.title === this.currentPage); 424 | if (defaultPage) { 425 | this.currentPageDescription = defaultPage.description; 426 | } 427 | window.addEventListener("message", (event) => { 428 | if (event.data.action == "SHOW_INTERFACE") { 429 | this.showUI = true 430 | this.Locales = event.data.Locales 431 | this.AdvancedConfiguration = event.data.AdvancedConfiguration 432 | this.SimpleConfiguration = event.data.SimpleConfiguration 433 | this.readyPresets = event.data.readyPresets 434 | this.Pages = event.data.Pages 435 | this.subPages = event.data.subPages 436 | this.currentVehicleName = event.data.vehicleDatas.currentVehicleName 437 | this.currentVehiclePlate = event.data.vehicleDatas.currentVehiclePlate 438 | this.extrasUI = false 439 | this.recorderUI = false 440 | this.policeUI = false 441 | this.currentVehicleAcceleration = event.data.vehicleDatas.currentVehicleAcceleration 442 | this.currentVehicleTopSpeed = event.data.vehicleDatas.currentVehicleTopSpeed 443 | this.currentVehicleTraction = event.data.vehicleDatas.currentVehicleTraction 444 | this.currentVehicleBraking = event.data.vehicleDatas.currentVehicleBraking 445 | this.currentVehicleFuelLevel = event.data.vehicleDatas.currentVehicleFuelLevel 446 | this.currentVehicleHealth = event.data.vehicleDatas.currentVehicleHealth 447 | this.currentVehicleOilLevel = event.data.vehicleDatas.currentVehicleOilLevel 448 | this.currentVehicleEngineTemp = event.data.vehicleDatas.currentVehicleEngineTemp 449 | this.updateHandlingData(event.data.handlingData); 450 | postNUI('GET_CUSTOMS_DATA',{}) 451 | 452 | 453 | } if (event.data.action == "GET_CURRENT_DATA") { 454 | const receivedData = event.data.GetCurrentData; 455 | 456 | this.Presets = receivedData.map(item => ({ 457 | title: item.Data.dataname || "Unknown Preset", 458 | description: "Saved", 459 | AdvancedConfigurationData : item.Data.AdvancedConfigurationData 460 | })); 461 | } if (event.data.action == "GET_VEHICLE_DATA") { 462 | this.updateHandlingData(event.data.vehicleData); 463 | postNUI('GET_CUSTOMS_DATA',{}) 464 | } if (event.data.action == "GET_TOOL") { 465 | 466 | this.extrasUI = true 467 | this.recorderUI = true 468 | this.policeUI = false 469 | this.presetInput= false 470 | this.showUI = false 471 | this.reachHundred = event.data.reach100 472 | this.reachSixty = event.data.reach60 473 | this.reachQuarterMile = event.data.reachquart 474 | this.reachHalfMile = event.data.reachhalf 475 | } 476 | if (event.data.action == "TIMER") { 477 | if (event.data.status == 0) { 478 | this.stopTimer(); 479 | } else if (event.data.status == 1) { 480 | this.startTimer(); 481 | } else if (event.data.status == 2) { 482 | this.resetTimer(); 483 | } 484 | } 485 | document.onkeyup = (data) => { 486 | if (data.which == 27) { 487 | postNUI("exit"); 488 | this.showUI = false; 489 | return; 490 | } 491 | }; 492 | }); 493 | 494 | }, 495 | 496 | destroyed() { 497 | 498 | }, 499 | created() { 500 | 501 | }, 502 | beforeDestroy() { 503 | }, 504 | }); 505 | 506 | app.use(store).mount("#app"); 507 | 508 | 509 | var resourceName = "ld-tunertablet"; 510 | 511 | if (window.GetParentResourceName) { 512 | resourceName = window.GetParentResourceName(); 513 | } 514 | 515 | window.postNUI = async (name, data) => { 516 | try { 517 | const response = await fetch(`https://${resourceName}/${name}`, { 518 | method: "POST", 519 | mode: "cors", 520 | cache: "no-cache", 521 | credentials: "same-origin", 522 | headers: { 523 | "Content-Type": "application/json" 524 | }, 525 | redirect: "follow", 526 | referrerPolicy: "no-referrer", 527 | body: JSON.stringify(data) 528 | }); 529 | return !response.ok ? null : response.json(); 530 | } catch (error) { 531 | // console.log(error) 532 | } 533 | }; 534 | 535 | 536 | 537 | -------------------------------------------------------------------------------- /html/src/scripts/vuex.global.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vuex v4.1.0 3 | * (c) 2022 Evan You 4 | * @license MIT 5 | */ 6 | var Vuex = (function (vue) { 7 | 'use strict'; 8 | 9 | var storeKey = 'store'; 10 | 11 | function useStore (key) { 12 | if ( key === void 0 ) key = null; 13 | 14 | return vue.inject(key !== null ? key : storeKey) 15 | } 16 | 17 | function getDevtoolsGlobalHook() { 18 | return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__; 19 | } 20 | function getTarget() { 21 | // @ts-ignore 22 | return typeof navigator !== 'undefined' 23 | ? window 24 | : typeof global !== 'undefined' 25 | ? global 26 | : {}; 27 | } 28 | 29 | var HOOK_SETUP = 'devtools-plugin:setup'; 30 | 31 | function setupDevtoolsPlugin(pluginDescriptor, setupFn) { 32 | var hook = getDevtoolsGlobalHook(); 33 | if (hook) { 34 | hook.emit(HOOK_SETUP, pluginDescriptor, setupFn); 35 | } 36 | else { 37 | var target = getTarget(); 38 | var list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || []; 39 | list.push({ 40 | pluginDescriptor: pluginDescriptor, 41 | setupFn: setupFn 42 | }); 43 | } 44 | } 45 | 46 | /** 47 | * Get the first item that pass the test 48 | * by second argument function 49 | * 50 | * @param {Array} list 51 | * @param {Function} f 52 | * @return {*} 53 | */ 54 | function find (list, f) { 55 | return list.filter(f)[0] 56 | } 57 | 58 | /** 59 | * Deep copy the given object considering circular structure. 60 | * This function caches all nested objects and its copies. 61 | * If it detects circular structure, use cached copy to avoid infinite loop. 62 | * 63 | * @param {*} obj 64 | * @param {Array} cache 65 | * @return {*} 66 | */ 67 | function deepCopy (obj, cache) { 68 | if ( cache === void 0 ) cache = []; 69 | 70 | // just return if obj is immutable value 71 | if (obj === null || typeof obj !== 'object') { 72 | return obj 73 | } 74 | 75 | // if obj is hit, it is in circular structure 76 | var hit = find(cache, function (c) { return c.original === obj; }); 77 | if (hit) { 78 | return hit.copy 79 | } 80 | 81 | var copy = Array.isArray(obj) ? [] : {}; 82 | // put the copy into cache at first 83 | // because we want to refer it in recursive deepCopy 84 | cache.push({ 85 | original: obj, 86 | copy: copy 87 | }); 88 | 89 | Object.keys(obj).forEach(function (key) { 90 | copy[key] = deepCopy(obj[key], cache); 91 | }); 92 | 93 | return copy 94 | } 95 | 96 | /** 97 | * forEach for object 98 | */ 99 | function forEachValue (obj, fn) { 100 | Object.keys(obj).forEach(function (key) { return fn(obj[key], key); }); 101 | } 102 | 103 | function isObject (obj) { 104 | return obj !== null && typeof obj === 'object' 105 | } 106 | 107 | function isPromise (val) { 108 | return val && typeof val.then === 'function' 109 | } 110 | 111 | function assert (condition, msg) { 112 | if (!condition) { throw new Error(("[vuex] " + msg)) } 113 | } 114 | 115 | function partial (fn, arg) { 116 | return function () { 117 | return fn(arg) 118 | } 119 | } 120 | 121 | function genericSubscribe (fn, subs, options) { 122 | if (subs.indexOf(fn) < 0) { 123 | options && options.prepend 124 | ? subs.unshift(fn) 125 | : subs.push(fn); 126 | } 127 | return function () { 128 | var i = subs.indexOf(fn); 129 | if (i > -1) { 130 | subs.splice(i, 1); 131 | } 132 | } 133 | } 134 | 135 | function resetStore (store, hot) { 136 | store._actions = Object.create(null); 137 | store._mutations = Object.create(null); 138 | store._wrappedGetters = Object.create(null); 139 | store._modulesNamespaceMap = Object.create(null); 140 | var state = store.state; 141 | // init all modules 142 | installModule(store, state, [], store._modules.root, true); 143 | // reset state 144 | resetStoreState(store, state, hot); 145 | } 146 | 147 | function resetStoreState (store, state, hot) { 148 | var oldState = store._state; 149 | var oldScope = store._scope; 150 | 151 | // bind store public getters 152 | store.getters = {}; 153 | // reset local getters cache 154 | store._makeLocalGettersCache = Object.create(null); 155 | var wrappedGetters = store._wrappedGetters; 156 | var computedObj = {}; 157 | var computedCache = {}; 158 | 159 | // create a new effect scope and create computed object inside it to avoid 160 | // getters (computed) getting destroyed on component unmount. 161 | var scope = vue.effectScope(true); 162 | 163 | scope.run(function () { 164 | forEachValue(wrappedGetters, function (fn, key) { 165 | // use computed to leverage its lazy-caching mechanism 166 | // direct inline function use will lead to closure preserving oldState. 167 | // using partial to return function with only arguments preserved in closure environment. 168 | computedObj[key] = partial(fn, store); 169 | computedCache[key] = vue.computed(function () { return computedObj[key](); }); 170 | Object.defineProperty(store.getters, key, { 171 | get: function () { return computedCache[key].value; }, 172 | enumerable: true // for local getters 173 | }); 174 | }); 175 | }); 176 | 177 | store._state = vue.reactive({ 178 | data: state 179 | }); 180 | 181 | // register the newly created effect scope to the store so that we can 182 | // dispose the effects when this method runs again in the future. 183 | store._scope = scope; 184 | 185 | // enable strict mode for new state 186 | if (store.strict) { 187 | enableStrictMode(store); 188 | } 189 | 190 | if (oldState) { 191 | if (hot) { 192 | // dispatch changes in all subscribed watchers 193 | // to force getter re-evaluation for hot reloading. 194 | store._withCommit(function () { 195 | oldState.data = null; 196 | }); 197 | } 198 | } 199 | 200 | // dispose previously registered effect scope if there is one. 201 | if (oldScope) { 202 | oldScope.stop(); 203 | } 204 | } 205 | 206 | function installModule (store, rootState, path, module, hot) { 207 | var isRoot = !path.length; 208 | var namespace = store._modules.getNamespace(path); 209 | 210 | // register in namespace map 211 | if (module.namespaced) { 212 | if (store._modulesNamespaceMap[namespace] && true) { 213 | console.error(("[vuex] duplicate namespace " + namespace + " for the namespaced module " + (path.join('/')))); 214 | } 215 | store._modulesNamespaceMap[namespace] = module; 216 | } 217 | 218 | // set state 219 | if (!isRoot && !hot) { 220 | var parentState = getNestedState(rootState, path.slice(0, -1)); 221 | var moduleName = path[path.length - 1]; 222 | store._withCommit(function () { 223 | { 224 | if (moduleName in parentState) { 225 | console.warn( 226 | ("[vuex] state field \"" + moduleName + "\" was overridden by a module with the same name at \"" + (path.join('.')) + "\"") 227 | ); 228 | } 229 | } 230 | parentState[moduleName] = module.state; 231 | }); 232 | } 233 | 234 | var local = module.context = makeLocalContext(store, namespace, path); 235 | 236 | module.forEachMutation(function (mutation, key) { 237 | var namespacedType = namespace + key; 238 | registerMutation(store, namespacedType, mutation, local); 239 | }); 240 | 241 | module.forEachAction(function (action, key) { 242 | var type = action.root ? key : namespace + key; 243 | var handler = action.handler || action; 244 | registerAction(store, type, handler, local); 245 | }); 246 | 247 | module.forEachGetter(function (getter, key) { 248 | var namespacedType = namespace + key; 249 | registerGetter(store, namespacedType, getter, local); 250 | }); 251 | 252 | module.forEachChild(function (child, key) { 253 | installModule(store, rootState, path.concat(key), child, hot); 254 | }); 255 | } 256 | 257 | /** 258 | * make localized dispatch, commit, getters and state 259 | * if there is no namespace, just use root ones 260 | */ 261 | function makeLocalContext (store, namespace, path) { 262 | var noNamespace = namespace === ''; 263 | 264 | var local = { 265 | dispatch: noNamespace ? store.dispatch : function (_type, _payload, _options) { 266 | var args = unifyObjectStyle(_type, _payload, _options); 267 | var payload = args.payload; 268 | var options = args.options; 269 | var type = args.type; 270 | 271 | if (!options || !options.root) { 272 | type = namespace + type; 273 | if (!store._actions[type]) { 274 | console.error(("[vuex] unknown local action type: " + (args.type) + ", global type: " + type)); 275 | return 276 | } 277 | } 278 | 279 | return store.dispatch(type, payload) 280 | }, 281 | 282 | commit: noNamespace ? store.commit : function (_type, _payload, _options) { 283 | var args = unifyObjectStyle(_type, _payload, _options); 284 | var payload = args.payload; 285 | var options = args.options; 286 | var type = args.type; 287 | 288 | if (!options || !options.root) { 289 | type = namespace + type; 290 | if (!store._mutations[type]) { 291 | console.error(("[vuex] unknown local mutation type: " + (args.type) + ", global type: " + type)); 292 | return 293 | } 294 | } 295 | 296 | store.commit(type, payload, options); 297 | } 298 | }; 299 | 300 | // getters and state object must be gotten lazily 301 | // because they will be changed by state update 302 | Object.defineProperties(local, { 303 | getters: { 304 | get: noNamespace 305 | ? function () { return store.getters; } 306 | : function () { return makeLocalGetters(store, namespace); } 307 | }, 308 | state: { 309 | get: function () { return getNestedState(store.state, path); } 310 | } 311 | }); 312 | 313 | return local 314 | } 315 | 316 | function makeLocalGetters (store, namespace) { 317 | if (!store._makeLocalGettersCache[namespace]) { 318 | var gettersProxy = {}; 319 | var splitPos = namespace.length; 320 | Object.keys(store.getters).forEach(function (type) { 321 | // skip if the target getter is not match this namespace 322 | if (type.slice(0, splitPos) !== namespace) { return } 323 | 324 | // extract local getter type 325 | var localType = type.slice(splitPos); 326 | 327 | // Add a port to the getters proxy. 328 | // Define as getter property because 329 | // we do not want to evaluate the getters in this time. 330 | Object.defineProperty(gettersProxy, localType, { 331 | get: function () { return store.getters[type]; }, 332 | enumerable: true 333 | }); 334 | }); 335 | store._makeLocalGettersCache[namespace] = gettersProxy; 336 | } 337 | 338 | return store._makeLocalGettersCache[namespace] 339 | } 340 | 341 | function registerMutation (store, type, handler, local) { 342 | var entry = store._mutations[type] || (store._mutations[type] = []); 343 | entry.push(function wrappedMutationHandler (payload) { 344 | handler.call(store, local.state, payload); 345 | }); 346 | } 347 | 348 | function registerAction (store, type, handler, local) { 349 | var entry = store._actions[type] || (store._actions[type] = []); 350 | entry.push(function wrappedActionHandler (payload) { 351 | var res = handler.call(store, { 352 | dispatch: local.dispatch, 353 | commit: local.commit, 354 | getters: local.getters, 355 | state: local.state, 356 | rootGetters: store.getters, 357 | rootState: store.state 358 | }, payload); 359 | if (!isPromise(res)) { 360 | res = Promise.resolve(res); 361 | } 362 | if (store._devtoolHook) { 363 | return res.catch(function (err) { 364 | store._devtoolHook.emit('vuex:error', err); 365 | throw err 366 | }) 367 | } else { 368 | return res 369 | } 370 | }); 371 | } 372 | 373 | function registerGetter (store, type, rawGetter, local) { 374 | if (store._wrappedGetters[type]) { 375 | { 376 | console.error(("[vuex] duplicate getter key: " + type)); 377 | } 378 | return 379 | } 380 | store._wrappedGetters[type] = function wrappedGetter (store) { 381 | return rawGetter( 382 | local.state, // local state 383 | local.getters, // local getters 384 | store.state, // root state 385 | store.getters // root getters 386 | ) 387 | }; 388 | } 389 | 390 | function enableStrictMode (store) { 391 | vue.watch(function () { return store._state.data; }, function () { 392 | { 393 | assert(store._committing, "do not mutate vuex store state outside mutation handlers."); 394 | } 395 | }, { deep: true, flush: 'sync' }); 396 | } 397 | 398 | function getNestedState (state, path) { 399 | return path.reduce(function (state, key) { return state[key]; }, state) 400 | } 401 | 402 | function unifyObjectStyle (type, payload, options) { 403 | if (isObject(type) && type.type) { 404 | options = payload; 405 | payload = type; 406 | type = type.type; 407 | } 408 | 409 | { 410 | assert(typeof type === 'string', ("expects string as the type, but found " + (typeof type) + ".")); 411 | } 412 | 413 | return { type: type, payload: payload, options: options } 414 | } 415 | 416 | var LABEL_VUEX_BINDINGS = 'vuex bindings'; 417 | var MUTATIONS_LAYER_ID = 'vuex:mutations'; 418 | var ACTIONS_LAYER_ID = 'vuex:actions'; 419 | var INSPECTOR_ID = 'vuex'; 420 | 421 | var actionId = 0; 422 | 423 | function addDevtools (app, store) { 424 | setupDevtoolsPlugin( 425 | { 426 | id: 'org.vuejs.vuex', 427 | app: app, 428 | label: 'Vuex', 429 | homepage: 'https://next.vuex.vuejs.org/', 430 | logo: 'https://vuejs.org/images/icons/favicon-96x96.png', 431 | packageName: 'vuex', 432 | componentStateTypes: [LABEL_VUEX_BINDINGS] 433 | }, 434 | function (api) { 435 | api.addTimelineLayer({ 436 | id: MUTATIONS_LAYER_ID, 437 | label: 'Vuex Mutations', 438 | color: COLOR_LIME_500 439 | }); 440 | 441 | api.addTimelineLayer({ 442 | id: ACTIONS_LAYER_ID, 443 | label: 'Vuex Actions', 444 | color: COLOR_LIME_500 445 | }); 446 | 447 | api.addInspector({ 448 | id: INSPECTOR_ID, 449 | label: 'Vuex', 450 | icon: 'storage', 451 | treeFilterPlaceholder: 'Filter stores...' 452 | }); 453 | 454 | api.on.getInspectorTree(function (payload) { 455 | if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { 456 | if (payload.filter) { 457 | var nodes = []; 458 | flattenStoreForInspectorTree(nodes, store._modules.root, payload.filter, ''); 459 | payload.rootNodes = nodes; 460 | } else { 461 | payload.rootNodes = [ 462 | formatStoreForInspectorTree(store._modules.root, '') 463 | ]; 464 | } 465 | } 466 | }); 467 | 468 | api.on.getInspectorState(function (payload) { 469 | if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { 470 | var modulePath = payload.nodeId; 471 | makeLocalGetters(store, modulePath); 472 | payload.state = formatStoreForInspectorState( 473 | getStoreModule(store._modules, modulePath), 474 | modulePath === 'root' ? store.getters : store._makeLocalGettersCache, 475 | modulePath 476 | ); 477 | } 478 | }); 479 | 480 | api.on.editInspectorState(function (payload) { 481 | if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { 482 | var modulePath = payload.nodeId; 483 | var path = payload.path; 484 | if (modulePath !== 'root') { 485 | path = modulePath.split('/').filter(Boolean).concat( path); 486 | } 487 | store._withCommit(function () { 488 | payload.set(store._state.data, path, payload.state.value); 489 | }); 490 | } 491 | }); 492 | 493 | store.subscribe(function (mutation, state) { 494 | var data = {}; 495 | 496 | if (mutation.payload) { 497 | data.payload = mutation.payload; 498 | } 499 | 500 | data.state = state; 501 | 502 | api.notifyComponentUpdate(); 503 | api.sendInspectorTree(INSPECTOR_ID); 504 | api.sendInspectorState(INSPECTOR_ID); 505 | 506 | api.addTimelineEvent({ 507 | layerId: MUTATIONS_LAYER_ID, 508 | event: { 509 | time: Date.now(), 510 | title: mutation.type, 511 | data: data 512 | } 513 | }); 514 | }); 515 | 516 | store.subscribeAction({ 517 | before: function (action, state) { 518 | var data = {}; 519 | if (action.payload) { 520 | data.payload = action.payload; 521 | } 522 | action._id = actionId++; 523 | action._time = Date.now(); 524 | data.state = state; 525 | 526 | api.addTimelineEvent({ 527 | layerId: ACTIONS_LAYER_ID, 528 | event: { 529 | time: action._time, 530 | title: action.type, 531 | groupId: action._id, 532 | subtitle: 'start', 533 | data: data 534 | } 535 | }); 536 | }, 537 | after: function (action, state) { 538 | var data = {}; 539 | var duration = Date.now() - action._time; 540 | data.duration = { 541 | _custom: { 542 | type: 'duration', 543 | display: (duration + "ms"), 544 | tooltip: 'Action duration', 545 | value: duration 546 | } 547 | }; 548 | if (action.payload) { 549 | data.payload = action.payload; 550 | } 551 | data.state = state; 552 | 553 | api.addTimelineEvent({ 554 | layerId: ACTIONS_LAYER_ID, 555 | event: { 556 | time: Date.now(), 557 | title: action.type, 558 | groupId: action._id, 559 | subtitle: 'end', 560 | data: data 561 | } 562 | }); 563 | } 564 | }); 565 | } 566 | ); 567 | } 568 | 569 | // extracted from tailwind palette 570 | var COLOR_LIME_500 = 0x84cc16; 571 | var COLOR_DARK = 0x666666; 572 | var COLOR_WHITE = 0xffffff; 573 | 574 | var TAG_NAMESPACED = { 575 | label: 'namespaced', 576 | textColor: COLOR_WHITE, 577 | backgroundColor: COLOR_DARK 578 | }; 579 | 580 | /** 581 | * @param {string} path 582 | */ 583 | function extractNameFromPath (path) { 584 | return path && path !== 'root' ? path.split('/').slice(-2, -1)[0] : 'Root' 585 | } 586 | 587 | /** 588 | * @param {*} module 589 | * @return {import('@vue/devtools-api').CustomInspectorNode} 590 | */ 591 | function formatStoreForInspectorTree (module, path) { 592 | return { 593 | id: path || 'root', 594 | // all modules end with a `/`, we want the last segment only 595 | // cart/ -> cart 596 | // nested/cart/ -> cart 597 | label: extractNameFromPath(path), 598 | tags: module.namespaced ? [TAG_NAMESPACED] : [], 599 | children: Object.keys(module._children).map(function (moduleName) { return formatStoreForInspectorTree( 600 | module._children[moduleName], 601 | path + moduleName + '/' 602 | ); } 603 | ) 604 | } 605 | } 606 | 607 | /** 608 | * @param {import('@vue/devtools-api').CustomInspectorNode[]} result 609 | * @param {*} module 610 | * @param {string} filter 611 | * @param {string} path 612 | */ 613 | function flattenStoreForInspectorTree (result, module, filter, path) { 614 | if (path.includes(filter)) { 615 | result.push({ 616 | id: path || 'root', 617 | label: path.endsWith('/') ? path.slice(0, path.length - 1) : path || 'Root', 618 | tags: module.namespaced ? [TAG_NAMESPACED] : [] 619 | }); 620 | } 621 | Object.keys(module._children).forEach(function (moduleName) { 622 | flattenStoreForInspectorTree(result, module._children[moduleName], filter, path + moduleName + '/'); 623 | }); 624 | } 625 | 626 | /** 627 | * @param {*} module 628 | * @return {import('@vue/devtools-api').CustomInspectorState} 629 | */ 630 | function formatStoreForInspectorState (module, getters, path) { 631 | getters = path === 'root' ? getters : getters[path]; 632 | var gettersKeys = Object.keys(getters); 633 | var storeState = { 634 | state: Object.keys(module.state).map(function (key) { return ({ 635 | key: key, 636 | editable: true, 637 | value: module.state[key] 638 | }); }) 639 | }; 640 | 641 | if (gettersKeys.length) { 642 | var tree = transformPathsToObjectTree(getters); 643 | storeState.getters = Object.keys(tree).map(function (key) { return ({ 644 | key: key.endsWith('/') ? extractNameFromPath(key) : key, 645 | editable: false, 646 | value: canThrow(function () { return tree[key]; }) 647 | }); }); 648 | } 649 | 650 | return storeState 651 | } 652 | 653 | function transformPathsToObjectTree (getters) { 654 | var result = {}; 655 | Object.keys(getters).forEach(function (key) { 656 | var path = key.split('/'); 657 | if (path.length > 1) { 658 | var target = result; 659 | var leafKey = path.pop(); 660 | path.forEach(function (p) { 661 | if (!target[p]) { 662 | target[p] = { 663 | _custom: { 664 | value: {}, 665 | display: p, 666 | tooltip: 'Module', 667 | abstract: true 668 | } 669 | }; 670 | } 671 | target = target[p]._custom.value; 672 | }); 673 | target[leafKey] = canThrow(function () { return getters[key]; }); 674 | } else { 675 | result[key] = canThrow(function () { return getters[key]; }); 676 | } 677 | }); 678 | return result 679 | } 680 | 681 | function getStoreModule (moduleMap, path) { 682 | var names = path.split('/').filter(function (n) { return n; }); 683 | return names.reduce( 684 | function (module, moduleName, i) { 685 | var child = module[moduleName]; 686 | if (!child) { 687 | throw new Error(("Missing module \"" + moduleName + "\" for path \"" + path + "\".")) 688 | } 689 | return i === names.length - 1 ? child : child._children 690 | }, 691 | path === 'root' ? moduleMap : moduleMap.root._children 692 | ) 693 | } 694 | 695 | function canThrow (cb) { 696 | try { 697 | return cb() 698 | } catch (e) { 699 | return e 700 | } 701 | } 702 | 703 | // Base data struct for store's module, package with some attribute and method 704 | var Module = function Module (rawModule, runtime) { 705 | this.runtime = runtime; 706 | // Store some children item 707 | this._children = Object.create(null); 708 | // Store the origin module object which passed by programmer 709 | this._rawModule = rawModule; 710 | var rawState = rawModule.state; 711 | 712 | // Store the origin module's state 713 | this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}; 714 | }; 715 | 716 | var prototypeAccessors$1 = { namespaced: { configurable: true } }; 717 | 718 | prototypeAccessors$1.namespaced.get = function () { 719 | return !!this._rawModule.namespaced 720 | }; 721 | 722 | Module.prototype.addChild = function addChild (key, module) { 723 | this._children[key] = module; 724 | }; 725 | 726 | Module.prototype.removeChild = function removeChild (key) { 727 | delete this._children[key]; 728 | }; 729 | 730 | Module.prototype.getChild = function getChild (key) { 731 | return this._children[key] 732 | }; 733 | 734 | Module.prototype.hasChild = function hasChild (key) { 735 | return key in this._children 736 | }; 737 | 738 | Module.prototype.update = function update (rawModule) { 739 | this._rawModule.namespaced = rawModule.namespaced; 740 | if (rawModule.actions) { 741 | this._rawModule.actions = rawModule.actions; 742 | } 743 | if (rawModule.mutations) { 744 | this._rawModule.mutations = rawModule.mutations; 745 | } 746 | if (rawModule.getters) { 747 | this._rawModule.getters = rawModule.getters; 748 | } 749 | }; 750 | 751 | Module.prototype.forEachChild = function forEachChild (fn) { 752 | forEachValue(this._children, fn); 753 | }; 754 | 755 | Module.prototype.forEachGetter = function forEachGetter (fn) { 756 | if (this._rawModule.getters) { 757 | forEachValue(this._rawModule.getters, fn); 758 | } 759 | }; 760 | 761 | Module.prototype.forEachAction = function forEachAction (fn) { 762 | if (this._rawModule.actions) { 763 | forEachValue(this._rawModule.actions, fn); 764 | } 765 | }; 766 | 767 | Module.prototype.forEachMutation = function forEachMutation (fn) { 768 | if (this._rawModule.mutations) { 769 | forEachValue(this._rawModule.mutations, fn); 770 | } 771 | }; 772 | 773 | Object.defineProperties( Module.prototype, prototypeAccessors$1 ); 774 | 775 | var ModuleCollection = function ModuleCollection (rawRootModule) { 776 | // register root module (Vuex.Store options) 777 | this.register([], rawRootModule, false); 778 | }; 779 | 780 | ModuleCollection.prototype.get = function get (path) { 781 | return path.reduce(function (module, key) { 782 | return module.getChild(key) 783 | }, this.root) 784 | }; 785 | 786 | ModuleCollection.prototype.getNamespace = function getNamespace (path) { 787 | var module = this.root; 788 | return path.reduce(function (namespace, key) { 789 | module = module.getChild(key); 790 | return namespace + (module.namespaced ? key + '/' : '') 791 | }, '') 792 | }; 793 | 794 | ModuleCollection.prototype.update = function update$1 (rawRootModule) { 795 | update([], this.root, rawRootModule); 796 | }; 797 | 798 | ModuleCollection.prototype.register = function register (path, rawModule, runtime) { 799 | var this$1$1 = this; 800 | if ( runtime === void 0 ) runtime = true; 801 | 802 | { 803 | assertRawModule(path, rawModule); 804 | } 805 | 806 | var newModule = new Module(rawModule, runtime); 807 | if (path.length === 0) { 808 | this.root = newModule; 809 | } else { 810 | var parent = this.get(path.slice(0, -1)); 811 | parent.addChild(path[path.length - 1], newModule); 812 | } 813 | 814 | // register nested modules 815 | if (rawModule.modules) { 816 | forEachValue(rawModule.modules, function (rawChildModule, key) { 817 | this$1$1.register(path.concat(key), rawChildModule, runtime); 818 | }); 819 | } 820 | }; 821 | 822 | ModuleCollection.prototype.unregister = function unregister (path) { 823 | var parent = this.get(path.slice(0, -1)); 824 | var key = path[path.length - 1]; 825 | var child = parent.getChild(key); 826 | 827 | if (!child) { 828 | { 829 | console.warn( 830 | "[vuex] trying to unregister module '" + key + "', which is " + 831 | "not registered" 832 | ); 833 | } 834 | return 835 | } 836 | 837 | if (!child.runtime) { 838 | return 839 | } 840 | 841 | parent.removeChild(key); 842 | }; 843 | 844 | ModuleCollection.prototype.isRegistered = function isRegistered (path) { 845 | var parent = this.get(path.slice(0, -1)); 846 | var key = path[path.length - 1]; 847 | 848 | if (parent) { 849 | return parent.hasChild(key) 850 | } 851 | 852 | return false 853 | }; 854 | 855 | function update (path, targetModule, newModule) { 856 | { 857 | assertRawModule(path, newModule); 858 | } 859 | 860 | // update target module 861 | targetModule.update(newModule); 862 | 863 | // update nested modules 864 | if (newModule.modules) { 865 | for (var key in newModule.modules) { 866 | if (!targetModule.getChild(key)) { 867 | { 868 | console.warn( 869 | "[vuex] trying to add a new module '" + key + "' on hot reloading, " + 870 | 'manual reload is needed' 871 | ); 872 | } 873 | return 874 | } 875 | update( 876 | path.concat(key), 877 | targetModule.getChild(key), 878 | newModule.modules[key] 879 | ); 880 | } 881 | } 882 | } 883 | 884 | var functionAssert = { 885 | assert: function (value) { return typeof value === 'function'; }, 886 | expected: 'function' 887 | }; 888 | 889 | var objectAssert = { 890 | assert: function (value) { return typeof value === 'function' || 891 | (typeof value === 'object' && typeof value.handler === 'function'); }, 892 | expected: 'function or object with "handler" function' 893 | }; 894 | 895 | var assertTypes = { 896 | getters: functionAssert, 897 | mutations: functionAssert, 898 | actions: objectAssert 899 | }; 900 | 901 | function assertRawModule (path, rawModule) { 902 | Object.keys(assertTypes).forEach(function (key) { 903 | if (!rawModule[key]) { return } 904 | 905 | var assertOptions = assertTypes[key]; 906 | 907 | forEachValue(rawModule[key], function (value, type) { 908 | assert( 909 | assertOptions.assert(value), 910 | makeAssertionMessage(path, key, type, value, assertOptions.expected) 911 | ); 912 | }); 913 | }); 914 | } 915 | 916 | function makeAssertionMessage (path, key, type, value, expected) { 917 | var buf = key + " should be " + expected + " but \"" + key + "." + type + "\""; 918 | if (path.length > 0) { 919 | buf += " in module \"" + (path.join('.')) + "\""; 920 | } 921 | buf += " is " + (JSON.stringify(value)) + "."; 922 | return buf 923 | } 924 | 925 | function createStore (options) { 926 | return new Store(options) 927 | } 928 | 929 | var Store = function Store (options) { 930 | var this$1$1 = this; 931 | if ( options === void 0 ) options = {}; 932 | 933 | { 934 | assert(typeof Promise !== 'undefined', "vuex requires a Promise polyfill in this browser."); 935 | assert(this instanceof Store, "store must be called with the new operator."); 936 | } 937 | 938 | var plugins = options.plugins; if ( plugins === void 0 ) plugins = []; 939 | var strict = options.strict; if ( strict === void 0 ) strict = false; 940 | var devtools = options.devtools; 941 | 942 | // store internal state 943 | this._committing = false; 944 | this._actions = Object.create(null); 945 | this._actionSubscribers = []; 946 | this._mutations = Object.create(null); 947 | this._wrappedGetters = Object.create(null); 948 | this._modules = new ModuleCollection(options); 949 | this._modulesNamespaceMap = Object.create(null); 950 | this._subscribers = []; 951 | this._makeLocalGettersCache = Object.create(null); 952 | 953 | // EffectScope instance. when registering new getters, we wrap them inside 954 | // EffectScope so that getters (computed) would not be destroyed on 955 | // component unmount. 956 | this._scope = null; 957 | 958 | this._devtools = devtools; 959 | 960 | // bind commit and dispatch to self 961 | var store = this; 962 | var ref = this; 963 | var dispatch = ref.dispatch; 964 | var commit = ref.commit; 965 | this.dispatch = function boundDispatch (type, payload) { 966 | return dispatch.call(store, type, payload) 967 | }; 968 | this.commit = function boundCommit (type, payload, options) { 969 | return commit.call(store, type, payload, options) 970 | }; 971 | 972 | // strict mode 973 | this.strict = strict; 974 | 975 | var state = this._modules.root.state; 976 | 977 | // init root module. 978 | // this also recursively registers all sub-modules 979 | // and collects all module getters inside this._wrappedGetters 980 | installModule(this, state, [], this._modules.root); 981 | 982 | // initialize the store state, which is responsible for the reactivity 983 | // (also registers _wrappedGetters as computed properties) 984 | resetStoreState(this, state); 985 | 986 | // apply plugins 987 | plugins.forEach(function (plugin) { return plugin(this$1$1); }); 988 | }; 989 | 990 | var prototypeAccessors = { state: { configurable: true } }; 991 | 992 | Store.prototype.install = function install (app, injectKey) { 993 | app.provide(injectKey || storeKey, this); 994 | app.config.globalProperties.$store = this; 995 | 996 | var useDevtools = this._devtools !== undefined 997 | ? this._devtools 998 | : true ; 999 | 1000 | if (useDevtools) { 1001 | addDevtools(app, this); 1002 | } 1003 | }; 1004 | 1005 | prototypeAccessors.state.get = function () { 1006 | return this._state.data 1007 | }; 1008 | 1009 | prototypeAccessors.state.set = function (v) { 1010 | { 1011 | assert(false, "use store.replaceState() to explicit replace store state."); 1012 | } 1013 | }; 1014 | 1015 | Store.prototype.commit = function commit (_type, _payload, _options) { 1016 | var this$1$1 = this; 1017 | 1018 | // check object-style commit 1019 | var ref = unifyObjectStyle(_type, _payload, _options); 1020 | var type = ref.type; 1021 | var payload = ref.payload; 1022 | var options = ref.options; 1023 | 1024 | var mutation = { type: type, payload: payload }; 1025 | var entry = this._mutations[type]; 1026 | if (!entry) { 1027 | { 1028 | console.error(("[vuex] unknown mutation type: " + type)); 1029 | } 1030 | return 1031 | } 1032 | this._withCommit(function () { 1033 | entry.forEach(function commitIterator (handler) { 1034 | handler(payload); 1035 | }); 1036 | }); 1037 | 1038 | this._subscribers 1039 | .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe 1040 | .forEach(function (sub) { return sub(mutation, this$1$1.state); }); 1041 | 1042 | if ( 1043 | options && options.silent 1044 | ) { 1045 | console.warn( 1046 | "[vuex] mutation type: " + type + ". Silent option has been removed. " + 1047 | 'Use the filter functionality in the vue-devtools' 1048 | ); 1049 | } 1050 | }; 1051 | 1052 | Store.prototype.dispatch = function dispatch (_type, _payload) { 1053 | var this$1$1 = this; 1054 | 1055 | // check object-style dispatch 1056 | var ref = unifyObjectStyle(_type, _payload); 1057 | var type = ref.type; 1058 | var payload = ref.payload; 1059 | 1060 | var action = { type: type, payload: payload }; 1061 | var entry = this._actions[type]; 1062 | if (!entry) { 1063 | { 1064 | console.error(("[vuex] unknown action type: " + type)); 1065 | } 1066 | return 1067 | } 1068 | 1069 | try { 1070 | this._actionSubscribers 1071 | .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe 1072 | .filter(function (sub) { return sub.before; }) 1073 | .forEach(function (sub) { return sub.before(action, this$1$1.state); }); 1074 | } catch (e) { 1075 | { 1076 | console.warn("[vuex] error in before action subscribers: "); 1077 | console.error(e); 1078 | } 1079 | } 1080 | 1081 | var result = entry.length > 1 1082 | ? Promise.all(entry.map(function (handler) { return handler(payload); })) 1083 | : entry[0](payload); 1084 | 1085 | return new Promise(function (resolve, reject) { 1086 | result.then(function (res) { 1087 | try { 1088 | this$1$1._actionSubscribers 1089 | .filter(function (sub) { return sub.after; }) 1090 | .forEach(function (sub) { return sub.after(action, this$1$1.state); }); 1091 | } catch (e) { 1092 | { 1093 | console.warn("[vuex] error in after action subscribers: "); 1094 | console.error(e); 1095 | } 1096 | } 1097 | resolve(res); 1098 | }, function (error) { 1099 | try { 1100 | this$1$1._actionSubscribers 1101 | .filter(function (sub) { return sub.error; }) 1102 | .forEach(function (sub) { return sub.error(action, this$1$1.state, error); }); 1103 | } catch (e) { 1104 | { 1105 | console.warn("[vuex] error in error action subscribers: "); 1106 | console.error(e); 1107 | } 1108 | } 1109 | reject(error); 1110 | }); 1111 | }) 1112 | }; 1113 | 1114 | Store.prototype.subscribe = function subscribe (fn, options) { 1115 | return genericSubscribe(fn, this._subscribers, options) 1116 | }; 1117 | 1118 | Store.prototype.subscribeAction = function subscribeAction (fn, options) { 1119 | var subs = typeof fn === 'function' ? { before: fn } : fn; 1120 | return genericSubscribe(subs, this._actionSubscribers, options) 1121 | }; 1122 | 1123 | Store.prototype.watch = function watch$1 (getter, cb, options) { 1124 | var this$1$1 = this; 1125 | 1126 | { 1127 | assert(typeof getter === 'function', "store.watch only accepts a function."); 1128 | } 1129 | return vue.watch(function () { return getter(this$1$1.state, this$1$1.getters); }, cb, Object.assign({}, options)) 1130 | }; 1131 | 1132 | Store.prototype.replaceState = function replaceState (state) { 1133 | var this$1$1 = this; 1134 | 1135 | this._withCommit(function () { 1136 | this$1$1._state.data = state; 1137 | }); 1138 | }; 1139 | 1140 | Store.prototype.registerModule = function registerModule (path, rawModule, options) { 1141 | if ( options === void 0 ) options = {}; 1142 | 1143 | if (typeof path === 'string') { path = [path]; } 1144 | 1145 | { 1146 | assert(Array.isArray(path), "module path must be a string or an Array."); 1147 | assert(path.length > 0, 'cannot register the root module by using registerModule.'); 1148 | } 1149 | 1150 | this._modules.register(path, rawModule); 1151 | installModule(this, this.state, path, this._modules.get(path), options.preserveState); 1152 | // reset store to update getters... 1153 | resetStoreState(this, this.state); 1154 | }; 1155 | 1156 | Store.prototype.unregisterModule = function unregisterModule (path) { 1157 | var this$1$1 = this; 1158 | 1159 | if (typeof path === 'string') { path = [path]; } 1160 | 1161 | { 1162 | assert(Array.isArray(path), "module path must be a string or an Array."); 1163 | } 1164 | 1165 | this._modules.unregister(path); 1166 | this._withCommit(function () { 1167 | var parentState = getNestedState(this$1$1.state, path.slice(0, -1)); 1168 | delete parentState[path[path.length - 1]]; 1169 | }); 1170 | resetStore(this); 1171 | }; 1172 | 1173 | Store.prototype.hasModule = function hasModule (path) { 1174 | if (typeof path === 'string') { path = [path]; } 1175 | 1176 | { 1177 | assert(Array.isArray(path), "module path must be a string or an Array."); 1178 | } 1179 | 1180 | return this._modules.isRegistered(path) 1181 | }; 1182 | 1183 | Store.prototype.hotUpdate = function hotUpdate (newOptions) { 1184 | this._modules.update(newOptions); 1185 | resetStore(this, true); 1186 | }; 1187 | 1188 | Store.prototype._withCommit = function _withCommit (fn) { 1189 | var committing = this._committing; 1190 | this._committing = true; 1191 | fn(); 1192 | this._committing = committing; 1193 | }; 1194 | 1195 | Object.defineProperties( Store.prototype, prototypeAccessors ); 1196 | 1197 | /** 1198 | * Reduce the code which written in Vue.js for getting the state. 1199 | * @param {String} [namespace] - Module's namespace 1200 | * @param {Object|Array} states # Object's item can be a function which accept state and getters for param, you can do something for state and getters in it. 1201 | * @param {Object} 1202 | */ 1203 | var mapState = normalizeNamespace(function (namespace, states) { 1204 | var res = {}; 1205 | if (!isValidMap(states)) { 1206 | console.error('[vuex] mapState: mapper parameter must be either an Array or an Object'); 1207 | } 1208 | normalizeMap(states).forEach(function (ref) { 1209 | var key = ref.key; 1210 | var val = ref.val; 1211 | 1212 | res[key] = function mappedState () { 1213 | var state = this.$store.state; 1214 | var getters = this.$store.getters; 1215 | if (namespace) { 1216 | var module = getModuleByNamespace(this.$store, 'mapState', namespace); 1217 | if (!module) { 1218 | return 1219 | } 1220 | state = module.context.state; 1221 | getters = module.context.getters; 1222 | } 1223 | return typeof val === 'function' 1224 | ? val.call(this, state, getters) 1225 | : state[val] 1226 | }; 1227 | // mark vuex getter for devtools 1228 | res[key].vuex = true; 1229 | }); 1230 | return res 1231 | }); 1232 | 1233 | /** 1234 | * Reduce the code which written in Vue.js for committing the mutation 1235 | * @param {String} [namespace] - Module's namespace 1236 | * @param {Object|Array} mutations # Object's item can be a function which accept `commit` function as the first param, it can accept another params. You can commit mutation and do any other things in this function. specially, You need to pass anthor params from the mapped function. 1237 | * @return {Object} 1238 | */ 1239 | var mapMutations = normalizeNamespace(function (namespace, mutations) { 1240 | var res = {}; 1241 | if (!isValidMap(mutations)) { 1242 | console.error('[vuex] mapMutations: mapper parameter must be either an Array or an Object'); 1243 | } 1244 | normalizeMap(mutations).forEach(function (ref) { 1245 | var key = ref.key; 1246 | var val = ref.val; 1247 | 1248 | res[key] = function mappedMutation () { 1249 | var args = [], len = arguments.length; 1250 | while ( len-- ) args[ len ] = arguments[ len ]; 1251 | 1252 | // Get the commit method from store 1253 | var commit = this.$store.commit; 1254 | if (namespace) { 1255 | var module = getModuleByNamespace(this.$store, 'mapMutations', namespace); 1256 | if (!module) { 1257 | return 1258 | } 1259 | commit = module.context.commit; 1260 | } 1261 | return typeof val === 'function' 1262 | ? val.apply(this, [commit].concat(args)) 1263 | : commit.apply(this.$store, [val].concat(args)) 1264 | }; 1265 | }); 1266 | return res 1267 | }); 1268 | 1269 | /** 1270 | * Reduce the code which written in Vue.js for getting the getters 1271 | * @param {String} [namespace] - Module's namespace 1272 | * @param {Object|Array} getters 1273 | * @return {Object} 1274 | */ 1275 | var mapGetters = normalizeNamespace(function (namespace, getters) { 1276 | var res = {}; 1277 | if (!isValidMap(getters)) { 1278 | console.error('[vuex] mapGetters: mapper parameter must be either an Array or an Object'); 1279 | } 1280 | normalizeMap(getters).forEach(function (ref) { 1281 | var key = ref.key; 1282 | var val = ref.val; 1283 | 1284 | // The namespace has been mutated by normalizeNamespace 1285 | val = namespace + val; 1286 | res[key] = function mappedGetter () { 1287 | if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) { 1288 | return 1289 | } 1290 | if (!(val in this.$store.getters)) { 1291 | console.error(("[vuex] unknown getter: " + val)); 1292 | return 1293 | } 1294 | return this.$store.getters[val] 1295 | }; 1296 | // mark vuex getter for devtools 1297 | res[key].vuex = true; 1298 | }); 1299 | return res 1300 | }); 1301 | 1302 | /** 1303 | * Reduce the code which written in Vue.js for dispatch the action 1304 | * @param {String} [namespace] - Module's namespace 1305 | * @param {Object|Array} actions # Object's item can be a function which accept `dispatch` function as the first param, it can accept anthor params. You can dispatch action and do any other things in this function. specially, You need to pass anthor params from the mapped function. 1306 | * @return {Object} 1307 | */ 1308 | var mapActions = normalizeNamespace(function (namespace, actions) { 1309 | var res = {}; 1310 | if (!isValidMap(actions)) { 1311 | console.error('[vuex] mapActions: mapper parameter must be either an Array or an Object'); 1312 | } 1313 | normalizeMap(actions).forEach(function (ref) { 1314 | var key = ref.key; 1315 | var val = ref.val; 1316 | 1317 | res[key] = function mappedAction () { 1318 | var args = [], len = arguments.length; 1319 | while ( len-- ) args[ len ] = arguments[ len ]; 1320 | 1321 | // get dispatch function from store 1322 | var dispatch = this.$store.dispatch; 1323 | if (namespace) { 1324 | var module = getModuleByNamespace(this.$store, 'mapActions', namespace); 1325 | if (!module) { 1326 | return 1327 | } 1328 | dispatch = module.context.dispatch; 1329 | } 1330 | return typeof val === 'function' 1331 | ? val.apply(this, [dispatch].concat(args)) 1332 | : dispatch.apply(this.$store, [val].concat(args)) 1333 | }; 1334 | }); 1335 | return res 1336 | }); 1337 | 1338 | /** 1339 | * Rebinding namespace param for mapXXX function in special scoped, and return them by simple object 1340 | * @param {String} namespace 1341 | * @return {Object} 1342 | */ 1343 | var createNamespacedHelpers = function (namespace) { return ({ 1344 | mapState: mapState.bind(null, namespace), 1345 | mapGetters: mapGetters.bind(null, namespace), 1346 | mapMutations: mapMutations.bind(null, namespace), 1347 | mapActions: mapActions.bind(null, namespace) 1348 | }); }; 1349 | 1350 | /** 1351 | * Normalize the map 1352 | * normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ] 1353 | * normalizeMap({a: 1, b: 2, c: 3}) => [ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 } ] 1354 | * @param {Array|Object} map 1355 | * @return {Object} 1356 | */ 1357 | function normalizeMap (map) { 1358 | if (!isValidMap(map)) { 1359 | return [] 1360 | } 1361 | return Array.isArray(map) 1362 | ? map.map(function (key) { return ({ key: key, val: key }); }) 1363 | : Object.keys(map).map(function (key) { return ({ key: key, val: map[key] }); }) 1364 | } 1365 | 1366 | /** 1367 | * Validate whether given map is valid or not 1368 | * @param {*} map 1369 | * @return {Boolean} 1370 | */ 1371 | function isValidMap (map) { 1372 | return Array.isArray(map) || isObject(map) 1373 | } 1374 | 1375 | /** 1376 | * Return a function expect two param contains namespace and map. it will normalize the namespace and then the param's function will handle the new namespace and the map. 1377 | * @param {Function} fn 1378 | * @return {Function} 1379 | */ 1380 | function normalizeNamespace (fn) { 1381 | return function (namespace, map) { 1382 | if (typeof namespace !== 'string') { 1383 | map = namespace; 1384 | namespace = ''; 1385 | } else if (namespace.charAt(namespace.length - 1) !== '/') { 1386 | namespace += '/'; 1387 | } 1388 | return fn(namespace, map) 1389 | } 1390 | } 1391 | 1392 | /** 1393 | * Search a special module from store by namespace. if module not exist, print error message. 1394 | * @param {Object} store 1395 | * @param {String} helper 1396 | * @param {String} namespace 1397 | * @return {Object} 1398 | */ 1399 | function getModuleByNamespace (store, helper, namespace) { 1400 | var module = store._modulesNamespaceMap[namespace]; 1401 | if (!module) { 1402 | console.error(("[vuex] module namespace not found in " + helper + "(): " + namespace)); 1403 | } 1404 | return module 1405 | } 1406 | 1407 | // Credits: borrowed code from fcomb/redux-logger 1408 | 1409 | function createLogger (ref) { 1410 | if ( ref === void 0 ) ref = {}; 1411 | var collapsed = ref.collapsed; if ( collapsed === void 0 ) collapsed = true; 1412 | var filter = ref.filter; if ( filter === void 0 ) filter = function (mutation, stateBefore, stateAfter) { return true; }; 1413 | var transformer = ref.transformer; if ( transformer === void 0 ) transformer = function (state) { return state; }; 1414 | var mutationTransformer = ref.mutationTransformer; if ( mutationTransformer === void 0 ) mutationTransformer = function (mut) { return mut; }; 1415 | var actionFilter = ref.actionFilter; if ( actionFilter === void 0 ) actionFilter = function (action, state) { return true; }; 1416 | var actionTransformer = ref.actionTransformer; if ( actionTransformer === void 0 ) actionTransformer = function (act) { return act; }; 1417 | var logMutations = ref.logMutations; if ( logMutations === void 0 ) logMutations = true; 1418 | var logActions = ref.logActions; if ( logActions === void 0 ) logActions = true; 1419 | var logger = ref.logger; if ( logger === void 0 ) logger = console; 1420 | 1421 | return function (store) { 1422 | var prevState = deepCopy(store.state); 1423 | 1424 | if (typeof logger === 'undefined') { 1425 | return 1426 | } 1427 | 1428 | if (logMutations) { 1429 | store.subscribe(function (mutation, state) { 1430 | var nextState = deepCopy(state); 1431 | 1432 | if (filter(mutation, prevState, nextState)) { 1433 | var formattedTime = getFormattedTime(); 1434 | var formattedMutation = mutationTransformer(mutation); 1435 | var message = "mutation " + (mutation.type) + formattedTime; 1436 | 1437 | startMessage(logger, message, collapsed); 1438 | logger.log('%c prev state', 'color: #9E9E9E; font-weight: bold', transformer(prevState)); 1439 | logger.log('%c mutation', 'color: #03A9F4; font-weight: bold', formattedMutation); 1440 | logger.log('%c next state', 'color: #4CAF50; font-weight: bold', transformer(nextState)); 1441 | endMessage(logger); 1442 | } 1443 | 1444 | prevState = nextState; 1445 | }); 1446 | } 1447 | 1448 | if (logActions) { 1449 | store.subscribeAction(function (action, state) { 1450 | if (actionFilter(action, state)) { 1451 | var formattedTime = getFormattedTime(); 1452 | var formattedAction = actionTransformer(action); 1453 | var message = "action " + (action.type) + formattedTime; 1454 | 1455 | startMessage(logger, message, collapsed); 1456 | logger.log('%c action', 'color: #03A9F4; font-weight: bold', formattedAction); 1457 | endMessage(logger); 1458 | } 1459 | }); 1460 | } 1461 | } 1462 | } 1463 | 1464 | function startMessage (logger, message, collapsed) { 1465 | var startMessage = collapsed 1466 | ? logger.groupCollapsed 1467 | : logger.group; 1468 | 1469 | // render 1470 | try { 1471 | startMessage.call(logger, message); 1472 | } catch (e) { 1473 | logger.log(message); 1474 | } 1475 | } 1476 | 1477 | function endMessage (logger) { 1478 | try { 1479 | logger.groupEnd(); 1480 | } catch (e) { 1481 | logger.log('—— log end ——'); 1482 | } 1483 | } 1484 | 1485 | function getFormattedTime () { 1486 | var time = new Date(); 1487 | return (" @ " + (pad(time.getHours(), 2)) + ":" + (pad(time.getMinutes(), 2)) + ":" + (pad(time.getSeconds(), 2)) + "." + (pad(time.getMilliseconds(), 3))) 1488 | } 1489 | 1490 | function repeat (str, times) { 1491 | return (new Array(times + 1)).join(str) 1492 | } 1493 | 1494 | function pad (num, maxLength) { 1495 | return repeat('0', maxLength - num.toString().length) + num 1496 | } 1497 | 1498 | var index_cjs = { 1499 | version: '4.1.0', 1500 | Store: Store, 1501 | storeKey: storeKey, 1502 | createStore: createStore, 1503 | useStore: useStore, 1504 | mapState: mapState, 1505 | mapMutations: mapMutations, 1506 | mapGetters: mapGetters, 1507 | mapActions: mapActions, 1508 | createNamespacedHelpers: createNamespacedHelpers, 1509 | createLogger: createLogger 1510 | }; 1511 | 1512 | return index_cjs; 1513 | 1514 | }(Vue)); 1515 | --------------------------------------------------------------------------------