new seatbelt script

This commit is contained in:
KingMcDonalds
2025-06-29 15:09:12 -07:00
parent c125a70f57
commit a1f00d73d0
24 changed files with 3378 additions and 2 deletions
@@ -15,7 +15,7 @@
<animConvRoofName>null</animConvRoofName>
<animConvRoofWindowsAffected />
<ptfxAssetName>null</ptfxAssetName>
<audioNameHash>GUARDIAN</audioNameHash>
<audioNameHash>kc80cumminsn14jake</audioNameHash>
<layout>LAYOUT_TRUCK</layout>
<coverBoundOffsets>PHANTOM_COVER_OFFSET_INFO</coverBoundOffsets>
<explosionInfo>EXPLOSION_INFO_TRUCK</explosionInfo>
+2
View File
@@ -0,0 +1,2 @@
static/*
!static/**/*.js
+172
View File
@@ -0,0 +1,172 @@
{
"root": true,
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"parserOptions": {
"ecmaVersion": 2021
},
"env": {
"es2021": true,
"browser": true,
"jquery": true
},
"rules": {
"prettier/prettier": [
2,
{
"printWidth": 120,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "all",
"endOfLine": "lf",
"arrowParens": "avoid"
}
],
"strict": ["error", "global"],
"no-await-in-loop": "warn",
"no-compare-neg-zero": "error",
"no-template-curly-in-string": "error",
"no-unsafe-negation": "error",
"valid-jsdoc": [
"error",
{
"requireReturn": false,
"requireReturnDescription": false,
"prefer": {
"return": "returns",
"arg": "param"
},
"preferType": {
"String": "string",
"Number": "number",
"Boolean": "boolean",
"Symbol": "symbol",
"object": "Object",
"function": "Function",
"array": "Array",
"date": "Date",
"error": "Error",
"null": "void"
}
}
],
"accessor-pairs": "warn",
"array-callback-return": "error",
"consistent-return": "error",
"curly": ["error", "multi-line", "consistent"],
"dot-location": ["error", "property"],
"dot-notation": "error",
"eqeqeq": "error",
"no-empty-function": "error",
"no-floating-decimal": "error",
"no-implied-eval": "error",
"no-invalid-this": "error",
"no-lone-blocks": "error",
"no-multi-spaces": "error",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-new": "error",
"no-octal-escape": "error",
"no-return-assign": "error",
"no-return-await": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-throw-literal": "error",
"no-unmodified-loop-condition": "error",
"no-unused-expressions": "error",
"no-useless-call": "error",
"no-useless-concat": "error",
"no-useless-escape": "error",
"no-useless-return": "error",
"no-void": "error",
"no-warning-comments": "warn",
"prefer-promise-reject-errors": "error",
"require-await": "warn",
"wrap-iife": "error",
"yoda": "error",
"no-label-var": "error",
"no-shadow": "error",
"no-undef-init": "error",
"callback-return": "error",
"getter-return": "off",
"handle-callback-err": "error",
"no-mixed-requires": "error",
"no-new-require": "error",
"no-path-concat": "error",
"array-bracket-spacing": "error",
"block-spacing": "error",
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"capitalized-comments": ["error", "always", { "ignoreConsecutiveComments": true }],
"comma-dangle": ["error", "always-multiline"],
"comma-spacing": "error",
"comma-style": "error",
"computed-property-spacing": "error",
"consistent-this": ["error", "$this"],
"eol-last": "error",
"func-names": "error",
"func-name-matching": "error",
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"key-spacing": "error",
"keyword-spacing": "error",
"max-depth": "error",
"max-len": ["error", 120, 2],
"max-nested-callbacks": ["error", { "max": 4 }],
"max-statements-per-line": ["error", { "max": 2 }],
"new-cap": "off",
"newline-per-chained-call": ["error", { "ignoreChainWithDepth": 3 }],
"no-array-constructor": "error",
"no-inline-comments": "error",
"no-lonely-if": "error",
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
"no-new-object": "error",
"no-spaced-func": "error",
"no-trailing-spaces": "error",
"no-unneeded-ternary": "error",
"no-whitespace-before-property": "error",
"nonblock-statement-body-position": "error",
"object-curly-spacing": ["error", "always"],
"operator-assignment": "error",
"padded-blocks": ["error", "never"],
"quote-props": ["error", "as-needed"],
"quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }],
"semi-spacing": "error",
"semi": "error",
"space-before-blocks": "error",
"space-before-function-paren": [
"error",
{
"anonymous": "never",
"named": "never",
"asyncArrow": "always"
}
],
"space-in-parens": "error",
"space-infix-ops": "error",
"space-unary-ops": "error",
"spaced-comment": ["error", "always", { "markers": ["/"] }],
"template-tag-spacing": "error",
"unicode-bom": "error",
"arrow-body-style": "error",
"arrow-parens": ["error", "as-needed"],
"arrow-spacing": "error",
"no-duplicate-imports": "error",
"no-useless-computed-key": "error",
"no-useless-constructor": "error",
"prefer-arrow-callback": "error",
"prefer-numeric-literals": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
"prefer-object-has-own": "error",
"rest-spread-spacing": "error",
"template-curly-spacing": "error",
"yield-star-spacing": "error"
},
"globals": {
"Howl": "readonly"
}
}
+132
View File
@@ -0,0 +1,132 @@
config.lua
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
+2
View File
@@ -0,0 +1,2 @@
static/*
!static/**/*.css
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "stylelint-config-standard"
}
+1
View File
@@ -0,0 +1 @@
nodeLinker: node-modules
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Reece Stokes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+70
View File
@@ -0,0 +1,70 @@
# Seatbelt
## About
A modern realisation on seatbelt scripts, for additional roleplay and realism to your server.
## Installation
- Install to your `resource` folder (or [download manually](https://github.com/TFNRP/seatbelt/archive/refs/heads/main.zip)):
```bash
git clone https://github.com/TFNRP/seatbelt.git
```
- (Optional) Copy `config.default.lua`, rename the copy to `config.lua` and edit it to your liking.
- Add the following to your `server.cfg`:
- `ensure seatbelt`
- (Optional) `add_ace group.police seatbelt.notify allow`
Replace `police` with your LEO group.
This is optional if you don't use ACL - read [Configuration](#configuration).
## Features
Modern Seatbelt does the following:
- Buckle up with `/seatbelt` or <kbd>K</kbd>.
- Keybinds can be changed in FiveM keybind settings.
- Quick unbuckle by pressing <kbd>Shift</kbd> + <kbd>F</kbd>.
- Heftier consequences for MVAs.
- Law enforcement notified of traffic violation
when driving behind (or crossing infront).
- Out-of-the-way notifications whilst driving
without seatbelt.
- Lightweight, out of the box.
- Supports the TFNRP framework.
- Realistic sound effects for buckling & driving w/o SB.
## Coming Soon
Features that are planned for Modern Seatbelt:
- Complimentary scripts for compatible UI support.
## Configuration
> Note: Do not edit `config.default.lua`
Copy `config.default.lua` and rename the copy to `config.lua`.
Check [config.lua](https://github.com/TFNRP/seatbelt/blob/main/config.default.lua) for better details.
property | description
-- | --
`LeoCheck` = `2` | The method this script should use to check whether a player is an (on-duty) LEO.</br>`1` = be inside a police vehicle; or</br>`2` = have the `seatbelt.notify` ACL.
`PlayerIdentifierType` = `1` | What type of LEO notifications should be shown?</br>`1` = Player ID (i.e. "Player 7")</br>`2` = Seat position (i.e. "Driver", "Passenger", "Rear left passenger", "Far rear left passenger")</br>`3` = Username (i.e. "Hagen Hyena", "1D-32 Backer P.")
`Distance` = `20` | The amount of distance LEOs have to be to detect people not weaing a seatbelt.
`DefaultKeybind` = `K` | The default seatbelt keybind.
`Excluded` | A list of excluded vehicles and vehicle seats.
## Contributing
[Reports issues and create suggestions](https://github.com/TFNRP/seatbelt/issues).
[Improve code, fix issues and add suggestions](https://github.com/TFNRP/seatbelt/pulls).
## Credits & Copyright
- [@TehRamsus](https://forum.cfx.re/u/TehRamsus) for their [Seatbelt](https://github.com/TehRamsus/Seatbelt) script, which inspired me to create Modern Seatbelt.
- [@Mercy3](https://forum.cfx.re/u/Mercy3) for providing and giving permission of use of `static/assets/{buckle,chime,unbuckle}.ogg` files.
Licensed under [MIT License](https://github.com/TFNRP/seatbelt/blob/main/LICENSE). No code or assets of the inspiring work has been used.
The author of [Seatbelt](https://github.com/TehRamsus/Seatbelt) stated they would rework their original work but has not done so since Oct 25th, '21, hence my release.
+177
View File
@@ -0,0 +1,177 @@
local showingWarning = false
local lastSpeed = 0
local lastVelocity = vector3(0, 0, 0)
local newbieBeep = true
local showHelp = false
local activated
local hasSeatbelt
RegisterKeyMapping('seatbelt', 'Seatbelt', 'keyboard', Constants.DefaultKeybind)
RegisterFrameworkCommand('seatbelt', function()
local ped = PlayerPedId()
if IsPedInAnyVehicle(ped) then
local vehcileHasSeatbelt, strong = DoesPedVehicleHaveSeatbelt(ped)
if vehcileHasSeatbelt and not strong then
if activated then
DeactivateSeatbelt()
else
ActivateSeatbelt()
end
SendNuiMessage('{"t":0,"d":' .. (activated == true and '1' or '0') .. '}')
end
end
end)
function ActivateSeatbelt()
if activated == true then
return error('seatbelt attempted to activate when already active.')
end
-- compat for other resources like carhud
TriggerEvent('seatbelt:stateChange', true)
-- disable exit keys
Citizen.CreateThread(function()
while activated do
Citizen.Wait(1)
DisableControlAction(0, 75, true)
DisableControlAction(27, 75, true)
end
end)
-- quick unbuckled
Citizen.CreateThread(function()
while activated do
Citizen.Wait(1)
if IsDisabledControlJustPressed(0, 75) and IsControlPressed(0, 21) then
DeactivateSeatbelt()
end
end
end)
-- validation
Citizen.CreateThread(function()
while activated do
if not IsPedInAnyVehicle(PlayerPedId()) then
DeactivateSeatbelt()
end
Citizen.Wait(50)
end
end)
activated = true
end
function DeactivateSeatbelt()
if activated == false then
return error('seatbelt attempted to deactivate when already deactivated.')
end
TriggerEvent('seatbelt:stateChange', false)
-- help text separate from hud
Citizen.CreateThread(function()
while not activated do
if showHelp then
local message = '~BLIP_GANG_VEHICLE~ Press ~INPUT_REPLAY_SHOWHOTKEY~ to ~y~buckle~s~ your seatbelt.'
ShowHelpText(message, newbieBeep)
newbieBeep = false
for _ = 0, 8 do
Citizen.Wait(5)
ShowHelpText(message, false)
end
end
Citizen.Wait(65)
end
end)
-- handling and HUD
Citizen.CreateThread(function()
while not activated do
local ped = PlayerPedId()
if IsPedInAnyVehicle(ped) then
local _hasSeatbelt, strong = DoesPedVehicleHaveSeatbelt(ped)
hasSeatbelt = _hasSeatbelt
if hasSeatbelt and not strong then
local vehicle = GetVehiclePedIsIn(ped)
local speed = GetEntitySpeed(vehicle)
if speed > (50 / 3.6) and (lastSpeed - speed) > (speed * .2) then
local coords = GetEntityCoords(ped)
local fw = Fwv(ped)
ForceStopWarning()
showHelp = false
SetEntityCoords(ped, coords.x + fw.x, coords.y + fw.y, coords.z - .47, true, true, true)
SetEntityVelocity(ped, lastVelocity.x, lastVelocity.y, lastVelocity.z)
SetPedToRagdoll(ped, 1e3, 1e3, 0, false, false, false)
elseif speed > (20 / 3.6) then
SetWarning(true)
showHelp = not (IsPauseMenuActive() or IsHudHidden() or IsPlayerDead(PlayerId()))
else
SetWarning(false)
showHelp = false
end
lastSpeed = speed
lastVelocity = GetEntityVelocity(vehicle)
end
else
ForceStopWarning()
showHelp = false
end
Citizen.Wait(50)
end
SetWarning(false)
showHelp = false
end)
-- notification
Citizen.CreateThread(function()
while not activated do
local ped = PlayerPedId()
local vehicle = GetVehiclePedIsIn(ped)
if IsPedInAnyVehicle(ped) and hasSeatbelt and GetEntitySpeed(vehicle) * 3.6 > 10 then
TriggerServerEvent('seatbelt:ServerNotify')
end
Citizen.Wait(3e3)
end
end)
activated = false
end
function SetWarning(bool)
if bool ~= showingWarning then
SendNuiMessage('{"t":1,"d":' .. (bool == true and '1' or '0') .. '}')
showingWarning = bool
end
end
function ForceStopWarning()
if showingWarning then
SendNuiMessage('{"t":1,"d":3}')
showingWarning = false
end
end
RegisterNetEvent('seatbelt:ClientNotify', function(serverId)
local ped = PlayerPedId()
local player = GetPlayerFromServerId(serverId) -- onesync notice: returns -1 if not loaded
local playerPed = GetPlayerPed(player)
if player ~= PlayerId() and player > 0 and IsLEO() and not IsHudHidden() and IsPedInAnyVehicle(playerPed) then
local vehicle = GetVehiclePedIsIn(playerPed)
local identifier = GetPlayerIdentifier_(serverId, playerPed, vehicle)
if #(GetEntityCoords(ped) - GetEntityCoords(GetPlayerPed(player))) < Constants.Distance and identifier then
ShowNotification(
identifier ..
' is not weaing a seatbelt in <C>~y~' ..
GetVehicleNumberPlateText(vehicle) ..
'~s~</C>.'
)
end
end
end)
Citizen.CreateThread(function()
DeactivateSeatbelt()
end)
+49
View File
@@ -0,0 +1,49 @@
-- Do not edit config.default.lua
-- Copy config.default.lua and rename the copy to config.lua
--- Configuration variables
--- @type {}
Config = {}
--- The method that should be used to check whether a player is an on-duty LEO.
--- For a player to receive an LEO notification, they must
--- - `1` = be inside a police vehicle; or
--- - `2` = have the `seatbelt.notify` ACL.
--- @type 1 | 2
Config.LeoCheck = 2
--- Changes how players are identified to LEOs. Cannot be nilish
--- - `1` = Player ID (i.e. "Player 7")
--- - `2` = Seat position (i.e. "Driver", "Passenger", "Rear left passenger", "Far rear left passenger")
--- - `3` = Username (i.e. "Hagen Hyena", "1D-32 Backer P.")
--- @type 1 | 2 | 3
Config.PlayerIdentifierType = 1
--- Distance which LEOs can detect seatbelt-less occupants within.
--- @type number -- A postive number
Config.Distance = 20
--- The default seatbelt keybind.
--- After a player joins, their keybind will not change if
--- the default keybind changes.
--- Players can manually change this keybind in their settings.
--- @type string
--- @see {@link https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard}
Config.DefaultKeybind = 'K'
--- Custom vehicles/seats that don't have seatbelts.
--- Bicycles, motorbikes and submersibles are excluded automatically
--- - `1` = Seatbelt always off, windscreen ejection still occurs
--- - `2` = Seatbelt always on, ejection is never calculated
--- @type table<hash, (1 | 2) | table<number, 1 | 2>>
Config.Excluded = {
[GetHashKey('MINITANK')] = 2, -- Seatbelt always on.
[GetHashKey('HALFTRACK')] = { [3] = 1 }, -- Seat #3 has no seatbelt.
[GetHashKey('KHANJALI')] = 2,
[GetHashKey('APC')] = 2,
[GetHashKey('THRUSTER')] = 2,
[GetHashKey('RHINO')] = 2,
}
--- A model hash key
--- @alias hash number
+18
View File
@@ -0,0 +1,18 @@
fx_version 'cerulean'
game 'gta5'
repository 'https://github.com/TFNRP/seatbelt'
fork_repository 'https://github.com/TehRamsus/Seatbelt'
version '1.2.1'
author 'Reece Stokes <hagen@hyena.gay>'
client_script {
'@framework/util.lua',
'config.default.lua',
'config.lua',
'util.lua',
'client.lua',
}
server_script 'server.lua'
ui_page 'static/index.html'
file 'static/**'
+30
View File
@@ -0,0 +1,30 @@
{
"name": "seatbelt",
"version": "1.2.1",
"license": "MIT",
"author": "Reece Stokes <hagen@hyena.gay>",
"private": true,
"scripts": {
"test": "eslint static && stylelint static",
"lint": "eslint static && stylelint static",
"lint:fix": "eslint static --fix && stylelint static --fix"
},
"repository": {
"type": "git",
"url": "https://github.com/TFNRP/seatbelt.git"
},
"bugs": {
"url": "https://github.com/TFNRP/seatbelt/issues"
},
"homepage": "https://github.com/TFNRP/seatbelt#readme",
"devDependencies": {
"@types/howler": "^2.2.7",
"@types/jquery": "^3.5.16",
"eslint": "^8.46.0",
"eslint-config-prettier": "^8.9.0",
"eslint-plugin-prettier": "^5.0.0",
"prettier": "^3.0.0",
"stylelint": "^14.16.1",
"stylelint-config-standard": "^24.0.0"
}
}
+7
View File
@@ -0,0 +1,7 @@
RegisterNetEvent('seatbelt:ServerNotify', function()
TriggerClientEvent('seatbelt:ClientNotify', -1, source)
end)
RegisterNetEvent('seatbelt:ServerHasAce', function()
TriggerClientEvent('seatbelt:ClientHasAce', source, IsPlayerAceAllowed(source, 'seatbelt.notify'))
end)
Binary file not shown.
Binary file not shown.
Binary file not shown.
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://nui-game-internal/ui/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.3/howler.min.js"
integrity="sha512-6+YN/9o9BWrk6wSfGxQGpt3EUK6XeHi6yeHV+TYD2GR0Sj/cggRpXr1BrAQf0as6XslxomMUxXp2vIl+fv0QRA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="style.css">
<script src="./index.js"></script>
</head>
<body>
<div id="ui">
<svg id="seatbelt" xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24">
<path d="M12,2C13.11,2 14,2.9 14,4C14,5.11 13.11,6 12,6A2,2 0 0,1 10,4A2,2 0 0,1 12,2M12.39,14.79C14.03,14.79 15.46,14.89 16.64,15.04C16.7,12.32 16.46,9.92 16,9C15.87,8.73 15.69,8.5 15.5,8.3L7.43,15.22C8.79,15 10.5,14.79 12.39,14.79M7.46,17C7.59,18.74 7.85,20.5 8.27,22H10.34C10.05,21.12 9.84,20.09 9.68,19C9.68,19 12,18.56 14.32,19C14.16,20.09 13.95,21.12 13.66,22H15.73C16.17,20.45 16.43,18.61 16.56,16.79C15.41,16.65 14,16.54 12.39,16.54C10.46,16.54 8.78,16.75 7.46,17M12,7C12,7 9,7 8,9C7.66,9.68 7.44,11.15 7.37,12.96L13.92,7.34C12.93,7 12,7 12,7M18.57,5.67L17.43,4.34L13.92,7.35C14.47,7.54 15.05,7.84 15.5,8.3L18.57,5.67M20.67,15.83C20.58,15.8 19.14,15.33 16.64,15.04C16.63,15.61 16.6,16.2 16.56,16.79C18.81,17.07 20.1,17.5 20.12,17.5L20.67,15.83M7.37,12.96L3.43,16.34L4.32,17.82C4.34,17.81 5.5,17.36 7.46,17C7.35,15.59 7.32,14.2 7.37,12.96Z"/>
</svg>
</div>
</body>
</html>
+86
View File
@@ -0,0 +1,86 @@
/// <reference types="howler"/>
/// <reference types="jquery"/>
'use strict';
const sounds = {
buckle: new Howl({ src: ['./assets/buckle.ogg'] }),
chime: new Howl({ src: ['./assets/chime.ogg'] }),
unbluckle: new Howl({ src: ['./assets/unbuckle.ogg'] }),
};
const buckle = [sounds.unbluckle, sounds.buckle];
$(() => {
const top = $('#seatbelt').css('top');
const enableIcon = () => {
const ui = $('#ui');
ui.stop(true, true);
ui.css('display', 'flex');
$('#seatbelt').animate(
{
top,
opacity: '1.0',
},
700,
);
}
const disableIcon = () => {
const ui = $('#ui');
ui.stop(true, true);
$('#seatbelt').animate(
{
top: '100vw',
opacity: '0.0',
},
700,
() => ui.css('display', 'none'),
);
}
sounds.chime.on('play', enableIcon);
sounds.chime.on('stop', disableIcon);
sounds.chime.on('end', () => {
if (!sounds.chime.loop()) disableIcon();
});
window.addEventListener('message', event => {
const payload = event.data;
switch (payload.t) {
case 0: {
const data = payload.d;
let playing;
// If another sound is already playing, play the new sound from the reverse of the elapsed time
if ((playing = buckle.find(sound => sound.playing()))) {
const seek = buckle[data].duration() - playing.seek() - 0.423764;
playing.stop();
buckle[data].play();
if (seek > 0) buckle[data].seek(seek);
} else {
buckle[data].play();
}
break;
}
case 1: {
switch (payload.d) {
case 1:
sounds.chime.loop(true);
if (!sounds.chime.playing())
sounds.chime.play();
break;
case 3:
sounds.chime.stop();
sounds.unbluckle.stop();
case 0:
sounds.chime.loop(false);
break;
} break;
}
}
});
});
+3
View File
@@ -0,0 +1,3 @@
<svg id="seatbelt" xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24">
<path d="M12,2C13.11,2 14,2.9 14,4C14,5.11 13.11,6 12,6A2,2 0 0,1 10,4A2,2 0 0,1 12,2M12.39,14.79C14.03,14.79 15.46,14.89 16.64,15.04C16.7,12.32 16.46,9.92 16,9C15.87,8.73 15.69,8.5 15.5,8.3L7.43,15.22C8.79,15 10.5,14.79 12.39,14.79M7.46,17C7.59,18.74 7.85,20.5 8.27,22H10.34C10.05,21.12 9.84,20.09 9.68,19C9.68,19 12,18.56 14.32,19C14.16,20.09 13.95,21.12 13.66,22H15.73C16.17,20.45 16.43,18.61 16.56,16.79C15.41,16.65 14,16.54 12.39,16.54C10.46,16.54 8.78,16.75 7.46,17M12,7C12,7 9,7 8,9C7.66,9.68 7.44,11.15 7.37,12.96L13.92,7.34C12.93,7 12,7 12,7M18.57,5.67L17.43,4.34L13.92,7.35C14.47,7.54 15.05,7.84 15.5,8.3L18.57,5.67M20.67,15.83C20.58,15.8 19.14,15.33 16.64,15.04C16.63,15.61 16.6,16.2 16.56,16.79C18.81,17.07 20.1,17.5 20.12,17.5L20.67,15.83M7.37,12.96L3.43,16.34L4.32,17.82C4.34,17.81 5.5,17.36 7.46,17C7.35,15.59 7.32,14.2 7.37,12.96Z"/>
</svg>

After

Width:  |  Height:  |  Size: 944 B

+23
View File
@@ -0,0 +1,23 @@
body {
user-select: none;
overflow: hidden;
}
#ui {
display: none;
}
@keyframes blink {
0% { opacity: 1; }
50% { opacity: 0; }
}
svg {
position: absolute;
animation: blink normal 1s infinite ease-in-out;
fill: red;
top: 42.1vw;
left: 15.4vw;
width: 3vw;
height: 3vw;
}
+141
View File
@@ -0,0 +1,141 @@
if RegisterFrameworkCommand == nil then
-- polyfill
RegisterFrameworkCommand = function (name, handler, restricted)
if type(name) == 'table' then
for _, c in ipairs(name) do
RegisterFrameworkCommand(c, handler, restricted)
end
else
RegisterCommand(name, handler, restricted)
end
end
end
if GetConvar('tfnrp_framework_init', 'false') == 'true' then
IsHudHidden = exports.framework.IsHudHidden
function IsLEO()
return exports.framework:GetLocalClientDuty() > 0
end
else
function IsHudHidden()
return false
end
if (Config.LeoCheck == 2) then
local has_ace = false
local update = true
function IsLEO()
if (update) then
TriggerServerEvent('seatbelt:ServerHasAce')
update = false
Citizen.SetTimeout(12e3, function ()
update = true
end)
while update == false do
Citizen.Wait(1)
end
update = false
end
return has_ace
end
RegisterNetEvent('seatbelt:ClientHasAce', function(bool)
has_ace = bool
update = true
end)
else
if (Config.LeoCheck ~= 1) then
Citizen.Trace('Config.LeoCheck was not set correctly, using police vehicle check instead')
end
function IsLEO()
return IsPedInAnyPoliceVehicle(PlayerPedId())
end
end
Config.LeoCheck = nil
end
-- config setup
Constants = {
Distance = Config.Distance + 0.0,
Excluded = Config.Excluded,
DefaultKeybind = Config.DefaultKeybind,
}
local GetPlayerIdentifierMethods = {
function(serverId)
return '<C>Player ' .. serverId .. '</C>'
end,
function(_, playerPed, vehicle)
local hash = GetEntityModel(vehicle)
local seats = GetVehicleModelNumberOfSeats(hash)
local names = {
'Driver',
'Passenger',
'Rear left passenger',
'Rear right passenger',
'Far rear left passenger',
'Far rear right passenger',
}
for seat = 1, math.min(#names, seats) do
if GetPedInVehicleSeat(vehicle, seat - 2) == playerPed then
return names[seat]
end
end
end,
function(serverId)
return '<C>' .. GetPlayerName(serverId) .. '</C>'
end,
}
--- GetPlayerIdentifier is a reserved namespace
GetPlayerIdentifier_ = GetPlayerIdentifierMethods[Config.PlayerIdentifierType]
GetPlayerIdentifierMethods = nil
Config = nil
function DoesPedVehicleHaveSeatbelt(ped)
if not IsPedInAnyVehicle(ped)
or IsPedOnAnyBike(ped)
or IsPedInAnyBoat(ped)
or IsPedInAnyPlane(ped)
or IsPedInAnyHeli(ped)
then return false, false end
local vehicle = GetVehiclePedIsIn(ped)
local model = GetEntityModel(vehicle)
if Constants.Excluded[model] then
if Constants.Excluded[model] == 2 then
return true, true
elseif type(Constants.Excluded[model]) == 'table' then
for seat, type in pairs(Constants.Excluded[model]) do
if GetPedInVehicleSeat(vehicle, seat - 2) == ped then
return false, type == 2
end
end
end
end
return true, false
end
function Fwv(entity)
local hr = GetEntityHeading(entity) + 90.0
if hr < 0.0 then hr = 360.0 + hr end
hr = hr * 0.0174533
return { x = math.cos(hr) * 2.0, y = math.sin(hr) * 2.0 }
end
function ShowNotification(string)
BeginTextCommandThefeedPost('STRING')
AddTextComponentSubstringPlayerName(string)
EndTextCommandThefeedPostTicker(true, true)
end
function ShowHelpText(text, beep)
SetTextComponentFormat('STRING')
AddTextComponentSubstringPlayerName(text)
EndTextCommandDisplayHelp(0, 0, beep, -1)
end
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -229,7 +229,7 @@ ensure scully_lawenforcement
ensure VehicleTrustSystem-master
ensure LSRPC-K9
ensure 5500wrecker
ensure seatbelt-1.2.1
ensure seatbelt
***EUPCLOTHING***
ensure eup-stream