Part 17
This commit is contained in:
parent
d4cf6d9da4
commit
5d95c0182e
21
resources/[ps]/fivem-freecam/LICENSE.md
Normal file
21
resources/[ps]/fivem-freecam/LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Deltanic
|
||||
|
||||
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.
|
72
resources/[ps]/fivem-freecam/README.md
Normal file
72
resources/[ps]/fivem-freecam/README.md
Normal file
@ -0,0 +1,72 @@
|
||||
FiveM Freecam
|
||||
=============
|
||||
|
||||
Simple freecam API for FiveM.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Easy to use freecam API
|
||||
- Improved state accuracy over native GTA
|
||||
- Moves with the minimap
|
||||
- Adjustable moving speed
|
||||
- Support for keyboard and gamepads
|
||||
- Fully configurable
|
||||
|
||||
Controls
|
||||
--------
|
||||
|
||||
These are the default controls for the freecam. Keep in mind controls may be
|
||||
different depending on your game settings or keyboard layout.
|
||||
|
||||
> Controls can be customized by [configuring the freecam](docs/CONFIGURING.md#control-mapping).
|
||||
|
||||
### Keyboard
|
||||
|
||||
- Mouse to look around
|
||||
- W and S to move forward and backward
|
||||
- A and D to move left and right
|
||||
- Q and E to move up and down
|
||||
- Alt to slow down
|
||||
- Shift to speed up
|
||||
|
||||
### Gamepad
|
||||
|
||||
- Left joystick to move around
|
||||
- Right joystick to look around
|
||||
- Left button to move down
|
||||
- Right button to move up
|
||||
- Left trigger to slow down
|
||||
- Right trigger to speed up
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
In your `fxmanifest.lua`:
|
||||
```lua
|
||||
dependency 'fivem-freecam'
|
||||
client_script 'script.lua'
|
||||
```
|
||||
|
||||
In your `script.lua`:
|
||||
```lua
|
||||
local Freecam = exports['fivem-freecam']
|
||||
|
||||
-- Toggles the freecam by pressing F5
|
||||
Citizen.CreateThread(function ()
|
||||
while true do
|
||||
Citizen.Wait(0)
|
||||
if IsDisabledControlJustPressed(0, 166) then
|
||||
local isActive = Freecam:IsActive()
|
||||
Freecam:SetActive(not isActive)
|
||||
end
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
- [Configuring](docs/CONFIGURING.md)
|
||||
- [Functions](docs/EXPORTS.md)
|
||||
- [Events](docs/EVENTS.md)
|
139
resources/[ps]/fivem-freecam/client/camera.lua
Normal file
139
resources/[ps]/fivem-freecam/client/camera.lua
Normal file
@ -0,0 +1,139 @@
|
||||
local _internal_camera = nil
|
||||
local _internal_isFrozen = false
|
||||
|
||||
local _internal_pos = nil
|
||||
local _internal_rot = nil
|
||||
local _internal_fov = nil
|
||||
local _internal_vecX = nil
|
||||
local _internal_vecY = nil
|
||||
local _internal_vecZ = nil
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function GetInitialCameraPosition()
|
||||
if _G.CAMERA_SETTINGS.KEEP_POSITION and _internal_pos then
|
||||
return _internal_pos
|
||||
end
|
||||
|
||||
return GetGameplayCamCoord()
|
||||
end
|
||||
|
||||
function GetInitialCameraRotation()
|
||||
if _G.CAMERA_SETTINGS.KEEP_ROTATION and _internal_rot then
|
||||
return _internal_rot
|
||||
end
|
||||
|
||||
local rot = GetGameplayCamRot()
|
||||
return vector3(rot.x, 0.0, rot.z)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function IsFreecamFrozen()
|
||||
return _internal_isFrozen
|
||||
end
|
||||
|
||||
function SetFreecamFrozen(frozen)
|
||||
local frozen = frozen == true
|
||||
_internal_isFrozen = frozen
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function GetFreecamPosition()
|
||||
return _internal_pos
|
||||
end
|
||||
|
||||
function SetFreecamPosition(x, y, z)
|
||||
local pos = vector3(x, y, z)
|
||||
local int = GetInteriorAtCoords(pos)
|
||||
|
||||
LoadInterior(int)
|
||||
SetFocusArea(pos)
|
||||
LockMinimapPosition(x, y)
|
||||
SetCamCoord(_internal_camera, pos)
|
||||
|
||||
_internal_pos = pos
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function GetFreecamRotation()
|
||||
return _internal_rot
|
||||
end
|
||||
|
||||
function SetFreecamRotation(x, y, z)
|
||||
local rotX, rotY, rotZ = ClampCameraRotation(x, y, z)
|
||||
local vecX, vecY, vecZ = EulerToMatrix(rotX, rotY, rotZ)
|
||||
local rot = vector3(rotX, rotY, rotZ)
|
||||
|
||||
LockMinimapAngle(math.floor(rotZ))
|
||||
SetCamRot(_internal_camera, rot)
|
||||
|
||||
_internal_rot = rot
|
||||
_internal_vecX = vecX
|
||||
_internal_vecY = vecY
|
||||
_internal_vecZ = vecZ
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function GetFreecamFov()
|
||||
return _internal_fov
|
||||
end
|
||||
|
||||
function SetFreecamFov(fov)
|
||||
local fov = Clamp(fov, 0.0, 90.0)
|
||||
SetCamFov(_internal_camera, fov)
|
||||
_internal_fov = fov
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function GetFreecamMatrix()
|
||||
return _internal_vecX,
|
||||
_internal_vecY,
|
||||
_internal_vecZ,
|
||||
_internal_pos
|
||||
end
|
||||
|
||||
function GetFreecamTarget(distance)
|
||||
local target = _internal_pos + (_internal_vecY * distance)
|
||||
return target
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function IsFreecamActive()
|
||||
return IsCamActive(_internal_camera) == 1
|
||||
end
|
||||
|
||||
function SetFreecamActive(active)
|
||||
if active == IsFreecamActive() then
|
||||
return
|
||||
end
|
||||
|
||||
local enableEasing = _G.CAMERA_SETTINGS.ENABLE_EASING
|
||||
local easingDuration = _G.CAMERA_SETTINGS.EASING_DURATION
|
||||
|
||||
if active then
|
||||
local pos = GetInitialCameraPosition()
|
||||
local rot = GetInitialCameraRotation()
|
||||
|
||||
_internal_camera = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
|
||||
|
||||
SetFreecamFov(_G.CAMERA_SETTINGS.FOV)
|
||||
SetFreecamPosition(pos.x, pos.y, pos.z)
|
||||
SetFreecamRotation(rot.x, rot.y, rot.z)
|
||||
TriggerEvent('freecam:onEnter')
|
||||
else
|
||||
DestroyCam(_internal_camera)
|
||||
ClearFocus()
|
||||
UnlockMinimapPosition()
|
||||
UnlockMinimapAngle()
|
||||
TriggerEvent('freecam:onExit')
|
||||
end
|
||||
|
||||
SetPlayerControl(PlayerId(), not active)
|
||||
RenderScriptCams(active, enableEasing, easingDuration)
|
||||
end
|
95
resources/[ps]/fivem-freecam/client/config.lua
Normal file
95
resources/[ps]/fivem-freecam/client/config.lua
Normal file
@ -0,0 +1,95 @@
|
||||
local INPUT_LOOK_LR = 1
|
||||
local INPUT_LOOK_UD = 2
|
||||
local INPUT_CHARACTER_WHEEL = 19
|
||||
local INPUT_SPRINT = 21
|
||||
local INPUT_MOVE_UD = 31
|
||||
local INPUT_MOVE_LR = 30
|
||||
local INPUT_VEH_ACCELERATE = 71
|
||||
local INPUT_VEH_BRAKE = 72
|
||||
local INPUT_PARACHUTE_BRAKE_LEFT = 152
|
||||
local INPUT_PARACHUTE_BRAKE_RIGHT = 153
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local BASE_CONTROL_MAPPING = protect({
|
||||
-- Rotation
|
||||
LOOK_X = INPUT_LOOK_LR,
|
||||
LOOK_Y = INPUT_LOOK_UD,
|
||||
|
||||
-- Position
|
||||
MOVE_X = INPUT_MOVE_LR,
|
||||
MOVE_Y = INPUT_MOVE_UD,
|
||||
MOVE_Z = { INPUT_PARACHUTE_BRAKE_LEFT, INPUT_PARACHUTE_BRAKE_RIGHT },
|
||||
|
||||
-- Multiplier
|
||||
MOVE_FAST = INPUT_SPRINT,
|
||||
MOVE_SLOW = INPUT_CHARACTER_WHEEL
|
||||
})
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local BASE_CONTROL_SETTINGS = protect({
|
||||
-- Rotation
|
||||
LOOK_SENSITIVITY_X = 5,
|
||||
LOOK_SENSITIVITY_Y = 5,
|
||||
|
||||
-- Position
|
||||
BASE_MOVE_MULTIPLIER = 1,
|
||||
FAST_MOVE_MULTIPLIER = 10,
|
||||
SLOW_MOVE_MULTIPLIER = 10,
|
||||
})
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local BASE_CAMERA_SETTINGS = protect({
|
||||
--Camera
|
||||
FOV = 45.0,
|
||||
|
||||
-- On enable/disable
|
||||
ENABLE_EASING = true,
|
||||
EASING_DURATION = 1000,
|
||||
|
||||
-- Keep position/rotation
|
||||
KEEP_POSITION = false,
|
||||
KEEP_ROTATION = false
|
||||
})
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
_G.KEYBOARD_CONTROL_MAPPING = table.copy(BASE_CONTROL_MAPPING)
|
||||
_G.GAMEPAD_CONTROL_MAPPING = table.copy(BASE_CONTROL_MAPPING)
|
||||
|
||||
-- Swap up/down movement (LB for down, RB for up)
|
||||
_G.GAMEPAD_CONTROL_MAPPING.MOVE_Z[1] = INPUT_PARACHUTE_BRAKE_LEFT
|
||||
_G.GAMEPAD_CONTROL_MAPPING.MOVE_Z[2] = INPUT_PARACHUTE_BRAKE_RIGHT
|
||||
|
||||
-- Use LT and RT for speed
|
||||
_G.GAMEPAD_CONTROL_MAPPING.MOVE_FAST = INPUT_VEH_ACCELERATE
|
||||
_G.GAMEPAD_CONTROL_MAPPING.MOVE_SLOW = INPUT_VEH_BRAKE
|
||||
|
||||
protect(_G.KEYBOARD_CONTROL_MAPPING)
|
||||
protect(_G.GAMEPAD_CONTROL_MAPPING)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
_G.KEYBOARD_CONTROL_SETTINGS = table.copy(BASE_CONTROL_SETTINGS)
|
||||
_G.GAMEPAD_CONTROL_SETTINGS = table.copy(BASE_CONTROL_SETTINGS)
|
||||
|
||||
-- Gamepad sensitivity can be reduced by BASE.
|
||||
_G.GAMEPAD_CONTROL_SETTINGS.LOOK_SENSITIVITY_X = 2
|
||||
_G.GAMEPAD_CONTROL_SETTINGS.LOOK_SENSITIVITY_Y = 2
|
||||
|
||||
protect(_G.KEYBOARD_CONTROL_SETTINGS)
|
||||
protect(_G.GAMEPAD_CONTROL_SETTINGS)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
_G.CAMERA_SETTINGS = table.copy(BASE_CAMERA_SETTINGS)
|
||||
protect(_G.CAMERA_SETTINGS)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Create some convenient variables.
|
||||
-- Allows us to access controls and config without a gamepad switch.
|
||||
_G.CONTROL_MAPPING = CreateGamepadMetatable(_G.KEYBOARD_CONTROL_MAPPING, _G.GAMEPAD_CONTROL_MAPPING)
|
||||
_G.CONTROL_SETTINGS = CreateGamepadMetatable(_G.KEYBOARD_CONTROL_SETTINGS, _G.GAMEPAD_CONTROL_SETTINGS)
|
29
resources/[ps]/fivem-freecam/client/exports.lua
Normal file
29
resources/[ps]/fivem-freecam/client/exports.lua
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
exports('IsActive', IsFreecamActive)
|
||||
exports('SetActive', SetFreecamActive)
|
||||
exports('IsFrozen', IsFreecamFrozen)
|
||||
exports('SetFrozen', SetFreecamFrozen)
|
||||
exports('GetFov', GetFreecamFov)
|
||||
exports('SetFov', SetFreecamFov)
|
||||
exports('GetPosition', GetFreecamPosition)
|
||||
exports('SetPosition', SetFreecamPosition)
|
||||
exports('GetRotation', GetFreecamRotation)
|
||||
exports('SetRotation', SetFreecamRotation)
|
||||
exports('GetMatrix', GetFreecamMatrix)
|
||||
exports('GetTarget', GetFreecamTarget)
|
||||
|
||||
exports('GetPitch', function () return GetFreecamRotation().x end)
|
||||
exports('GetRoll', function () return GetFreecamRotation().y end)
|
||||
exports('GetYaw', function () return GetFreecamRotation().z end)
|
||||
|
||||
exports('GetKeyboardControl', function (key) return _G.KEYBOARD_CONTROL_MAPPING[key] end)
|
||||
exports('GetGamepadControl', function (key) return _G.GAMEPAD_CONTROL_MAPPING[key] end)
|
||||
exports('GetKeyboardSetting', function (key) return _G.KEYBOARD_CONTROL_SETTINGS[key] end)
|
||||
exports('GetGamepadSetting', function (key) return _G.GAMEPAD_CONTROL_SETTINGS[key] end)
|
||||
exports('GetCameraSetting', function (key) return _G.CAMERA_SETTINGS[key] end)
|
||||
|
||||
exports('SetKeyboardControl', function (key, value) _G.KEYBOARD_CONTROL_MAPPING[key] = value end)
|
||||
exports('SetGamepadControl', function (key, value) _G.GAMEPAD_CONTROL_MAPPING[key] = value end)
|
||||
exports('SetKeyboardSetting', function (key, value) _G.KEYBOARD_CONTROL_SETTINGS[key] = value end)
|
||||
exports('SetGamepadSetting', function (key, value) _G.GAMEPAD_CONTROL_SETTINGS[key] = value end)
|
||||
exports('SetCameraSetting', function (key, value) _G.CAMERA_SETTINGS[key] = value end)
|
83
resources/[ps]/fivem-freecam/client/main.lua
Normal file
83
resources/[ps]/fivem-freecam/client/main.lua
Normal file
@ -0,0 +1,83 @@
|
||||
local SETTINGS = _G.CONTROL_SETTINGS
|
||||
local CONTROLS = _G.CONTROL_MAPPING
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function GetSpeedMultiplier()
|
||||
local fastNormal = GetSmartControlNormal(CONTROLS.MOVE_FAST)
|
||||
local slowNormal = GetSmartControlNormal(CONTROLS.MOVE_SLOW)
|
||||
|
||||
local baseSpeed = SETTINGS.BASE_MOVE_MULTIPLIER
|
||||
local fastSpeed = 1 + ((SETTINGS.FAST_MOVE_MULTIPLIER - 1) * fastNormal)
|
||||
local slowSpeed = 1 + ((SETTINGS.SLOW_MOVE_MULTIPLIER - 1) * slowNormal)
|
||||
|
||||
local frameMultiplier = GetFrameTime() * 60
|
||||
local speedMultiplier = baseSpeed * fastSpeed / slowSpeed
|
||||
|
||||
return speedMultiplier * frameMultiplier
|
||||
end
|
||||
|
||||
local function UpdateCamera()
|
||||
if not IsFreecamActive() or IsPauseMenuActive() then
|
||||
return
|
||||
end
|
||||
|
||||
if not IsFreecamFrozen() then
|
||||
local vecX, vecY = GetFreecamMatrix()
|
||||
local vecZ = vector3(0, 0, 1)
|
||||
|
||||
local pos = GetFreecamPosition()
|
||||
local rot = GetFreecamRotation()
|
||||
|
||||
-- Get speed multiplier for movement
|
||||
local speedMultiplier = GetSpeedMultiplier()
|
||||
|
||||
-- Get rotation input
|
||||
local lookX = GetSmartControlNormal(CONTROLS.LOOK_X)
|
||||
local lookY = GetSmartControlNormal(CONTROLS.LOOK_Y)
|
||||
|
||||
-- Get position input
|
||||
local moveX = GetSmartControlNormal(CONTROLS.MOVE_X)
|
||||
local moveY = GetSmartControlNormal(CONTROLS.MOVE_Y)
|
||||
local moveZ = GetSmartControlNormal(CONTROLS.MOVE_Z)
|
||||
|
||||
-- Calculate new rotation.
|
||||
local rotX = rot.x + (-lookY * SETTINGS.LOOK_SENSITIVITY_X)
|
||||
local rotZ = rot.z + (-lookX * SETTINGS.LOOK_SENSITIVITY_Y)
|
||||
local rotY = rot.y
|
||||
|
||||
-- Adjust position relative to camera rotation.
|
||||
pos = pos + (vecX * moveX * speedMultiplier)
|
||||
pos = pos + (vecY * -moveY * speedMultiplier)
|
||||
pos = pos + (vecZ * moveZ * speedMultiplier)
|
||||
|
||||
-- Adjust new rotation
|
||||
rot = vector3(rotX, rotY, rotZ)
|
||||
|
||||
-- Update camera
|
||||
SetFreecamPosition(pos.x, pos.y, pos.z)
|
||||
SetFreecamRotation(rot.x, rot.y, rot.z)
|
||||
end
|
||||
|
||||
-- Trigger a tick event. Resources depending on the freecam position can
|
||||
-- make use of this event.
|
||||
TriggerEvent('freecam:onTick')
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Citizen.CreateThread(function ()
|
||||
while true do
|
||||
Citizen.Wait(0)
|
||||
UpdateCamera()
|
||||
end
|
||||
end)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- When the resource is stopped, make sure to return the camera to the player.
|
||||
AddEventHandler('onResourceStop', function (resourceName)
|
||||
if resourceName == GetCurrentResourceName() then
|
||||
SetFreecamActive(false)
|
||||
end
|
||||
end)
|
91
resources/[ps]/fivem-freecam/client/utils.lua
Normal file
91
resources/[ps]/fivem-freecam/client/utils.lua
Normal file
@ -0,0 +1,91 @@
|
||||
function table.copy(x)
|
||||
local copy = {}
|
||||
for k, v in pairs(x) do
|
||||
if type(v) == 'table' then
|
||||
copy[k] = table.copy(v)
|
||||
else
|
||||
copy[k] = v
|
||||
end
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
function protect(t)
|
||||
local fn = function (_, k)
|
||||
error('Key `' .. tostring(k) .. '` is not supported.')
|
||||
end
|
||||
|
||||
return setmetatable(t, {
|
||||
__index = fn,
|
||||
__newindex = fn
|
||||
})
|
||||
end
|
||||
|
||||
function CreateGamepadMetatable(keyboard, gamepad)
|
||||
return setmetatable({}, {
|
||||
__index = function (t, k)
|
||||
local src = IsGamepadControl() and gamepad or keyboard
|
||||
return src[k]
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function Clamp(x, min, max)
|
||||
return math.min(math.max(x, min), max)
|
||||
end
|
||||
|
||||
function ClampCameraRotation(rotX, rotY, rotZ)
|
||||
local x = Clamp(rotX, -90.0, 90.0)
|
||||
local y = rotY % 360
|
||||
local z = rotZ % 360
|
||||
return x, y, z
|
||||
end
|
||||
|
||||
function IsGamepadControl()
|
||||
return not IsInputDisabled(2)
|
||||
end
|
||||
|
||||
function GetSmartControlNormal(control)
|
||||
if type(control) == 'table' then
|
||||
local normal1 = GetDisabledControlNormal(0, control[1])
|
||||
local normal2 = GetDisabledControlNormal(0, control[2])
|
||||
return normal1 - normal2
|
||||
end
|
||||
|
||||
return GetDisabledControlNormal(0, control)
|
||||
end
|
||||
|
||||
function EulerToMatrix(rotX, rotY, rotZ)
|
||||
local radX = math.rad(rotX)
|
||||
local radY = math.rad(rotY)
|
||||
local radZ = math.rad(rotZ)
|
||||
|
||||
local sinX = math.sin(radX)
|
||||
local sinY = math.sin(radY)
|
||||
local sinZ = math.sin(radZ)
|
||||
local cosX = math.cos(radX)
|
||||
local cosY = math.cos(radY)
|
||||
local cosZ = math.cos(radZ)
|
||||
|
||||
local vecX = {}
|
||||
local vecY = {}
|
||||
local vecZ = {}
|
||||
|
||||
vecX.x = cosY * cosZ
|
||||
vecX.y = cosY * sinZ
|
||||
vecX.z = -sinY
|
||||
|
||||
vecY.x = cosZ * sinX * sinY - cosX * sinZ
|
||||
vecY.y = cosX * cosZ - sinX * sinY * sinZ
|
||||
vecY.z = cosY * sinX
|
||||
|
||||
vecZ.x = -cosX * cosZ * sinY + sinX * sinZ
|
||||
vecZ.y = -cosZ * sinX + cosX * sinY * sinZ
|
||||
vecZ.z = cosX * cosY
|
||||
|
||||
vecX = vector3(vecX.x, vecX.y, vecX.z)
|
||||
vecY = vector3(vecY.x, vecY.y, vecY.z)
|
||||
vecZ = vector3(vecZ.x, vecZ.y, vecZ.z)
|
||||
|
||||
return vecX, vecY, vecZ
|
||||
end
|
130
resources/[ps]/fivem-freecam/docs/CONFIGURING.md
Normal file
130
resources/[ps]/fivem-freecam/docs/CONFIGURING.md
Normal file
@ -0,0 +1,130 @@
|
||||
Configuring
|
||||
===========
|
||||
|
||||
Camera settings
|
||||
---------------
|
||||
|
||||
The freecam accepts a few configuration values. These can be changed
|
||||
programmatically like so:
|
||||
|
||||
```lua
|
||||
local Freecam = exports['fivem-freecam']
|
||||
Freecam:SetCameraSetting('EASING_DURATION', 2500)
|
||||
```
|
||||
|
||||
Full list of camera settings and their default values:
|
||||
|
||||
```lua
|
||||
--Camera
|
||||
FOV = 45.0
|
||||
|
||||
-- On enable/disable
|
||||
ENABLE_EASING = true
|
||||
EASING_DURATION = 1000
|
||||
|
||||
-- Keep position/rotation
|
||||
KEEP_POSITION = false
|
||||
KEEP_ROTATION = false
|
||||
```
|
||||
|
||||
> **Note:** FOV is applied upon entering the freecam. To change the FOV when
|
||||
> the camera is already active, use [SetFov](EXPORTS.md#setfov).
|
||||
|
||||
- [SetCameraSetting](EXPORTS.md#setcamerasetting)
|
||||
- [SetFov](EXPORTS.md#setfov)
|
||||
|
||||
Control mapping
|
||||
---------------
|
||||
|
||||
It's possible to change the controls of the freecam. Controls are defined for
|
||||
keyboards and gamepads and can be changed individually:
|
||||
|
||||
```lua
|
||||
local Freecam = exports['fivem-freecam']
|
||||
Freecam:SetKeyboardControl('MOVE_X', INPUT_MOVE_LR)
|
||||
Freecam:SetGamepadControl('MOVE_Y', INPUT_MOVE_UD)
|
||||
```
|
||||
|
||||
> Input names are taken from the [FiveM docs][fivem-docs].
|
||||
> Use their corresponding control ID in your own code.
|
||||
|
||||
- [SetKeyboardControl](EXPORTS.md#setkeyboardcontrol)
|
||||
- [SetGamepadControl](EXPORTS.md#setgamepadcontrol)
|
||||
|
||||
|
||||
Adjustable **keyboard** mapping and their default controls:
|
||||
|
||||
```lua
|
||||
-- Rotation
|
||||
LOOK_X = INPUT_LOOK_LR
|
||||
LOOK_Y = INPUT_LOOK_UD
|
||||
|
||||
-- Position
|
||||
MOVE_X = INPUT_MOVE_LR
|
||||
MOVE_Y = INPUT_MOVE_UD
|
||||
MOVE_Z = { INPUT_PARACHUTE_BRAKE_LEFT, INPUT_PARACHUTE_BRAKE_RIGHT }
|
||||
|
||||
-- Multiplier
|
||||
MOVE_FAST = INPUT_SPRINT
|
||||
MOVE_SLOW = INPUT_CHARACTER_WHEEL
|
||||
```
|
||||
|
||||
Adjustable **gamepad** mapping and their default controls:
|
||||
|
||||
```lua
|
||||
-- Rotation
|
||||
LOOK_X = INPUT_LOOK_LR
|
||||
LOOK_Y = INPUT_LOOK_UD
|
||||
|
||||
-- Position
|
||||
MOVE_X = INPUT_MOVE_LR
|
||||
MOVE_Y = INPUT_MOVE_UD
|
||||
MOVE_Z = { INPUT_PARACHUTE_BRAKE_RIGHT, INPUT_PARACHUTE_BRAKE_LEFT }
|
||||
|
||||
-- Multiplier
|
||||
MOVE_FAST = INPUT_VEH_ACCELERATE
|
||||
MOVE_SLOW = INPUT_VEH_BRAKE
|
||||
```
|
||||
|
||||
Control settings
|
||||
----------------
|
||||
|
||||
Control settings such as move speed multipliers and camera move sensitivity can
|
||||
also be changed through settings:
|
||||
|
||||
```lua
|
||||
local Freecam = exports['fivem-freecam']
|
||||
Freecam:SetKeyboardSetting('LOOK_SENSITIVITY_X', 5)
|
||||
Freecam:SetGamepadSetting('LOOK_SENSITIVITY_X', 2)
|
||||
```
|
||||
|
||||
- [SetKeyboardSetting](EXPORTS.md#setkeyboardsetting)
|
||||
- [SetGamepadSetting](EXPORTS.md#setgamepadsetting)
|
||||
|
||||
Adjustable **keyboard** settings and their default values:
|
||||
|
||||
```lua
|
||||
-- Rotation
|
||||
LOOK_SENSITIVITY_X = 5
|
||||
LOOK_SENSITIVITY_Y = 5
|
||||
|
||||
-- Position
|
||||
BASE_MOVE_MULTIPLIER = 1
|
||||
FAST_MOVE_MULTIPLIER = 10
|
||||
SLOW_MOVE_MULTIPLIER = 10
|
||||
```
|
||||
|
||||
Adjustable **gamepad** settings and their default values:
|
||||
|
||||
```lua
|
||||
-- Rotation
|
||||
LOOK_SENSITIVITY_X = 2
|
||||
LOOK_SENSITIVITY_Y = 2
|
||||
|
||||
-- Position
|
||||
BASE_MOVE_MULTIPLIER = 1
|
||||
FAST_MOVE_MULTIPLIER = 10
|
||||
SLOW_MOVE_MULTIPLIER = 10
|
||||
```
|
||||
|
||||
[fivem-docs]: https://docs.fivem.net/game-references/controls/
|
49
resources/[ps]/fivem-freecam/docs/EVENTS.md
Normal file
49
resources/[ps]/fivem-freecam/docs/EVENTS.md
Normal file
@ -0,0 +1,49 @@
|
||||
Events
|
||||
======
|
||||
|
||||
`freecam:onEnter`
|
||||
-----------------
|
||||
|
||||
Called upon entering the freecam after `Freecam:SetActive(true)`. Useful to
|
||||
detect state changes of the freecam.
|
||||
|
||||
```lua
|
||||
AddEventHandler('freecam:onEnter', function ()
|
||||
-- Plays an effect upon entering the freecam.
|
||||
StartScreenEffect('SuccessNeutral', 500, false)
|
||||
PlaySoundFrontend(-1, 'Hit_In', 'PLAYER_SWITCH_CUSTOM_SOUNDSET', 1)
|
||||
end)
|
||||
```
|
||||
|
||||
`freecam:onExit`
|
||||
----------------
|
||||
|
||||
Called upon exiting the freecam after `Freecam:SetActive(false)`. Useful to
|
||||
detect state changes of the freecam.
|
||||
|
||||
```lua
|
||||
AddEventHandler('freecam:onExit', function ()
|
||||
-- Plays an effect upon exiting the freecam.
|
||||
StartScreenEffect('SuccessNeutral', 500, false)
|
||||
PlaySoundFrontend(-1, 'Hit_Out', 'PLAYER_SWITCH_CUSTOM_SOUNDSET', 1)
|
||||
end)
|
||||
```
|
||||
|
||||
`freecam:onTick`
|
||||
----------------
|
||||
|
||||
Called every tick for as long as the freecam is active. Calls after any
|
||||
positional or rotational updates so anything attached to the freecam stays
|
||||
in sync. Not called when the freecam is inactive.
|
||||
|
||||
No values are passed to this event.
|
||||
|
||||
```lua
|
||||
local Freecam = exports['fivem-freecam']
|
||||
AddEventHandler('freecam:onTick', function ()
|
||||
-- Gets the current target position of the freecam.
|
||||
-- You could attach the player to this, or an object.
|
||||
local target = Freecam:GetTarget(50)
|
||||
print(target)
|
||||
end)
|
||||
```
|
255
resources/[ps]/fivem-freecam/docs/EXPORTS.md
Normal file
255
resources/[ps]/fivem-freecam/docs/EXPORTS.md
Normal file
@ -0,0 +1,255 @@
|
||||
Exports
|
||||
=======
|
||||
<!-- C# code blocks are used for convenient syntax highlighting. -->
|
||||
|
||||
Exported freecam functions.
|
||||
|
||||
> Examples assume you are referencing the freecam exports as such:
|
||||
> ```lua
|
||||
> local Freecam = exports['fivem-freecam']
|
||||
> Freecam:SetActive(true)
|
||||
> ```
|
||||
|
||||
### Getters
|
||||
- [IsActive](#IsActive)
|
||||
- [IsFrozen](#IsFrozen)
|
||||
- [GetFov](#GetFov)
|
||||
- [GetPosition](#GetPosition)
|
||||
- [GetRotation](#GetRotation)
|
||||
- [GetMatrix](#GetMatrix)
|
||||
- [GetTarget](#GetTarget)
|
||||
- [GetPitch](#GetPitch)
|
||||
- [GetRoll](#GetRoll)
|
||||
- [GetYaw](#GetYaw)
|
||||
- [GetCameraSetting](#GetCameraSetting)
|
||||
- [GetKeyboardSetting](#GetKeyboardSetting)
|
||||
- [GetGamepadSetting](#GetGamepadSetting)
|
||||
- [GetKeyboardControl](#GetKeyboardControl)
|
||||
|
||||
### Setters
|
||||
- [SetActive](#SetActive)
|
||||
- [SetFrozen](#SetFrozen)
|
||||
- [SetFov](#SetFov)
|
||||
- [SetPosition](#SetPosition)
|
||||
- [SetRotation](#SetRotation)
|
||||
- [SetCameraSetting](#SetCameraSetting)
|
||||
- [SetKeyboardSetting](#SetKeyboardSetting)
|
||||
- [SetGamepadSetting](#SetGamepadSetting)
|
||||
- [SetKeyboardControl](#SetKeyboardControl)
|
||||
- [SetGamepadControl](#SetGamepadControl)
|
||||
|
||||
---
|
||||
|
||||
`IsActive`
|
||||
----------
|
||||
Returns wether the freecam is currently active or not.
|
||||
|
||||
```c#
|
||||
bool isActive = Freecam:IsActive()
|
||||
```
|
||||
|
||||
`SetActive`
|
||||
-----------
|
||||
Enters or exits the freecam.
|
||||
|
||||
```c#
|
||||
void Freecam:SetActive(bool active)
|
||||
```
|
||||
|
||||
`IsFrozen`
|
||||
----------
|
||||
Returns wether the freecam position is currently frozen.
|
||||
|
||||
```c#
|
||||
bool isFrozen = Freecam:IsFrozen()
|
||||
```
|
||||
|
||||
`SetFrozen`
|
||||
-----------
|
||||
Sets the freecam frozen. When frozen, controls do not update the position or
|
||||
rotation anymore but [SetPosition](#setposition)/[SetRotation](#setrotation) will.
|
||||
|
||||
```c#
|
||||
void Freecam:SetFrozen(bool frozen)
|
||||
```
|
||||
|
||||
`GetFov`
|
||||
--------
|
||||
Returns the field of view of the freecam.
|
||||
|
||||
```c#
|
||||
float fov = Freecam:GetFov()
|
||||
```
|
||||
|
||||
`SetFov`
|
||||
--------
|
||||
Sets the current field of view of the freecam. This does NOT update the default
|
||||
FOV for the freecam. Use [SetCameraSetting](#setcamerasetting) for that.
|
||||
|
||||
```c#
|
||||
void Freecam:SetFov(float fov)
|
||||
```
|
||||
|
||||
`GetPosition`
|
||||
-------------
|
||||
Returns the current position of the freecam.
|
||||
|
||||
```c#
|
||||
vector3 position = Freecam:GetPosition()
|
||||
```
|
||||
|
||||
`SetPosition`
|
||||
-------------
|
||||
Sets a new position for the freecam.
|
||||
|
||||
```c#
|
||||
void Freecam:SetPosition(float posX, float posY, float posZ)
|
||||
```
|
||||
|
||||
`GetRotation`
|
||||
-------------
|
||||
Returns the current rotation of the freecam.
|
||||
|
||||
```c#
|
||||
vector3 rotation = Freecam:GetRotation()
|
||||
```
|
||||
|
||||
`SetRotation`
|
||||
-------------
|
||||
Sets a new position for the freecam.
|
||||
|
||||
```c#
|
||||
void Freecam:SetRotation(float rotX, float rotY, float rotZ)
|
||||
```
|
||||
|
||||
`GetMatrix`
|
||||
-----------
|
||||
Returns the current view matrix of the freecam.
|
||||
|
||||
```c#
|
||||
vector3 vecX, vector3 vecY, vector3 vecZ, vector3 pos = Freecam:GetMatrix()
|
||||
```
|
||||
|
||||
`GetTarget`
|
||||
-----------
|
||||
Returns the position the freecam is looking at from the given distance.
|
||||
|
||||
```c#
|
||||
vector3 target = Freecam:GetTarget(float distance)
|
||||
```
|
||||
|
||||
`GetPitch`
|
||||
----------
|
||||
Returns the current pitch (rotX) of the freecam.
|
||||
|
||||
```c#
|
||||
float pitch = Freecam:GetPitch()
|
||||
```
|
||||
|
||||
`GetRoll`
|
||||
---------
|
||||
Returns the current roll (rotY) of the freecam.
|
||||
|
||||
```c#
|
||||
float roll = Freecam:GetRoll()
|
||||
```
|
||||
|
||||
`GetYaw`
|
||||
--------
|
||||
Returns the current yaw (rotZ) of the freecam.
|
||||
|
||||
```c#
|
||||
float yaw = Freecam:GetYaw()
|
||||
```
|
||||
|
||||
`GetCameraSetting`
|
||||
------------------
|
||||
Returns the value of a camera setting.
|
||||
See [CONFIGURING](CONFIGURING.md#camera-settings) for details.
|
||||
|
||||
```c#
|
||||
mixed value = Freecam:GetCameraSetting(string key)
|
||||
```
|
||||
|
||||
`SetCameraSetting`
|
||||
------------------
|
||||
Sets the value of a camera setting.
|
||||
See [CONFIGURING](CONFIGURING.md#camera-settings) for details.
|
||||
|
||||
```c#
|
||||
void Freecam:SetCameraSetting(string key, mixed value)
|
||||
```
|
||||
|
||||
`GetKeyboardSetting`
|
||||
--------------------
|
||||
Returns the value of a keyboard setting.
|
||||
See [CONFIGURING](CONFIGURING.md#control-settings) for details.
|
||||
|
||||
```c#
|
||||
mixed value = Freecam:GetKeyboardSetting(string key)
|
||||
```
|
||||
|
||||
`SetKeyboardSetting`
|
||||
--------------------
|
||||
Sets the value of a keyboard setting.
|
||||
See [CONFIGURING](CONFIGURING.md#control-settings) for details.
|
||||
|
||||
```c#
|
||||
void Freecam:SetKeyboardSetting(string key, mixed value)
|
||||
```
|
||||
|
||||
`GetGamepadSetting`
|
||||
-------------------
|
||||
Returns the value of a gamepad setting.
|
||||
See [CONFIGURING](CONFIGURING.md#control-settings) for details.
|
||||
|
||||
```c#
|
||||
mixed value = Freecam:GetGamepadSetting(string key)
|
||||
```
|
||||
|
||||
`SetGamepadSetting`
|
||||
-------------------
|
||||
Sets the value of a gamepad setting.
|
||||
See [CONFIGURING](CONFIGURING.md#control-settings) for details.
|
||||
|
||||
```c#
|
||||
void Freecam:SetGamepadSetting(string key, mixed value)
|
||||
```
|
||||
|
||||
`GetKeyboardControl`
|
||||
--------------------
|
||||
Returns the value of a keyboard control.
|
||||
See [CONFIGURING](CONFIGURING.md#control-mapping) for details.
|
||||
|
||||
```c#
|
||||
mixed value = Freecam:GetKeyboardControl(string key)
|
||||
```
|
||||
|
||||
`SetKeyboardControl`
|
||||
--------------------
|
||||
Sets the value of a keyboard control.
|
||||
See [CONFIGURING](CONFIGURING.md#control-mapping) for details.
|
||||
|
||||
```c#
|
||||
void Freecam:SetKeyboardControl(string key, int value)
|
||||
void Freecam:SetKeyboardControl(string key, table value)
|
||||
```
|
||||
|
||||
`GetGamepadControl`
|
||||
-------------------
|
||||
Returns the value of a gamepad control.
|
||||
See [CONFIGURING](CONFIGURING.md#control-mapping) for details.
|
||||
|
||||
```c#
|
||||
mixed value = Freecam:GetGamepadControl(string key)
|
||||
```
|
||||
|
||||
`SetGamepadControl`
|
||||
-------------------
|
||||
Sets the value of a gamepad control.
|
||||
See [CONFIGURING](CONFIGURING.md#control-mapping) for details.
|
||||
|
||||
```c#
|
||||
void Freecam:SetGamepadControl(string key, int value)
|
||||
void Freecam:SetGamepadControl(string key, table value)
|
||||
```
|
12
resources/[ps]/fivem-freecam/fxmanifest.lua
Normal file
12
resources/[ps]/fivem-freecam/fxmanifest.lua
Normal file
@ -0,0 +1,12 @@
|
||||
fx_version 'cerulean'
|
||||
games { 'gta5' }
|
||||
|
||||
author 'Deltanic'
|
||||
description 'Simple freecam API for FiveM.'
|
||||
version '1.0.0'
|
||||
|
||||
client_script 'client/utils.lua'
|
||||
client_script 'client/config.lua'
|
||||
client_script 'client/camera.lua'
|
||||
client_script 'client/exports.lua'
|
||||
client_script 'client/main.lua'
|
2
resources/[ps]/ps-adminmenu/.gitattributes
vendored
Normal file
2
resources/[ps]/ps-adminmenu/.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
26
resources/[ps]/ps-adminmenu/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
26
resources/[ps]/ps-adminmenu/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
458
resources/[ps]/ps-adminmenu/LICENSE
Normal file
458
resources/[ps]/ps-adminmenu/LICENSE
Normal file
@ -0,0 +1,458 @@
|
||||
Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)
|
||||
https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
This is a human-readable summary of (and not a substitute for) the license. Disclaimer.
|
||||
|
||||
You are free to:
|
||||
|
||||
Share — copy and redistribute the material in any medium or format
|
||||
Adapt — remix, transform, and build upon the material
|
||||
|
||||
The licensor cannot revoke these freedoms as long as you follow the license terms.
|
||||
|
||||
Under the following terms:
|
||||
Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
||||
|
||||
NonCommercial — You may not use the material for commercial purposes.
|
||||
|
||||
ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
|
||||
|
||||
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
||||
|
||||
Notices:
|
||||
You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
|
||||
No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
||||
Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution-NonCommercial-ShareAlike 4.0 International Public License
|
||||
("Public License"). To the extent this Public License may be
|
||||
interpreted as a contract, You are granted the Licensed Rights in
|
||||
consideration of Your acceptance of these terms and conditions, and the
|
||||
Licensor grants You such rights in consideration of benefits the
|
||||
Licensor receives from making the Licensed Material available under
|
||||
these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. BY-NC-SA Compatible License means a license listed at
|
||||
creativecommons.org/compatiblelicenses, approved by Creative
|
||||
Commons as essentially the equivalent of this Public License.
|
||||
|
||||
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
e. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
g. License Elements means the license attributes listed in the name
|
||||
of a Creative Commons Public License. The License Elements of this
|
||||
Public License are Attribution, NonCommercial, and ShareAlike.
|
||||
|
||||
h. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
i. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
k. NonCommercial means not primarily intended for or directed towards
|
||||
commercial advantage or monetary compensation. For purposes of
|
||||
this Public License, the exchange of the Licensed Material for
|
||||
other material subject to Copyright and Similar Rights by digital
|
||||
file-sharing or similar means is NonCommercial provided there is
|
||||
no payment of monetary compensation in connection with the
|
||||
exchange.
|
||||
|
||||
l. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
m. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
n. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part, for NonCommercial purposes only; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material for
|
||||
NonCommercial purposes only.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. Additional offer from the Licensor -- Adapted Material.
|
||||
Every recipient of Adapted Material from You
|
||||
automatically receives an offer from the Licensor to
|
||||
exercise the Licensed Rights in the Adapted Material
|
||||
under the conditions of the Adapter's License You apply.
|
||||
|
||||
c. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties, including when
|
||||
the Licensed Material is used other than for NonCommercial
|
||||
purposes.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
b. ShareAlike.
|
||||
|
||||
In addition to the conditions in Section 3(a), if You Share
|
||||
Adapted Material You produce, the following conditions also apply.
|
||||
|
||||
1. The Adapter's License You apply must be a Creative Commons
|
||||
license with the same License Elements, this version or
|
||||
later, or a BY-NC-SA Compatible License.
|
||||
|
||||
2. You must include the text of, or the URI or hyperlink to, the
|
||||
Adapter's License You apply. You may satisfy this condition
|
||||
in any reasonable manner based on the medium, means, and
|
||||
context in which You Share Adapted Material.
|
||||
|
||||
3. You may not offer or impose any additional or different terms
|
||||
or conditions on, or apply any Effective Technological
|
||||
Measures to, Adapted Material that restrict exercise of the
|
||||
rights granted under the Adapter's License You apply.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database for NonCommercial purposes
|
||||
only;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material,
|
||||
including for purposes of Section 3(b); and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
139
resources/[ps]/ps-adminmenu/README.md
Normal file
139
resources/[ps]/ps-adminmenu/README.md
Normal file
@ -0,0 +1,139 @@
|
||||
# ps-adminmenu
|
||||
The Admin Menu crafted by [OK1ez](https://github.com/OK1ez) and our dedicated team is user-friendly and intuitive. We invite you to contribute by submitting new features through PRs. We're always eager to review and consider new features. Make sure you use our template when opening Issues or they will be auto closed.
|
||||
|
||||
## Unofficial ESX Version
|
||||
Made by Avilchiis for the community, you can download it [here](https://github.com/avilchiis/ps-adminmenu). **WE DO NOT PROVIDE SUPPORT FOR ESX VERSION, DO NOT ASK YOU'LL BE IGNORED.**
|
||||
|
||||
# Preview
|
||||
<img src="https://github.com/Project-Sloth/ps-adminmenu/assets/82112471/0da6cf4d-fc72-497f-a59c-08011b3785ab" width="300">
|
||||
<img src="https://github.com/Project-Sloth/ps-adminmenu/assets/82112471/2d366445-4094-4a10-a570-265cb230fc37" width="300">
|
||||
<img src="https://github.com/Project-Sloth/ps-adminmenu/assets/82112471/33382d64-3b95-42aa-9659-d92dbdca94d2" width="600">
|
||||
<img src="https://github.com/Project-Sloth/ps-adminmenu/assets/82112471/d63982c6-9b04-4dec-b059-55e1cc5ea608" width="600">
|
||||
<img src="https://github.com/Project-Sloth/ps-adminmenu/assets/82112471/ab44df15-7d9e-4753-9c71-2492348a229d" width="600">
|
||||
<img src="https://github.com/Project-Sloth/ps-adminmenu/assets/82112471/0fb81425-dd45-4354-8fb7-94e62ac954ae" width="600">
|
||||
<img src="https://github.com/Project-Sloth/ps-adminmenu/assets/82112471/6f1d0ea9-ea55-4088-98de-ceb4fb1c3838" width="600">
|
||||
|
||||
# Change Language.
|
||||
- Place this `setr ox:locale en` inside your `server.cfg`
|
||||
- Change the `en` to your desired language!
|
||||
|
||||
**Supported Languages:**
|
||||
| **Alias** | **Language Names** |
|
||||
|--------------|---------------|
|
||||
|en |English |
|
||||
|fr |French |
|
||||
|id |Indonesia |
|
||||
|pt-br |Brazilian Portuguese |
|
||||
|tr |Turkish |
|
||||
|es |Spanish |
|
||||
|nl |Dutch |
|
||||
|no |Norwegian |
|
||||
|
||||
# Features
|
||||
* Admin Car
|
||||
* Ban Player
|
||||
* Bring Player
|
||||
* Change Plate
|
||||
* Checking number plates before ```Change Plate```
|
||||
* Change Time
|
||||
* Change Weather
|
||||
* Check Permissions
|
||||
* Clear Inventory
|
||||
* Clear Inventory Offline
|
||||
* Clothing Menu
|
||||
* Copy Coordinates
|
||||
* Delete Vehicle
|
||||
* Delete Laser
|
||||
* Explode Player
|
||||
* Fix Vehicle
|
||||
* Freeze Player
|
||||
* Give Clothing Menu
|
||||
* Give Item
|
||||
* Give Item to All
|
||||
* Give Money
|
||||
* Give Money to All
|
||||
* Give Vehicle to Player
|
||||
* Give NUI Focus
|
||||
* God Mode
|
||||
* Invisible
|
||||
* Infinite Ammo
|
||||
* Kick Player
|
||||
* Kill Player
|
||||
* Make Player Drunk
|
||||
* Message Player
|
||||
* Mute Player
|
||||
* Max Vehicle Mods
|
||||
* No Clip
|
||||
* Open Inventory
|
||||
* Open Stash
|
||||
* Open Trunk
|
||||
* Play Sound
|
||||
* Refuel Vehicle
|
||||
* Remove Money
|
||||
* Remove Stress
|
||||
* Revive All
|
||||
* Revive Player
|
||||
* Revive Radius
|
||||
* Set Bucket
|
||||
* Server Announcement
|
||||
* Set Ammo
|
||||
* Set Vehicle State in Garage (In & Out)
|
||||
* Set Gang
|
||||
* Set Job
|
||||
* Set on Fire
|
||||
* Set Permissions
|
||||
* Set Player Ped
|
||||
* Sit in Vehicle
|
||||
* Spawn Vehicle
|
||||
* Spectate Player
|
||||
* Teleport Back
|
||||
* Teleport to Coordinates
|
||||
* Teleport to Marker
|
||||
* Teleport to player
|
||||
* Toggle Blackout
|
||||
* Toggle Blips
|
||||
* Toggle Coords
|
||||
* Toggle Cuffs
|
||||
* Toggle Delete Laser
|
||||
* Toggle Duty
|
||||
* Toggle Names
|
||||
* Vehicle Dev Menu
|
||||
* Warn player
|
||||
|
||||
# Depedency
|
||||
1. [qb-core](https://github.com/qbcore-framework/qb-core)
|
||||
2. [ox_lib](https://github.com/overextended/ox_lib)
|
||||
|
||||
# Installation
|
||||
1. Download the latest release.
|
||||
2. Add the files to your server resources.
|
||||
3. Ensure `ps-adminmenu` in your server cfg. Make sure ox_lib starts before ps-adminmenu.
|
||||
4. Set the config in `shared/config.lua` to your needs.
|
||||
|
||||
A community video has been made for setup instructions and showcase, you can find it [here](https://www.youtube.com/watch?v=aez5RIi8db8&ab_channel=Kamaryn)
|
||||
|
||||
## Permissions
|
||||
Make sure you've correctly configured player permissions in your server.cfg by using ACE permissions with the appropriate identifier. Otherwise, you'll be unable to access or launch the admin menu. Here's a sample configuration where the player, MonkeyWhisper, is assigned god, admin, and mod roles, you should not have all 3 permissions for a single person. For a deeper understanding of how QBCore manages permissions, refer to [this documentation.](https://docs.qbcore.org/qbcore-documentation/guides/setting-permissions)
|
||||
|
||||
### Player Permission
|
||||
```
|
||||
add_principal identifier.fivem:565139 qbcore.god # MonkeyWhisper
|
||||
add_principal identifier.fivem:565139 qbcore.admin # MonkeyWhisper
|
||||
add_principal identifier.fivem:565139 qbcore.mod # MonkeyWhisper
|
||||
```
|
||||
|
||||
|
||||
## Setting Up Logs
|
||||
1. Set up a Discord Webhook for the channel you want the logs to be.
|
||||
2. Add this to `qb-smallresource/server/logs.lua` -
|
||||
`['ps-adminmenu'] = 'discord webhook'`
|
||||
3. Replace the place holder with your webhook link.
|
||||
|
||||
# To Do
|
||||
* Rework the blips/names
|
||||
|
||||
# Credits
|
||||
* [OK1ez](https://github.com/OK1ez)
|
||||
* [Lenzh](https://github.com/Lenzh)
|
||||
* [LeSiiN](https://github.com/LeSiiN)
|
||||
* Project Sloth Team
|
28
resources/[ps]/ps-adminmenu/client/chat.lua
Normal file
28
resources/[ps]/ps-adminmenu/client/chat.lua
Normal file
@ -0,0 +1,28 @@
|
||||
local function getMessagesCallBack()
|
||||
return lib.callback.await('ps-adminmenu:callback:GetMessages', false)
|
||||
end
|
||||
|
||||
RegisterNUICallback("GetMessages", function(_, cb)
|
||||
local data = getMessagesCallBack()
|
||||
if next(data) then
|
||||
SendNUIMessage({
|
||||
action = "setMessages",
|
||||
data = data
|
||||
})
|
||||
end
|
||||
cb(1)
|
||||
end)
|
||||
|
||||
RegisterNUICallback("SendMessage", function(msgData, cb)
|
||||
local message = msgData.message
|
||||
print(message, PlayerData.citizenid, PlayerData.charinfo.firstname .. " " .. PlayerData.charinfo.lastname )
|
||||
|
||||
TriggerServerEvent("ps-adminmenu:server:sendMessageServer", message, PlayerData.citizenid, PlayerData.charinfo.firstname .. " " .. PlayerData.charinfo.lastname)
|
||||
|
||||
local data = getMessagesCallBack()
|
||||
SendNUIMessage({
|
||||
action = "setMessages",
|
||||
data = data
|
||||
})
|
||||
cb(1)
|
||||
end)
|
88
resources/[ps]/ps-adminmenu/client/data.lua
Normal file
88
resources/[ps]/ps-adminmenu/client/data.lua
Normal file
@ -0,0 +1,88 @@
|
||||
local PedList = require "data.ped"
|
||||
|
||||
-- Returns a list of vehicles from QBCore.Shared.Vehicles
|
||||
local function GetVehicles()
|
||||
local vehicles = {}
|
||||
|
||||
for _, v in pairs(QBCore.Shared.Vehicles) do
|
||||
vehicles[#vehicles + 1] = { label = v.name, value = v.model }
|
||||
end
|
||||
|
||||
return vehicles
|
||||
end
|
||||
|
||||
-- Returns a list of items from QBCore.Shared.Items
|
||||
local function GetItems()
|
||||
local items = {}
|
||||
local ItemsData = QBCore.Shared.Items
|
||||
|
||||
if Config.Inventory == "ox_inventory" then
|
||||
ItemsData = exports.ox_inventory:Items()
|
||||
end
|
||||
|
||||
for name, v in pairs(ItemsData) do
|
||||
items[#items + 1] = { label = v.label, value = name }
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
-- Returns a list of jobs from QBCore.Shared.Jobs
|
||||
local function GetJobs()
|
||||
local jobs = {}
|
||||
|
||||
for name, v in pairs(QBCore.Shared.Jobs) do
|
||||
local gradeDataList = {}
|
||||
|
||||
for grade, gradeData in pairs(v.grades) do
|
||||
gradeDataList[#gradeDataList + 1] = { name = gradeData.name, grade = grade, isboss = gradeData.isboss }
|
||||
end
|
||||
|
||||
jobs[#jobs + 1] = { label = v.label, value = name, grades = gradeDataList }
|
||||
end
|
||||
|
||||
return jobs
|
||||
end
|
||||
|
||||
-- Returns a list of gangs from QBCore.Shared.Gangs
|
||||
local function GetGangs()
|
||||
local gangs = {}
|
||||
|
||||
for name, v in pairs(QBCore.Shared.Gangs) do
|
||||
local gradeDataList = {}
|
||||
|
||||
for grade, gradeData in pairs(v.grades) do
|
||||
gradeDataList[#gradeDataList + 1] = { name = gradeData.name, grade = grade, isboss = gradeData.isboss }
|
||||
end
|
||||
|
||||
gangs[#gangs + 1] = { label = v.label, value = name, grades = gradeDataList }
|
||||
end
|
||||
|
||||
return gangs
|
||||
end
|
||||
|
||||
-- Returns a list of locations from QBCore.Shared.Loactions
|
||||
local function GetLocations()
|
||||
local locations = {}
|
||||
|
||||
for name, v in pairs(QBCore.Shared.Locations) do
|
||||
locations[#locations + 1] = { label = name, value = v }
|
||||
end
|
||||
|
||||
return locations
|
||||
end
|
||||
|
||||
-- Sends data to the UI on resource start
|
||||
function GetData()
|
||||
SendNUIMessage({
|
||||
action = "data",
|
||||
data = {
|
||||
vehicles = GetVehicles(),
|
||||
items = GetItems(),
|
||||
jobs = GetJobs(),
|
||||
gangs = GetGangs(),
|
||||
locations = GetLocations(),
|
||||
pedlist = PedList
|
||||
},
|
||||
})
|
||||
end
|
231
resources/[ps]/ps-adminmenu/client/entity_view.lua
Normal file
231
resources/[ps]/ps-adminmenu/client/entity_view.lua
Normal file
@ -0,0 +1,231 @@
|
||||
|
||||
local dickheaddebug = false
|
||||
|
||||
local Keys = {
|
||||
["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57,
|
||||
["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177,
|
||||
["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18,
|
||||
["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182,
|
||||
["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81,
|
||||
["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70,
|
||||
["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178,
|
||||
["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173,
|
||||
["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118
|
||||
}
|
||||
|
||||
RegisterNetEvent("hud:enabledebug")
|
||||
AddEventHandler("hud:enabledebug",function()
|
||||
if not CheckPerms(Config.Actions["noclip"].perms) then return end
|
||||
dickheaddebug = not dickheaddebug
|
||||
if dickheaddebug then
|
||||
print("Debug: Enabled")
|
||||
else
|
||||
print("Debug: Disabled")
|
||||
end
|
||||
end)
|
||||
|
||||
local inFreeze = false
|
||||
local lowGrav = false
|
||||
|
||||
function drawTxt(x,y ,width,height,scale, text, r,g,b,a)
|
||||
SetTextFont(0)
|
||||
SetTextProportional(0)
|
||||
SetTextScale(0.25, 0.25)
|
||||
SetTextColour(r, g, b, a)
|
||||
SetTextDropShadow(0, 0, 0, 0,255)
|
||||
SetTextEdge(1, 0, 0, 0, 255)
|
||||
SetTextDropShadow()
|
||||
SetTextOutline()
|
||||
SetTextEntry("STRING")
|
||||
AddTextComponentString(text)
|
||||
DrawText(x - width/2, y - height/2 + 0.005)
|
||||
end
|
||||
|
||||
|
||||
function DrawText3Ds(x,y,z, text)
|
||||
local onScreen,_x,_y=World3dToScreen2d(x,y,z)
|
||||
local px,py,pz=table.unpack(GetGameplayCamCoords())
|
||||
|
||||
SetTextScale(0.35, 0.35)
|
||||
SetTextFont(4)
|
||||
SetTextProportional(1)
|
||||
SetTextColour(255, 255, 255, 215)
|
||||
SetTextEntry("STRING")
|
||||
SetTextCentre(1)
|
||||
AddTextComponentString(text)
|
||||
DrawText(_x,_y)
|
||||
local factor = (string.len(text)) / 370
|
||||
DrawRect(_x,_y+0.0125, 0.015+ factor, 0.03, 41, 11, 41, 68)
|
||||
end
|
||||
|
||||
function GetVehicle()
|
||||
local playerped = GetPlayerPed(-1)
|
||||
local playerCoords = GetEntityCoords(playerped)
|
||||
local handle, ped = FindFirstVehicle()
|
||||
local success
|
||||
local rped = nil
|
||||
local distanceFrom
|
||||
repeat
|
||||
local pos = GetEntityCoords(ped)
|
||||
local distance = GetDistanceBetweenCoords(playerCoords, pos, true)
|
||||
if canPedBeUsed(ped) and distance < 30.0 and (distanceFrom == nil or distance < distanceFrom) then
|
||||
distanceFrom = distance
|
||||
rped = ped
|
||||
-- FreezeEntityPosition(ped, inFreeze)
|
||||
if IsEntityTouchingEntity(GetPlayerPed(-1), ped) then
|
||||
DrawText3Ds(pos["x"],pos["y"],pos["z"]+1, "Veh: " .. ped .. " Model: " .. GetEntityModel(ped) .. " - RØRER" )
|
||||
else
|
||||
DrawText3Ds(pos["x"],pos["y"],pos["z"]+1, "Veh: " .. ped .. " Model: " .. GetEntityModel(ped) .. "" )
|
||||
end
|
||||
if lowGrav then
|
||||
SetEntityCoords(ped,pos["x"],pos["y"],pos["z"]+5.0)
|
||||
end
|
||||
end
|
||||
success, ped = FindNextVehicle(handle)
|
||||
until not success
|
||||
EndFindVehicle(handle)
|
||||
return rped
|
||||
end
|
||||
|
||||
function GetObject()
|
||||
local playerped = GetPlayerPed(-1)
|
||||
local playerCoords = GetEntityCoords(playerped)
|
||||
local handle, ped = FindFirstObject()
|
||||
local success
|
||||
local rped = nil
|
||||
local distanceFrom
|
||||
repeat
|
||||
local pos = GetEntityCoords(ped)
|
||||
local distance = GetDistanceBetweenCoords(playerCoords, pos, true)
|
||||
if distance < 5.0 then
|
||||
distanceFrom = distance
|
||||
rped = ped
|
||||
--FreezeEntityPosition(ped, inFreeze)
|
||||
if IsEntityTouchingEntity(GetPlayerPed(-1), ped) then
|
||||
DrawText3Ds(pos["x"],pos["y"],pos["z"]+1, "Obj: " .. ped .. " Model: " .. GetEntityModel(ped) .. " - RØRER" )
|
||||
-- DrawText3Ds(pos["x"],pos["y"],pos["z"]+0.8, "Coordinates: "..pos["x"]..", "..pos["y"]..", "..pos["z"].." IN CONTACT" )
|
||||
else
|
||||
DrawText3Ds(pos["x"],pos["y"],pos["z"]+1, "Obj: " .. ped .. " Model: " .. GetEntityModel(ped) .. "" )
|
||||
-- DrawText3Ds(pos["x"],pos["y"],pos["z"]+0.8, "Coordinates: "..pos["x"]..", "..pos["y"]..", "..pos["z"].."")
|
||||
end
|
||||
|
||||
if lowGrav then
|
||||
--ActivatePhysics(ped)
|
||||
SetEntityCoords(ped,pos["x"],pos["y"],pos["z"]+0.1)
|
||||
FreezeEntityPosition(ped, false)
|
||||
end
|
||||
end
|
||||
|
||||
success, ped = FindNextObject(handle)
|
||||
until not success
|
||||
EndFindObject(handle)
|
||||
return rped
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
function getNPC()
|
||||
local playerped = GetPlayerPed(-1)
|
||||
local playerCoords = GetEntityCoords(playerped)
|
||||
local handle, ped = FindFirstPed()
|
||||
local success
|
||||
local rped = nil
|
||||
local distanceFrom
|
||||
repeat
|
||||
local pos = GetEntityCoords(ped)
|
||||
local distance = GetDistanceBetweenCoords(playerCoords, pos, true)
|
||||
if canPedBeUsed(ped) and distance < 30.0 and (distanceFrom == nil or distance < distanceFrom) then
|
||||
distanceFrom = distance
|
||||
rped = ped
|
||||
|
||||
if IsEntityTouchingEntity(GetPlayerPed(-1), ped) then
|
||||
DrawText3Ds(pos["x"],pos["y"],pos["z"], "Ped: " .. ped .. " Model: " .. GetEntityModel(ped) .. " Relationship HASH: " .. GetPedRelationshipGroupHash(ped) .. " IN CONTACT" )
|
||||
else
|
||||
DrawText3Ds(pos["x"],pos["y"],pos["z"], "Ped: " .. ped .. " Model: " .. GetEntityModel(ped) .. " Relationship HASH: " .. GetPedRelationshipGroupHash(ped) )
|
||||
end
|
||||
|
||||
FreezeEntityPosition(ped, inFreeze)
|
||||
if lowGrav then
|
||||
SetPedToRagdoll(ped, 511, 511, 0, 0, 0, 0)
|
||||
SetEntityCoords(ped,pos["x"],pos["y"],pos["z"]+0.1)
|
||||
end
|
||||
end
|
||||
success, ped = FindNextPed(handle)
|
||||
until not success
|
||||
EndFindPed(handle)
|
||||
return rped
|
||||
end
|
||||
|
||||
function canPedBeUsed(ped)
|
||||
if ped == nil then
|
||||
return false
|
||||
end
|
||||
if ped == GetPlayerPed(-1) then
|
||||
return false
|
||||
end
|
||||
if not DoesEntityExist(ped) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
|
||||
Citizen.CreateThread( function()
|
||||
|
||||
while true do
|
||||
|
||||
Citizen.Wait(1)
|
||||
|
||||
if dickheaddebug then
|
||||
local pos = GetEntityCoords(GetPlayerPed(-1))
|
||||
|
||||
local forPos = GetOffsetFromEntityInWorldCoords(GetPlayerPed(-1), 0, 1.0, 0.0)
|
||||
local backPos = GetOffsetFromEntityInWorldCoords(GetPlayerPed(-1), 0, -1.0, 0.0)
|
||||
local LPos = GetOffsetFromEntityInWorldCoords(GetPlayerPed(-1), 1.0, 0.0, 0.0)
|
||||
local RPos = GetOffsetFromEntityInWorldCoords(GetPlayerPed(-1), -1.0, 0.0, 0.0)
|
||||
|
||||
local forPos2 = GetOffsetFromEntityInWorldCoords(GetPlayerPed(-1), 0, 2.0, 0.0)
|
||||
local backPos2 = GetOffsetFromEntityInWorldCoords(GetPlayerPed(-1), 0, -2.0, 0.0)
|
||||
local LPos2 = GetOffsetFromEntityInWorldCoords(GetPlayerPed(-1), 2.0, 0.0, 0.0)
|
||||
local RPos2 = GetOffsetFromEntityInWorldCoords(GetPlayerPed(-1), -2.0, 0.0, 0.0)
|
||||
|
||||
local x, y, z = table.unpack(GetEntityCoords(GetPlayerPed(-1), true))
|
||||
local currentStreetHash, intersectStreetHash = GetStreetNameAtCoord(x, y, z, currentStreetHash, intersectStreetHash)
|
||||
currentStreetName = GetStreetNameFromHashKey(currentStreetHash)
|
||||
|
||||
drawTxt(0.8, 0.50, 0.4,0.4,0.30, "Heading: " .. GetEntityHeading(GetPlayerPed(-1)), 55, 155, 55, 255)
|
||||
drawTxt(0.8, 0.52, 0.4,0.4,0.30, "Coords: " .. pos, 55, 155, 55, 255)
|
||||
drawTxt(0.8, 0.54, 0.4,0.4,0.30, "Attached Ent: " .. GetEntityAttachedTo(GetPlayerPed(-1)), 55, 155, 55, 255)
|
||||
drawTxt(0.8, 0.56, 0.4,0.4,0.30, "Health: " .. GetEntityHealth(GetPlayerPed(-1)), 55, 155, 55, 255)
|
||||
drawTxt(0.8, 0.58, 0.4,0.4,0.30, "H a G: " .. GetEntityHeightAboveGround(GetPlayerPed(-1)), 55, 155, 55, 255)
|
||||
drawTxt(0.8, 0.60, 0.4,0.4,0.30, "Model: " .. GetEntityModel(GetPlayerPed(-1)), 55, 155, 55, 255)
|
||||
drawTxt(0.8, 0.62, 0.4,0.4,0.30, "Speed: " .. GetEntitySpeed(GetPlayerPed(-1)), 55, 155, 55, 255)
|
||||
drawTxt(0.8, 0.64, 0.4,0.4,0.30, "Frame Time: " .. GetFrameTime(), 55, 155, 55, 255)
|
||||
drawTxt(0.8, 0.66, 0.4,0.4,0.30, "Street: " .. currentStreetName, 55, 155, 55, 255)
|
||||
|
||||
|
||||
DrawLine(pos,forPos, 255,0,0,115)
|
||||
DrawLine(pos,backPos, 255,0,0,115)
|
||||
|
||||
DrawLine(pos,LPos, 255,255,0,115)
|
||||
DrawLine(pos,RPos, 255,255,0,115)
|
||||
|
||||
DrawLine(forPos,forPos2, 255,0,255,115)
|
||||
DrawLine(backPos,backPos2, 255,0,255,115)
|
||||
|
||||
DrawLine(LPos,LPos2, 255,255,255,115)
|
||||
DrawLine(RPos,RPos2, 255,255,255,115)
|
||||
|
||||
local nearped = getNPC()
|
||||
|
||||
local veh = GetVehicle()
|
||||
|
||||
local nearobj = GetObject()
|
||||
|
||||
else
|
||||
Citizen.Wait(5000)
|
||||
end
|
||||
end
|
||||
end)
|
40
resources/[ps]/ps-adminmenu/client/inventory.lua
Normal file
40
resources/[ps]/ps-adminmenu/client/inventory.lua
Normal file
@ -0,0 +1,40 @@
|
||||
-- Open Inventory
|
||||
RegisterNetEvent('ps-adminmenu:client:openInventory', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local player = selectedData["Player"].value
|
||||
|
||||
if Config.Inventory == 'ox_inventory' then
|
||||
TriggerServerEvent("ps-adminmenu:server:OpenInv", player)
|
||||
else
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "otherplayer", player)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Open Stash
|
||||
RegisterNetEvent('ps-adminmenu:client:openStash', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local stash = selectedData["Stash"].value
|
||||
|
||||
if Config.Inventory == 'ox_inventory' then
|
||||
TriggerServerEvent("ps-adminmenu:server:OpenStash", stash)
|
||||
else
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "stash", tostring(stash))
|
||||
TriggerEvent("inventory:client:SetCurrentStash", tostring(stash))
|
||||
end
|
||||
end)
|
||||
|
||||
-- Open Trunk
|
||||
RegisterNetEvent('ps-adminmenu:client:openTrunk', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local vehiclePlate = selectedData["Plate"].value
|
||||
|
||||
if Config.Inventory == 'ox_inventory' then
|
||||
TriggerServerEvent("ps-adminmenu:server:OpenTrunk", vehiclePlate)
|
||||
else
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "trunk", tostring(vehiclePlate))
|
||||
TriggerEvent("inventory:client:SetCurrentStash", tostring(vehiclePlate))
|
||||
end
|
||||
end)
|
79
resources/[ps]/ps-adminmenu/client/main.lua
Normal file
79
resources/[ps]/ps-adminmenu/client/main.lua
Normal file
@ -0,0 +1,79 @@
|
||||
QBCore = exports['qb-core']:GetCoreObject()
|
||||
PlayerData = {}
|
||||
|
||||
-- Functions
|
||||
local function setupMenu()
|
||||
Wait(500)
|
||||
PlayerData = QBCore.Functions.GetPlayerData()
|
||||
local resources = lib.callback.await('ps-adminmenu:callback:GetResources', false)
|
||||
local commands = lib.callback.await('ps-adminmenu:callback:GetCommands', false)
|
||||
GetData()
|
||||
SendNUIMessage({
|
||||
action = "setupUI",
|
||||
data = {
|
||||
actions = Config.Actions,
|
||||
resources = resources,
|
||||
playerData = PlayerData,
|
||||
commands = commands
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
-- Event Handlers
|
||||
AddEventHandler("QBCore:Client:OnPlayerLoaded", function()
|
||||
setupMenu()
|
||||
end)
|
||||
|
||||
AddEventHandler("onResourceStart", function(resourceName)
|
||||
if (GetCurrentResourceName() == resourceName) then
|
||||
setupMenu()
|
||||
end
|
||||
end)
|
||||
|
||||
-- NUICallbacks
|
||||
RegisterNUICallback("hideUI", function()
|
||||
ToggleUI(false)
|
||||
end)
|
||||
|
||||
RegisterNUICallback("clickButton", function(data)
|
||||
local selectedData = data.selectedData
|
||||
local key = data.data
|
||||
local data = CheckDataFromKey(key)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
if data.type == "client" then
|
||||
TriggerEvent(data.event, key, selectedData)
|
||||
elseif data.type == "server" then
|
||||
TriggerServerEvent(data.event, key, selectedData)
|
||||
elseif data.type == "command" then
|
||||
ExecuteCommand(data.event)
|
||||
end
|
||||
|
||||
Log("Action Used",
|
||||
PlayerData.name ..
|
||||
" (" ..
|
||||
PlayerData.citizenid ..
|
||||
") - Used: " .. data.label .. (selectedData and (" with args: " .. json.encode(selectedData)) or ""))
|
||||
end)
|
||||
|
||||
-- Open UI Event
|
||||
RegisterNetEvent('ps-adminmenu:client:OpenUI', function()
|
||||
ToggleUI(true)
|
||||
end)
|
||||
|
||||
-- Close UI Event
|
||||
RegisterNetEvent('ps-adminmenu:client:CloseUI', function()
|
||||
ToggleUI(false)
|
||||
end)
|
||||
|
||||
-- Change resource state
|
||||
RegisterNUICallback("setResourceState", function(data, cb)
|
||||
local resources = lib.callback.await('ps-adminmenu:callback:ChangeResourceState', false, data)
|
||||
cb(resources)
|
||||
end)
|
||||
|
||||
-- Get players
|
||||
RegisterNUICallback("getPlayers", function(data, cb)
|
||||
local players = lib.callback.await('ps-adminmenu:callback:GetPlayers', false)
|
||||
cb(players)
|
||||
end)
|
221
resources/[ps]/ps-adminmenu/client/misc.lua
Normal file
221
resources/[ps]/ps-adminmenu/client/misc.lua
Normal file
@ -0,0 +1,221 @@
|
||||
-- Toggles Invincibility
|
||||
local visible = true
|
||||
RegisterNetEvent('ps-adminmenu:client:ToggleInvisible', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
visible = not visible
|
||||
|
||||
SetEntityVisible(cache.ped, visible, 0)
|
||||
end)
|
||||
|
||||
-- God Mode
|
||||
local godmode = false
|
||||
RegisterNetEvent('ps-adminmenu:client:ToggleGodmode', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
godmode = not godmode
|
||||
|
||||
if godmode then
|
||||
QBCore.Functions.Notify(locale("godmode", "enabled"), 'primary')
|
||||
exports['qb-ambulancejob']:ResetAll()
|
||||
while godmode do
|
||||
Wait(0)
|
||||
SetPlayerInvincible(cache.playerId, true)
|
||||
end
|
||||
SetPlayerInvincible(cache.playerId, false)
|
||||
QBCore.Functions.Notify(locale("godmode", "disabled"), 'primary')
|
||||
end
|
||||
end)
|
||||
|
||||
-- Cuff/Uncuff
|
||||
RegisterNetEvent('ps-adminmenu:client:ToggleCuffs', function(player)
|
||||
local target = GetPlayerServerId(player)
|
||||
TriggerEvent("police:client:GetCuffed", target)
|
||||
end)
|
||||
|
||||
-- Copy Coordinates
|
||||
local function CopyCoords(data)
|
||||
local coords = GetEntityCoords(cache.ped)
|
||||
local heading = GetEntityHeading(cache.ped)
|
||||
local formats = { vector2 = "%.2f, %.2f", vector3 = "%.2f, %.2f, %.2f", vector4 = "%.2f, %.2f, %.2f, %.2f", heading =
|
||||
"%.2f" }
|
||||
local format = formats[data]
|
||||
|
||||
local clipboardText = ""
|
||||
if data == "vector2" then
|
||||
clipboardText = string.format(format, coords.x, coords.y)
|
||||
elseif data == "vector3" then
|
||||
clipboardText = string.format(format, coords.x, coords.y, coords.z)
|
||||
elseif data == "vector4" then
|
||||
clipboardText = string.format(format, coords.x, coords.y, coords.z, heading)
|
||||
elseif data == "heading" then
|
||||
clipboardText = string.format(format, heading)
|
||||
end
|
||||
|
||||
lib.setClipboard(clipboardText)
|
||||
end
|
||||
|
||||
RegisterCommand("vector2", function()
|
||||
if not CheckPerms('mod') then return end
|
||||
CopyCoords("vector2")
|
||||
end, false)
|
||||
|
||||
RegisterCommand("vector3", function()
|
||||
if not CheckPerms('mod') then return end
|
||||
CopyCoords("vector3")
|
||||
end, false)
|
||||
|
||||
RegisterCommand("vector4", function()
|
||||
if not CheckPerms('mod') then return end
|
||||
CopyCoords("vector4")
|
||||
end, false)
|
||||
|
||||
RegisterCommand("heading", function()
|
||||
if not CheckPerms('mod') then return end
|
||||
CopyCoords("heading")
|
||||
end, false)
|
||||
|
||||
-- Infinite Ammo
|
||||
local InfiniteAmmo = false
|
||||
RegisterNetEvent('ps-adminmenu:client:setInfiniteAmmo', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
InfiniteAmmo = not InfiniteAmmo
|
||||
|
||||
if GetAmmoInPedWeapon(cache.ped, cache.weapon) < 6 then
|
||||
SetAmmoInClip(cache.ped, cache.weapon, 10)
|
||||
Wait(50)
|
||||
end
|
||||
|
||||
while InfiniteAmmo do
|
||||
SetPedInfiniteAmmo(cache.ped, true, cache.weapon)
|
||||
RefillAmmoInstantly(cache.ped)
|
||||
Wait(250)
|
||||
end
|
||||
|
||||
SetPedInfiniteAmmo(cache.ped, false, cache.weapon)
|
||||
end)
|
||||
|
||||
-- Toggle coords
|
||||
local showCoords = false
|
||||
local function showCoordsMenu()
|
||||
while showCoords do
|
||||
Wait(50)
|
||||
local coords = GetEntityCoords(PlayerPedId())
|
||||
local heading = GetEntityHeading(PlayerPedId())
|
||||
SendNUIMessage({
|
||||
action = "showCoordsMenu",
|
||||
data = {
|
||||
show = showCoords,
|
||||
x = QBCore.Shared.Round(coords.x, 2),
|
||||
y = QBCore.Shared.Round(coords.y, 2),
|
||||
z = QBCore.Shared.Round(coords.z, 2),
|
||||
heading = QBCore.Shared.Round(heading, 2)
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:client:ToggleCoords', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
showCoords = not showCoords
|
||||
|
||||
if showCoords then
|
||||
CreateThread(showCoordsMenu)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Set Ammo
|
||||
RegisterNetEvent('ps-adminmenu:client:SetAmmo', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local ammo = selectedData["Ammo Ammount"].value
|
||||
local weapon = GetSelectedPedWeapon(cache.ped)
|
||||
|
||||
if weapon ~= nil then
|
||||
SetPedAmmo(cache.ped, weapon, ammo)
|
||||
QBCore.Functions.Notify(locale("set_wepaon_ammo", tostring(ammo)), 'success')
|
||||
else
|
||||
QBCore.Functions.Notify(locale("no_weapon"), 'error')
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterCommand("setammo", function(source)
|
||||
if not CheckPerms('mod') then return end
|
||||
local weapon = GetSelectedPedWeapon(cache.ped)
|
||||
local ammo = 999
|
||||
if weapon ~= nil then
|
||||
SetPedAmmo(cache.ped, weapon, ammo)
|
||||
QBCore.Functions.Notify(locale("set_wepaon_ammo", tostring(ammo)), 'success')
|
||||
else
|
||||
QBCore.Functions.Notify(locale("no_weapon"), 'error')
|
||||
end
|
||||
end, false)
|
||||
|
||||
--Toggle Dev
|
||||
local ToggleDev = false
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:client:ToggleDev', function(dataKey)
|
||||
local data = CheckDataFromKey(dataKey)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
ToggleDev = not ToggleDev
|
||||
|
||||
SetPlayerInvincible(PlayerId(), ToggleDev) -- toggle dev mode (ps-hud/qb-hud)
|
||||
TriggerEvent('ps-adminmenu:client:ToggleCoords', dataKey) -- toggle Coords
|
||||
TriggerEvent('ps-adminmenu:client:ToggleGodmode', dataKey) -- Godmode
|
||||
|
||||
QBCore.Functions.Notify(locale("toggle_dev"), 'success')
|
||||
end)
|
||||
|
||||
-- Key Bindings
|
||||
local toogleAdmin = lib.addKeybind({
|
||||
name = 'toogleAdmin',
|
||||
description = locale("command_admin_desc"),
|
||||
defaultKey = Config.AdminKey,
|
||||
onPressed = function(self)
|
||||
ExecuteCommand('admin')
|
||||
end
|
||||
})
|
||||
|
||||
--noclip
|
||||
RegisterCommand('nc', function()
|
||||
TriggerEvent(Config.Actions["noclip"].event)
|
||||
end, false)
|
||||
|
||||
RegisterCommand('debug', function()
|
||||
TriggerEvent(Config.Actions["debug"].event)
|
||||
end, false)
|
||||
|
||||
RegisterCommand('dev', function()
|
||||
TriggerEvent(Config.OtherActions["toggleDevmode"].event)
|
||||
TriggerEvent(Config.Actions["debug"].event)
|
||||
end, false)
|
||||
|
||||
local toogleNoclip = lib.addKeybind({
|
||||
name = 'toogleNoclip',
|
||||
description = locale("command_noclip_desc"),
|
||||
defaultKey = Config.NoclipKey,
|
||||
onPressed = function(self)
|
||||
ExecuteCommand('nc')
|
||||
end
|
||||
})
|
||||
|
||||
if Config.Keybindings then
|
||||
toogleAdmin:disable(false)
|
||||
toogleNoclip:disable(false)
|
||||
else
|
||||
toogleAdmin:disable(true)
|
||||
toogleNoclip:disable(true)
|
||||
end
|
||||
|
||||
-- Set Ped
|
||||
RegisterNetEvent("ps-adminmenu:client:setPed", function(pedModels)
|
||||
lib.requestModel(pedModels, 1500)
|
||||
SetPlayerModel(cache.playerId, pedModels)
|
||||
SetPedDefaultComponentVariation(cache.ped)
|
||||
SetModelAsNoLongerNeeded(pedModels)
|
||||
end)
|
206
resources/[ps]/ps-adminmenu/client/noclip.lua
Normal file
206
resources/[ps]/ps-adminmenu/client/noclip.lua
Normal file
@ -0,0 +1,206 @@
|
||||
local noclip = false
|
||||
local cam = 0
|
||||
local ped
|
||||
local speed = 1
|
||||
local maxSpeed = 16
|
||||
|
||||
|
||||
-- Disable the controls
|
||||
local function DisabledControls()
|
||||
HudWeaponWheelIgnoreSelection()
|
||||
DisableAllControlActions(0)
|
||||
DisableAllControlActions(1)
|
||||
DisableAllControlActions(2)
|
||||
EnableControlAction(0, 220, true)
|
||||
EnableControlAction(0, 221, true)
|
||||
EnableControlAction(0, 245, true)
|
||||
end
|
||||
|
||||
-- Setup the camera
|
||||
local function SetupCam()
|
||||
local rotation = GetEntityRotation(ped)
|
||||
local coords = GetEntityCoords(ped)
|
||||
|
||||
cam = CreateCameraWithParams("DEFAULT_SCRIPTED_CAMERA", coords, vector3(0.0, 0.0, rotation.z), 75.0)
|
||||
SetCamActive(cam, true)
|
||||
RenderScriptCams(true, true, 1000, false, false)
|
||||
AttachCamToEntity(cam, ped, 0.0, 0.0, 1.0, true)
|
||||
end
|
||||
|
||||
-- Destroys the camera
|
||||
local function DestoryCam()
|
||||
Wait(100)
|
||||
SetGameplayCamRelativeHeading(0)
|
||||
RenderScriptCams(false, true, 1000, true, true)
|
||||
DetachEntity(ped, true, true)
|
||||
SetCamActive(cam, false)
|
||||
DestroyCam(cam, true)
|
||||
end
|
||||
|
||||
-- Checks if a control is always pressed
|
||||
local IsControlAlwaysPressed = function(inputGroup, control)
|
||||
return IsControlPressed(inputGroup, control) or IsDisabledControlPressed(inputGroup, control)
|
||||
end
|
||||
|
||||
-- Updates the camera rotation
|
||||
local function UpdateCameraRotation()
|
||||
local rightAxisX = GetControlNormal(0, 220)
|
||||
local rightAxisY = GetControlNormal(0, 221)
|
||||
local rotation = GetCamRot(cam, 2)
|
||||
local yValue = rightAxisY * -5
|
||||
local newX
|
||||
local newZ = rotation.z + (rightAxisX * -10)
|
||||
|
||||
if (rotation.x + yValue > -89.0) and (rotation.x + yValue < 89.0) then
|
||||
newX = rotation.x + yValue
|
||||
end
|
||||
|
||||
if newX ~= nil and newZ ~= nil then
|
||||
SetCamRot(cam, vector3(newX, rotation.y, newZ), 2)
|
||||
end
|
||||
|
||||
SetEntityHeading(ped, math.max(0, (rotation.z % 360)))
|
||||
end
|
||||
|
||||
-- Gets the ground coords
|
||||
local function TeleportToGround()
|
||||
local coords = GetEntityCoords(ped)
|
||||
local rayCast = StartShapeTestRay(coords.x, coords.y, coords.z, coords.x, coords.y, -10000.0, 1, 0)
|
||||
local _, hit, hitCoords = GetShapeTestResult(rayCast)
|
||||
|
||||
if hit == 1 then
|
||||
SetEntityCoords(ped, hitCoords.x, hitCoords.y, hitCoords.z)
|
||||
else
|
||||
SetEntityCoords(ped, coords.x, coords.y, coords.z)
|
||||
end
|
||||
end
|
||||
|
||||
-- Toggles the behavior of visiblty, collision, etc
|
||||
local function ToggleBehavior(bool)
|
||||
local coords = GetEntityCoords(ped)
|
||||
|
||||
RequestCollisionAtCoord(coords.x, coords.y, coords.z)
|
||||
FreezeEntityPosition(ped, bool)
|
||||
SetEntityCollision(ped, not bool, not bool)
|
||||
SetEntityVisible(ped, not bool, not bool)
|
||||
SetEntityInvincible(ped, bool)
|
||||
SetEntityAlpha(ped, bool and noclipAlpha or 255, false)
|
||||
SetLocalPlayerVisibleLocally(true)
|
||||
SetEveryoneIgnorePlayer(ped, bool)
|
||||
SetPoliceIgnorePlayer(ped, bool)
|
||||
|
||||
local vehicle = GetVehiclePedIsIn(ped, false)
|
||||
if vehicle ~= 0 then
|
||||
SetEntityAlpha(vehicle, bool and noclipAlpha or 255, false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Stops the noclip
|
||||
local function StopNoclip()
|
||||
DestoryCam()
|
||||
TeleportToGround()
|
||||
ToggleBehavior(false)
|
||||
end
|
||||
|
||||
-- Handels the speed
|
||||
local function UpdateSpeed()
|
||||
if IsControlAlwaysPressed(2, 14) then
|
||||
speed = speed - 0.5
|
||||
if speed < 0.5 then
|
||||
speed = 0.5
|
||||
end
|
||||
elseif IsControlAlwaysPressed(2, 15) then
|
||||
speed = speed + 0.5
|
||||
if speed > maxSpeed then
|
||||
speed = maxSpeed
|
||||
end
|
||||
elseif IsDisabledControlJustReleased(0, 348) then
|
||||
speed = 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Handels the movement
|
||||
local function UpdateMovement()
|
||||
local multi = 1.0
|
||||
if IsControlAlwaysPressed(0, 21) then
|
||||
multi = 2
|
||||
elseif IsControlAlwaysPressed(0, 19) then
|
||||
multi = 4
|
||||
elseif IsControlAlwaysPressed(0, 36) then
|
||||
multi = 0.25
|
||||
end
|
||||
|
||||
if IsControlAlwaysPressed(0, 32) then
|
||||
local pitch = GetCamRot(cam, 0)
|
||||
|
||||
if pitch.x >= 0 then
|
||||
SetEntityCoordsNoOffset(ped,
|
||||
GetOffsetFromEntityInWorldCoords(ped, 0.0, 0.5 * (speed * multi),
|
||||
(pitch.x * ((speed / 2) * multi)) / 89))
|
||||
else
|
||||
SetEntityCoordsNoOffset(ped,
|
||||
GetOffsetFromEntityInWorldCoords(ped, 0.0, 0.5 * (speed * multi),
|
||||
-1 * ((math.abs(pitch.x) * ((speed / 2) * multi)) / 89)))
|
||||
end
|
||||
elseif IsControlAlwaysPressed(0, 33) then
|
||||
local pitch = GetCamRot(cam, 2)
|
||||
|
||||
if pitch.x >= 0 then
|
||||
SetEntityCoordsNoOffset(ped,
|
||||
GetOffsetFromEntityInWorldCoords(ped, 0.0, -0.5 * (speed * multi),
|
||||
-1 * (pitch.x * ((speed / 2) * multi)) / 89))
|
||||
else
|
||||
SetEntityCoordsNoOffset(ped,
|
||||
GetOffsetFromEntityInWorldCoords(ped, 0.0, -0.5 * (speed * multi),
|
||||
((math.abs(pitch.x) * ((speed / 2) * multi)) / 89)))
|
||||
end
|
||||
end
|
||||
|
||||
if IsControlAlwaysPressed(0, 34) then
|
||||
SetEntityCoordsNoOffset(ped,
|
||||
GetOffsetFromEntityInWorldCoords(ped, -0.5 * (speed * multi), 0.0, 0.0))
|
||||
elseif IsControlAlwaysPressed(0, 35) then
|
||||
SetEntityCoordsNoOffset(ped,
|
||||
GetOffsetFromEntityInWorldCoords(ped, 0.5 * (speed * multi), 0.0, 0.0))
|
||||
end
|
||||
|
||||
if IsControlAlwaysPressed(0, 44) then
|
||||
SetEntityCoordsNoOffset(ped,
|
||||
GetOffsetFromEntityInWorldCoords(ped, 0.0, 0.0, 0.5 * (speed * multi)))
|
||||
elseif IsControlAlwaysPressed(0, 46) then
|
||||
SetEntityCoordsNoOffset(ped,
|
||||
GetOffsetFromEntityInWorldCoords(ped, 0.0, 0.0, -0.5 * (speed * multi)))
|
||||
end
|
||||
end
|
||||
|
||||
-- Toggles the noclip
|
||||
local function ToggleNoclip()
|
||||
noclip = not noclip
|
||||
|
||||
if cache.vehicle then
|
||||
ped = cache.vehicle
|
||||
else
|
||||
ped = cache.ped
|
||||
end
|
||||
|
||||
if noclip then
|
||||
SetupCam()
|
||||
ToggleBehavior(true)
|
||||
while noclip do
|
||||
Wait(0)
|
||||
UpdateCameraRotation()
|
||||
DisabledControls()
|
||||
UpdateSpeed()
|
||||
UpdateMovement()
|
||||
end
|
||||
else
|
||||
StopNoclip()
|
||||
end
|
||||
end
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:client:ToggleNoClip', function()
|
||||
if not CheckPerms(Config.Actions["noclip"].perms) then return end
|
||||
ToggleNoclip()
|
||||
end)
|
||||
|
182
resources/[ps]/ps-adminmenu/client/players.lua
Normal file
182
resources/[ps]/ps-adminmenu/client/players.lua
Normal file
@ -0,0 +1,182 @@
|
||||
local ShowBlips = false
|
||||
local ShowNames = false
|
||||
local NetCheck1 = false
|
||||
local NetCheck2 = false
|
||||
local Blip = nil
|
||||
local Tag = nil
|
||||
local currentPlayers = {}
|
||||
|
||||
-- Function to remove all names and Blips
|
||||
local function removeNameAndBlips()
|
||||
if DoesBlipExist(Blip) then
|
||||
RemoveBlip(Blip)
|
||||
end
|
||||
if Tag then
|
||||
SetMpGamerTagVisibility(Tag, 0, false)
|
||||
SetMpGamerTagVisibility(Tag, 2, false)
|
||||
SetMpGamerTagVisibility(Tag, 4, false)
|
||||
SetMpGamerTagVisibility(Tag, 6, false)
|
||||
RemoveMpGamerTag(Tag)
|
||||
end
|
||||
end
|
||||
|
||||
-- Function to Toggle Blips and Names
|
||||
local function ToggleBlipsAndNames(isBlips)
|
||||
if isBlips then
|
||||
ShowBlips = not ShowBlips
|
||||
NetCheck1 = ShowBlips
|
||||
local message = ShowBlips and "blips_activated" or "blips_deactivated"
|
||||
QBCore.Functions.Notify(locale(message), ShowBlips and "success" or "error")
|
||||
else
|
||||
ShowNames = not ShowNames
|
||||
NetCheck2 = ShowNames
|
||||
local message = ShowNames and "names_activated" or "names_deactivated"
|
||||
QBCore.Functions.Notify(locale(message), ShowNames and "success" or "error")
|
||||
end
|
||||
if not ShowNames or not ShowBlips then
|
||||
removeNameAndBlips()
|
||||
end
|
||||
end
|
||||
|
||||
-- Main Function to Update Blips and Names
|
||||
local function UpdateBlipsAndNames(players)
|
||||
local playerPed = PlayerPedId()
|
||||
local playerCoords = GetEntityCoords(playerPed, true)
|
||||
local blipSprites = { -- Sprite Per Vehicle Class
|
||||
[1] = 1,
|
||||
[8] = 226,
|
||||
[9] = 757,
|
||||
[10] = 477,
|
||||
[11] = 477,
|
||||
[12] = 67,
|
||||
[13] = 226,
|
||||
[14] = 427,
|
||||
[15] = 422,
|
||||
[16] = 423,
|
||||
[17] = 198,
|
||||
[18] = 56,
|
||||
[19] = 421,
|
||||
[20] = 477,
|
||||
}
|
||||
|
||||
for _, player in pairs(players) do
|
||||
local playerId = GetPlayerFromServerId(player.id)
|
||||
local ped = GetPlayerPed(playerId)
|
||||
local name = 'ID: ' .. player.id .. ' | ' .. player.name
|
||||
Blip = GetBlipFromEntity(ped)
|
||||
|
||||
Tag = CreateFakeMpGamerTag(ped, name, false, false, "", false)
|
||||
SetMpGamerTagAlpha(Tag, 0, 255)
|
||||
SetMpGamerTagAlpha(Tag, 2, 255)
|
||||
SetMpGamerTagAlpha(Tag, 4, 255)
|
||||
SetMpGamerTagAlpha(Tag, 6, 255)
|
||||
SetMpGamerTagHealthBarColour(Tag, 25)
|
||||
|
||||
local isPlayerTalking = NetworkIsPlayerTalking(playerId)
|
||||
local isPlayerInvincible = GetPlayerInvincible(playerId)
|
||||
if ShowNames then
|
||||
SetMpGamerTagVisibility(Tag, 0, true)
|
||||
SetMpGamerTagVisibility(Tag, 2, true)
|
||||
SetMpGamerTagVisibility(Tag, 4, isPlayerTalking)
|
||||
SetMpGamerTagVisibility(Tag, 6, isPlayerInvincible)
|
||||
else
|
||||
SetMpGamerTagVisibility(Tag, 0, false)
|
||||
SetMpGamerTagVisibility(Tag, 2, false)
|
||||
SetMpGamerTagVisibility(Tag, 4, false)
|
||||
SetMpGamerTagVisibility(Tag, 6, false)
|
||||
RemoveMpGamerTag(Tag)
|
||||
end
|
||||
|
||||
if ShowBlips then
|
||||
if not DoesBlipExist(Blip) then
|
||||
Blip = AddBlipForEntity(ped)
|
||||
ShowHeadingIndicatorOnBlip(Blip, true)
|
||||
SetBlipCategory(Blip, 7)
|
||||
else
|
||||
local veh = GetVehiclePedIsIn(ped, false)
|
||||
local classveh = GetVehicleClass(veh)
|
||||
local modelveh = GetEntityModel(veh)
|
||||
if veh ~= 0 then
|
||||
local blipSprite = blipSprites[classveh] or 225
|
||||
if modelveh == 'besra' or modelveh == 'hydra' or modelveh == 'lazer' then
|
||||
blipSprite = 424
|
||||
end
|
||||
|
||||
SetBlipSprite(Blip, blipSprite)
|
||||
ShowHeadingIndicatorOnBlip(Blip, false)
|
||||
|
||||
local passengers = GetVehicleNumberOfPassengers(veh)
|
||||
if passengers then
|
||||
if not IsVehicleSeatFree(veh, -1) then
|
||||
passengers = passengers + 1
|
||||
end
|
||||
ShowNumberOnBlip(Blip, passengers)
|
||||
else
|
||||
HideNumberOnBlip(Blip)
|
||||
end
|
||||
|
||||
SetBlipRotation(Blip, math.ceil(GetEntityHeading(veh)))
|
||||
SetBlipNameToPlayerName(Blip, playerId)
|
||||
SetBlipScale(Blip, 0.85)
|
||||
|
||||
local distance = math.floor(Vdist(playerCoords.x, playerCoords.y, playerCoords.z,
|
||||
GetEntityCoords(ped, true).x, GetEntityCoords(ped, true).y, GetEntityCoords(ped, true).z) /
|
||||
-1) +
|
||||
900
|
||||
distance = math.max(0, math.min(255, distance))
|
||||
SetBlipAlpha(Blip, distance)
|
||||
else
|
||||
HideNumberOnBlip(Blip)
|
||||
SetBlipSprite(Blip, 1)
|
||||
SetBlipNameToPlayerName(Blip, playerId)
|
||||
ShowHeadingIndicatorOnBlip(Blip, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function preparePlayers()
|
||||
currentPlayers = {}
|
||||
Wait(100)
|
||||
currentPlayers = lib.callback.await('ps-adminmenu:callback:GetPlayers')
|
||||
end
|
||||
|
||||
-- Toggle Blips and Names events
|
||||
RegisterNetEvent('ps-adminmenu:client:toggleBlips', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
if not ShowBlips then preparePlayers() end
|
||||
ToggleBlipsAndNames(true)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:client:toggleNames', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
if not ShowNames then preparePlayers() end
|
||||
ToggleBlipsAndNames(false)
|
||||
end)
|
||||
|
||||
-- Mute Player
|
||||
RegisterNetEvent("ps-adminmenu:client:MutePlayer", function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local playerId = selectedData["Player"].value
|
||||
if not playerId then return end
|
||||
exports["pma-voice"]:toggleMutePlayer(playerId)
|
||||
end)
|
||||
|
||||
-- Main loop to check for updates
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(1000)
|
||||
if NetCheck1 or NetCheck2 then
|
||||
UpdateBlipsAndNames(currentPlayers)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Remove Stress
|
||||
RegisterNetEvent('ps-adminmenu:client:removeStress', function(data)
|
||||
TriggerServerEvent('hud:server:RelieveStress', 100)
|
||||
end)
|
47
resources/[ps]/ps-adminmenu/client/spectate.lua
Normal file
47
resources/[ps]/ps-adminmenu/client/spectate.lua
Normal file
@ -0,0 +1,47 @@
|
||||
local oldPos = nil
|
||||
local spectateInfo = {
|
||||
toggled = false,
|
||||
target = 0,
|
||||
targetPed = 0
|
||||
}
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:requestSpectate', function(targetPed, target, name)
|
||||
oldPos = GetEntityCoords(cache.ped)
|
||||
spectateInfo = {
|
||||
toggled = true,
|
||||
target = target,
|
||||
targetPed = targetPed
|
||||
}
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:cancelSpectate', function()
|
||||
if NetworkIsInSpectatorMode() then
|
||||
NetworkSetInSpectatorMode(false, spectateInfo['targetPed'])
|
||||
end
|
||||
SetEntityVisible(cache.ped, true, 0)
|
||||
spectateInfo = { toggled = false, target = 0, targetPed = 0 }
|
||||
RequestCollisionAtCoord(oldPos)
|
||||
SetEntityCoords(cache.ped, oldPos)
|
||||
oldPos = nil;
|
||||
end)
|
||||
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(0)
|
||||
if spectateInfo['toggled'] then
|
||||
local targetPed = NetworkGetEntityFromNetworkId(spectateInfo.targetPed)
|
||||
if DoesEntityExist(targetPed) then
|
||||
SetEntityVisible(cache.ped, false, 0)
|
||||
if not NetworkIsInSpectatorMode() then
|
||||
RequestCollisionAtCoord(GetEntityCoords(targetPed))
|
||||
NetworkSetInSpectatorMode(true, targetPed)
|
||||
end
|
||||
else
|
||||
TriggerServerEvent('ps-adminmenu:spectate:teleport', spectateInfo['target'])
|
||||
while not DoesEntityExist(NetworkGetEntityFromNetworkId(spectateInfo.targetPed)) do Wait(100) end
|
||||
end
|
||||
else
|
||||
Wait(500)
|
||||
end
|
||||
end
|
||||
end)
|
65
resources/[ps]/ps-adminmenu/client/teleport.lua
Normal file
65
resources/[ps]/ps-adminmenu/client/teleport.lua
Normal file
@ -0,0 +1,65 @@
|
||||
local lastCoords
|
||||
|
||||
local function teleport(x, y, z)
|
||||
if cache.vehicle then
|
||||
return SetPedCoordsKeepVehicle(cache.ped, x, y, z)
|
||||
end
|
||||
|
||||
SetEntityCoords(cache.ped, x, y, z, false, false, false, false)
|
||||
end
|
||||
|
||||
-- Teleport to player
|
||||
RegisterNetEvent('ps-adminmenu:client:TeleportToPlayer', function(coords)
|
||||
lastCoords = GetEntityCoords(cache.ped)
|
||||
SetPedCoordsKeepVehicle(cache.ped, coords.x, coords.y, coords.z)
|
||||
end)
|
||||
|
||||
-- Teleport to coords
|
||||
RegisterNetEvent('ps-adminmenu:client:TeleportToCoords', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local coordsStr = selectedData["Coords"].value
|
||||
local x, y, z, heading
|
||||
|
||||
x, y, z, heading = coordsStr:match("(-?%d+%.?%d*),%s*(-?%d+%.?%d*),?%s*(-?%d*%.?%d*),?%s*(-?%d*%.?%d*)")
|
||||
|
||||
if not x or not y then
|
||||
x, y, z, heading = coordsStr:match("(-?%d+%.?%d*)%s+(-?%d+%.?%d*)%s*(-?%d*%.?%d*)%s*(-?%d*%.?%d*)")
|
||||
end
|
||||
|
||||
x = tonumber(x)
|
||||
y = tonumber(y)
|
||||
z = tonumber(z or 0)
|
||||
heading = tonumber(heading or 0)
|
||||
|
||||
if x and y then
|
||||
lastCoords = GetEntityCoords(cache.ped)
|
||||
if heading and heading ~= 0 then
|
||||
SetEntityHeading(cache.ped, heading)
|
||||
end
|
||||
SetPedCoordsKeepVehicle(cache.ped, x, y, z)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Teleport to Locaton
|
||||
RegisterNetEvent('ps-adminmenu:client:TeleportToLocation', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local coords = selectedData["Location"].value
|
||||
|
||||
lastCoords = GetEntityCoords(cache.ped)
|
||||
SetPedCoordsKeepVehicle(cache.ped, coords.x, coords.y, coords.z)
|
||||
end)
|
||||
|
||||
-- Teleport back
|
||||
RegisterNetEvent('ps-adminmenu:client:TeleportBack', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
if lastCoords then
|
||||
local coords = GetEntityCoords(cache.ped)
|
||||
teleport(lastCoords.x, lastCoords.y, lastCoords.z)
|
||||
lastCoords = coords
|
||||
end
|
||||
end)
|
134
resources/[ps]/ps-adminmenu/client/toggle_laser.lua
Normal file
134
resources/[ps]/ps-adminmenu/client/toggle_laser.lua
Normal file
@ -0,0 +1,134 @@
|
||||
local ObjectList = require "data.object"
|
||||
|
||||
local function DrawEntityBoundingBox(entity, color)
|
||||
local model = GetEntityModel(entity)
|
||||
local min, max = GetModelDimensions(model)
|
||||
local rightVector, forwardVector, upVector, position = GetEntityMatrix(entity)
|
||||
|
||||
-- Calculate size
|
||||
local dim =
|
||||
{
|
||||
x = 0.5 * (max.x - min.x),
|
||||
y = 0.5 * (max.y - min.y),
|
||||
z = 0.5 * (max.z - min.z)
|
||||
}
|
||||
|
||||
-- Calculate the eight bounding box edges
|
||||
local edges = {}
|
||||
edges[1] = position - dim.y * rightVector - dim.x * forwardVector - dim.z * upVector
|
||||
edges[2] = edges[1] + 2 * dim.y * rightVector
|
||||
edges[3] = edges[2] + 2 * dim.z * upVector
|
||||
edges[4] = edges[1] + 2 * dim.z * upVector
|
||||
edges[5] = position + dim.y * rightVector + dim.x * forwardVector + dim.z * upVector
|
||||
edges[6] = edges[5] - 2 * dim.y * rightVector
|
||||
edges[7] = edges[6] - 2 * dim.z * upVector
|
||||
edges[8] = edges[5] - 2 * dim.z * upVector
|
||||
|
||||
-- Draw lines to connect the edges and create the bounding box
|
||||
for i = 1, 4 do
|
||||
local j = i % 4 + 1
|
||||
DrawLine(edges[i].x, edges[i].y, edges[i].z, edges[j].x, edges[j].y, edges[j].z, color.r, color.g, color.b, color.a)
|
||||
DrawLine(edges[i + 4].x, edges[i + 4].y, edges[i + 4].z, edges[j + 4].x, edges[j + 4].y, edges[j + 4].z, color.r, color.g, color.b, color.a)
|
||||
DrawLine(edges[i].x, edges[i].y, edges[i].z, edges[i + 4].x, edges[i + 4].y, edges[i + 4].z, color.r, color.g, color.b, color.a)
|
||||
end
|
||||
end
|
||||
|
||||
local function RotationToDirection(rotation)
|
||||
local adjustedRotation =
|
||||
{
|
||||
x = (math.pi / 180) * rotation.x,
|
||||
y = (math.pi / 180) * rotation.y,
|
||||
z = (math.pi / 180) * rotation.z
|
||||
}
|
||||
local direction =
|
||||
{
|
||||
x = -math.sin(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
|
||||
y = math.cos(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
|
||||
z = math.sin(adjustedRotation.x)
|
||||
}
|
||||
return direction
|
||||
end
|
||||
|
||||
local function RayCastGamePlayCamera(distance)
|
||||
local cameraRotation = GetGameplayCamRot()
|
||||
local cameraCoord = GetGameplayCamCoord()
|
||||
local direction = RotationToDirection(cameraRotation)
|
||||
local destination =
|
||||
{
|
||||
x = cameraCoord.x + direction.x * distance,
|
||||
y = cameraCoord.y + direction.y * distance,
|
||||
z = cameraCoord.z + direction.z * distance
|
||||
}
|
||||
local a, b, c, d, e = GetShapeTestResult(StartShapeTestRay(cameraCoord.x, cameraCoord.y, cameraCoord.z, destination.x, destination.y, destination.z, -1, PlayerPedId(), 0))
|
||||
return b, c, e
|
||||
end
|
||||
|
||||
-- Toggle Delete Laser
|
||||
local activeLaser = false
|
||||
RegisterNetEvent('ps-adminmenu:client:ToggleLaser', function()
|
||||
local x = 0.4
|
||||
local y = 0.025
|
||||
activeLaser = not activeLaser
|
||||
CreateThread(function()
|
||||
while true do
|
||||
local wait = 7
|
||||
if activeLaser then
|
||||
local color = {r = 255, g = 255, b = 255, a = 200}
|
||||
local position = GetEntityCoords(PlayerPedId())
|
||||
local hit, coords, entity = RayCastGamePlayCamera(1000.0)
|
||||
local objectData = {}
|
||||
|
||||
DisableControlAction(0, 200)
|
||||
DisableControlAction(0, 26)
|
||||
|
||||
if hit and (IsEntityAVehicle(entity) or IsEntityAPed(entity) or IsEntityAnObject(entity)) then
|
||||
local entityCoord = GetEntityCoords(entity)
|
||||
local heading = GetEntityHeading(entity)
|
||||
local model = GetEntityModel(entity)
|
||||
local minimum, maximum = GetModelDimensions(model)
|
||||
DrawEntityBoundingBox(entity, color)
|
||||
DrawLine(position.x, position.y, position.z, coords.x, coords.y, coords.z, color.r, color.g, color.b, color.a)
|
||||
|
||||
objectData.hash = model
|
||||
objectData.name = ObjectList[model]
|
||||
objectData.coords = ("vec4(%s, %s, %s, %s)"):format(entityCoord.x, entityCoord.y, entityCoord.z, heading)
|
||||
|
||||
if IsControlJustReleased(0, 38) then
|
||||
SetEntityAsMissionEntity(entity, true, true)
|
||||
DeleteEntity(entity)
|
||||
end
|
||||
|
||||
if IsDisabledControlJustReleased(0, 26) then
|
||||
lib.setClipboard(json.encode(objectData, {indent = true}))
|
||||
end
|
||||
|
||||
elseif coords.x ~= 0.0 and coords.y ~= 0.0 then
|
||||
DrawLine(position.x, position.y, position.z, coords.x, coords.y, coords.z, color.r, color.g, color.b, color.a)
|
||||
DrawMarker(28, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 0.1, 0.1, 0.1, color.r, color.g, color.b, color.a, false, true, 2, nil, nil, false)
|
||||
end
|
||||
|
||||
if IsDisabledControlJustReleased(0, 200) then
|
||||
activeLaser = not activeLaser
|
||||
end
|
||||
|
||||
SendNUIMessage({
|
||||
action = "showEntityInfo",
|
||||
data = {
|
||||
show = true,
|
||||
hash = objectData.hash or "",
|
||||
name = objectData.name or "",
|
||||
}
|
||||
})
|
||||
else
|
||||
local wait = 500
|
||||
SendNUIMessage({
|
||||
action = "showEntityInfo",
|
||||
data = {
|
||||
show = false,
|
||||
}
|
||||
})
|
||||
end
|
||||
Wait(wait)
|
||||
end
|
||||
end)
|
||||
end)
|
48
resources/[ps]/ps-adminmenu/client/troll.lua
Normal file
48
resources/[ps]/ps-adminmenu/client/troll.lua
Normal file
@ -0,0 +1,48 @@
|
||||
-- Set on fire
|
||||
RegisterNetEvent('ps-adminmenu:client:SetOnFire', function(time)
|
||||
if not time then time = 10 end
|
||||
local timer = time * 1000
|
||||
StartEntityFire(cache.serverId)
|
||||
Wait(timer)
|
||||
StopEntityFire(cache.serverId)
|
||||
end)
|
||||
|
||||
-- Explode player
|
||||
RegisterNetEvent('ps-adminmenu:client:ExplodePlayer', function(damage)
|
||||
local coords = GetEntityCoords(cache.serverId)
|
||||
if damage == nil then damage = "nodamage" end
|
||||
if damage == "nodamage" then
|
||||
AddExplosion(coords.x, coords.y, coords.z, 'EXPLOSION_TANKER', 2.0, true, false, 2.0)
|
||||
else
|
||||
AddExplosion(coords.x, coords.y, coords.z, 2, 0.9, 1, 0, 1065353216, 0)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Play Sound
|
||||
RegisterNetEvent('ps-adminmenu:client:PlaySound', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local player = selectedData["Player"].value
|
||||
local sound = selectedData["Sound"].value
|
||||
|
||||
TriggerServerEvent("InteractSound_SV:PlayOnOne", player, sound, 0.30)
|
||||
end)
|
||||
|
||||
-- Drunk Player
|
||||
RegisterNetEvent('ps-adminmenu:client:InitiateDrunkEffect', function()
|
||||
local playerPed = cache.ped
|
||||
lib.requestAnimSet("MOVE_M@DRUNK@VERYDRUNK")
|
||||
Wait(650)
|
||||
SetPedMotionBlur(playerPed, true)
|
||||
SetPedMovementClipset(playerPed, "MOVE_M@DRUNK@VERYDRUNK", true)
|
||||
SetPedIsDrunk(playerPed, true)
|
||||
ShakeGameplayCam("DRUNK_SHAKE", 2.0)
|
||||
Wait(30000) -- Time To Be Drunk
|
||||
SetPedMoveRateOverride(playerPed, 1.0)
|
||||
SetRunSprintMultiplierForPlayer(playerPed, 1.0)
|
||||
SetPedIsDrunk(playerPed, false)
|
||||
SetPedMotionBlur(playerPed, false)
|
||||
ResetPedMovementClipset(playerPed)
|
||||
ShakeGameplayCam("DRUNK_SHAKE", 0.0)
|
||||
SetTimecycleModifierStrength(0.0)
|
||||
end)
|
53
resources/[ps]/ps-adminmenu/client/utils.lua
Normal file
53
resources/[ps]/ps-adminmenu/client/utils.lua
Normal file
@ -0,0 +1,53 @@
|
||||
--- @param bool boolean
|
||||
function ToggleUI(bool)
|
||||
SetNuiFocus(bool, bool)
|
||||
SendNUIMessage({
|
||||
action = "setVisible",
|
||||
data = bool
|
||||
})
|
||||
end
|
||||
|
||||
--- @param perms table
|
||||
function CheckPerms(perms)
|
||||
return lib.callback.await('ps-adminmenu:callback:CheckPerms', false, perms)
|
||||
end
|
||||
|
||||
function CheckDataFromKey(key)
|
||||
local actions = Config.Actions[key]
|
||||
if actions then
|
||||
local data = nil
|
||||
|
||||
if actions.event then
|
||||
data = actions
|
||||
end
|
||||
|
||||
if actions.dropdown then
|
||||
for _, v in pairs(actions.dropdown) do
|
||||
if v.event then
|
||||
local new = v
|
||||
new.perms = actions.perms
|
||||
data = new
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
local playerActions = Config.PlayerActions[key]
|
||||
if playerActions then
|
||||
return playerActions
|
||||
end
|
||||
|
||||
local otherActions = Config.OtherActions[key]
|
||||
if otherActions then
|
||||
return otherActions
|
||||
end
|
||||
end
|
||||
|
||||
--- @param title string
|
||||
--- @param message string
|
||||
function Log(title, message)
|
||||
TriggerServerEvent("qb-log:server:CreateLog", "ps-adminmenu", title, "red", message)
|
||||
end
|
208
resources/[ps]/ps-adminmenu/client/vehicles.lua
Normal file
208
resources/[ps]/ps-adminmenu/client/vehicles.lua
Normal file
@ -0,0 +1,208 @@
|
||||
local function GetVehicleName(hash)
|
||||
for _, v in pairs(QBCore.Shared.Vehicles) do
|
||||
if hash == v.hash then
|
||||
return v.model
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Own Vehicle
|
||||
RegisterNetEvent('ps-adminmenu:client:Admincar', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
if not cache.vehicle then return end
|
||||
|
||||
local props = lib.getVehicleProperties(cache.vehicle)
|
||||
local name = GetVehicleName(props.model)
|
||||
local sharedVehicles = QBCore.Shared.Vehicles[name]
|
||||
local hash = GetHashKey(cache.vehicle)
|
||||
|
||||
if sharedVehicles then
|
||||
TriggerServerEvent('ps-adminmenu:server:SaveCar', props, sharedVehicles, hash, props.plate)
|
||||
else
|
||||
QBCore.Functions.Notify(locale("cannot_store_veh"), 'error')
|
||||
end
|
||||
end)
|
||||
|
||||
-- Spawn Vehicle
|
||||
RegisterNetEvent('ps-adminmenu:client:SpawnVehicle', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local selectedVehicle = selectedData["Vehicle"].value
|
||||
local hash = GetHashKey(selectedVehicle)
|
||||
|
||||
if not IsModelValid(hash) then return end
|
||||
|
||||
lib.requestModel(hash)
|
||||
|
||||
if cache.vehicle then
|
||||
DeleteVehicle(cache.vehicle)
|
||||
end
|
||||
|
||||
local vehicle = CreateVehicle(hash, GetEntityCoords(cache.ped), GetEntityHeading(cache.ped), true, false)
|
||||
TaskWarpPedIntoVehicle(cache.ped, vehicle, -1)
|
||||
exports[Config.Fuel]:SetFuel(vehicle, 100.0)
|
||||
TriggerEvent("vehiclekeys:client:SetOwner", QBCore.Functions.GetPlate(vehicle))
|
||||
end)
|
||||
|
||||
-- Refuel Vehicle
|
||||
RegisterNetEvent('ps-adminmenu:client:RefuelVehicle', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
if cache.vehicle then
|
||||
exports[Config.Fuel]:SetFuel(cache.vehicle, 100.0)
|
||||
QBCore.Functions.Notify(locale("refueled_vehicle"), 'success')
|
||||
else
|
||||
QBCore.Functions.Notify(locale("not_in_vehicle"), 'error')
|
||||
end
|
||||
end)
|
||||
|
||||
-- Change plate
|
||||
RegisterNetEvent('ps-adminmenu:client:ChangePlate', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local plate = selectedData["Plate"].value
|
||||
|
||||
if string.len(plate) > 8 then
|
||||
return QBCore.Functions.Notify(locale("plate_max"), "error", 5000)
|
||||
end
|
||||
|
||||
if cache.vehicle then
|
||||
local AlreadyPlate = lib.callback.await("ps-adminmenu:callback:CheckAlreadyPlate", false, plate)
|
||||
|
||||
if AlreadyPlate then
|
||||
QBCore.Functions.Notify(locale("already_plate"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
local currentPlate = GetVehicleNumberPlateText(cache.vehicle)
|
||||
TriggerServerEvent('ps-adminmenu:server:ChangePlate', plate, currentPlate)
|
||||
Wait(100)
|
||||
SetVehicleNumberPlateText(cache.vehicle, plate)
|
||||
Wait(100)
|
||||
TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', QBCore.Functions.GetPlate(cache.vehicle))
|
||||
else
|
||||
QBCore.Functions.Notify(locale("not_in_vehicle"), 'error')
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
-- Toggle Vehicle Dev mode
|
||||
local VEHICLE_DEV_MODE = false
|
||||
local function UpdateVehicleMenu()
|
||||
while VEHICLE_DEV_MODE do
|
||||
Wait(1000)
|
||||
|
||||
local vehicle = lib.getVehicleProperties(cache.vehicle)
|
||||
local name = GetVehicleName(vehicle.model)
|
||||
local netID = VehToNet(cache.vehicle)
|
||||
|
||||
SendNUIMessage({
|
||||
action = "showVehicleMenu",
|
||||
data = {
|
||||
show = VEHICLE_DEV_MODE,
|
||||
name = name,
|
||||
model = vehicle.model,
|
||||
netID = netID,
|
||||
engine_health = vehicle.engineHealth,
|
||||
body_health = vehicle.bodyHealth,
|
||||
plate = vehicle.plate,
|
||||
fuel = vehicle.fuelLevel,
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:client:ToggleVehDevMenu', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
if not cache.vehicle then return end
|
||||
|
||||
VEHICLE_DEV_MODE = not VEHICLE_DEV_MODE
|
||||
|
||||
if VEHICLE_DEV_MODE then
|
||||
CreateThread(UpdateVehicleMenu)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Max Mods
|
||||
local PERFORMANCE_MOD_INDICES = { 11, 12, 13, 15, 16 }
|
||||
local function UpgradePerformance(vehicle)
|
||||
SetVehicleModKit(vehicle, 0)
|
||||
ToggleVehicleMod(vehicle, 18, true)
|
||||
SetVehicleFixed(vehicle)
|
||||
|
||||
for _, modType in ipairs(PERFORMANCE_MOD_INDICES) do
|
||||
local maxMod = GetNumVehicleMods(vehicle, modType) - 1
|
||||
SetVehicleMod(vehicle, modType, maxMod, customWheels)
|
||||
end
|
||||
|
||||
QBCore.Functions.Notify(locale("vehicle_max_modded"), 'success', 7500)
|
||||
end
|
||||
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:client:maxmodVehicle', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
if cache.vehicle then
|
||||
UpgradePerformance(cache.vehicle)
|
||||
else
|
||||
QBCore.Functions.Notify(locale("vehicle_not_driver"), 'error', 7500)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Spawn Personal vehicles
|
||||
|
||||
RegisterNetEvent("ps-adminmenu:client:SpawnPersonalVehicle", function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local plate = selectedData['VehiclePlate'].value
|
||||
local ped = PlayerPedId()
|
||||
local coords = QBCore.Functions.GetCoords(ped)
|
||||
local cid = QBCore.Functions.GetPlayerData().citizenid
|
||||
|
||||
lib.callback('ps-adminmenu:server:GetVehicleByPlate', false, function(vehModel)
|
||||
vehicle = vehModel
|
||||
end, plate)
|
||||
|
||||
Wait(100)
|
||||
QBCore.Functions.TriggerCallback('QBCore:Server:SpawnVehicle', function(vehicle)
|
||||
local veh = NetToVeh(vehicle)
|
||||
local props = QBCore.Functions.GetVehicleProperties(veh)
|
||||
SetEntityHeading(veh, coords.w)
|
||||
TaskWarpPedIntoVehicle(ped, veh, -1)
|
||||
SetVehicleModKit(veh, 0)
|
||||
Wait(100)
|
||||
QBCore.Functions.SetVehicleProperties(veh, props)
|
||||
SetVehicleNumberPlateText(veh, plate)
|
||||
exports[Config.Fuel]:SetFuel(veh, 100.0)
|
||||
TriggerEvent("vehiclekeys:client:SetOwner", plate)
|
||||
TriggerEvent('iens:repaira', ped)
|
||||
TriggerEvent('vehiclemod:client:fixEverything', ped)
|
||||
end, vehicle, coords, true)
|
||||
end)
|
||||
|
||||
|
||||
-- Get Vehicle Data
|
||||
lib.callback.register("ps-adminmenu:client:getvehData", function(vehicle)
|
||||
lib.requestModel(vehicle)
|
||||
|
||||
local coords = vec(GetOffsetFromEntityInWorldCoords(cache.ped, 0.0, 2.0, 0.5), GetEntityHeading(cache.ped) + 90)
|
||||
local veh = CreateVehicle(vehicle, coords, false, false)
|
||||
|
||||
local prop = {}
|
||||
if DoesEntityExist(veh) then
|
||||
SetEntityCollision(veh, false, false)
|
||||
FreezeEntityPosition(veh, true)
|
||||
prop = QBCore.Functions.GetVehicleProperties(veh)
|
||||
Wait(500)
|
||||
DeleteVehicle(veh)
|
||||
end
|
||||
|
||||
return prop
|
||||
end)
|
61
resources/[ps]/ps-adminmenu/client/world.lua
Normal file
61
resources/[ps]/ps-adminmenu/client/world.lua
Normal file
@ -0,0 +1,61 @@
|
||||
-- Changes the time
|
||||
RegisterNetEvent('ps-adminmenu:client:ChangeTime', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local time = selectedData["Time Events"].value
|
||||
|
||||
if not time then return end
|
||||
|
||||
TriggerServerEvent('qb-weathersync:server:setTime', time, 00)
|
||||
end)
|
||||
|
||||
-- Changes the weather
|
||||
RegisterNetEvent('ps-adminmenu:client:ChangeWeather', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local weather = selectedData["Weather"].value
|
||||
|
||||
TriggerServerEvent('qb-weathersync:server:setWeather', weather)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:client:copyToClipboard', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local dropdown = selectedData["Copy Coords"].value
|
||||
local ped = PlayerPedId()
|
||||
local string = nil
|
||||
if dropdown == 'vector2' then
|
||||
local coords = GetEntityCoords(ped)
|
||||
local x = QBCore.Shared.Round(coords.x, 2)
|
||||
local y = QBCore.Shared.Round(coords.y, 2)
|
||||
string = "vector2(".. x ..", ".. y ..")"
|
||||
QBCore.Functions.Notify(locale("copy_vector2"), 'success')
|
||||
elseif dropdown == 'vector3' then
|
||||
local coords = GetEntityCoords(ped)
|
||||
local x = QBCore.Shared.Round(coords.x, 2)
|
||||
local y = QBCore.Shared.Round(coords.y, 2)
|
||||
local z = QBCore.Shared.Round(coords.z, 2)
|
||||
string = "vector3(".. x ..", ".. y ..", ".. z ..")"
|
||||
QBCore.Functions.Notify(locale("copy_vector3"), 'success')
|
||||
elseif dropdown == 'vector4' then
|
||||
local coords = GetEntityCoords(ped)
|
||||
local x = QBCore.Shared.Round(coords.x, 2)
|
||||
local y = QBCore.Shared.Round(coords.y, 2)
|
||||
local z = QBCore.Shared.Round(coords.z, 2)
|
||||
local heading = GetEntityHeading(ped)
|
||||
local h = QBCore.Shared.Round(heading, 2)
|
||||
string = "vector4(".. x ..", ".. y ..", ".. z ..", ".. h ..")"
|
||||
QBCore.Functions.Notify(locale("copy_vector4"), 'success')
|
||||
elseif dropdown == 'heading' then
|
||||
local heading = GetEntityHeading(ped)
|
||||
local h = QBCore.Shared.Round(heading, 2)
|
||||
string = h
|
||||
QBCore.Functions.Notify(locale("copy_heading"), 'success')
|
||||
elseif string == nil then
|
||||
QBCore.Functions.Notify(locale("empty_input"), 'error')
|
||||
end
|
||||
|
||||
lib.setClipboard(string)
|
||||
|
||||
end)
|
20199
resources/[ps]/ps-adminmenu/data/object.lua
Normal file
20199
resources/[ps]/ps-adminmenu/data/object.lua
Normal file
File diff suppressed because it is too large
Load Diff
500
resources/[ps]/ps-adminmenu/data/ped.lua
Normal file
500
resources/[ps]/ps-adminmenu/data/ped.lua
Normal file
@ -0,0 +1,500 @@
|
||||
return {
|
||||
--- female
|
||||
{ label = "a_f_m_beach_01", value = "female"},
|
||||
{ label = "a_f_m_bevhills_01", value = "female"},
|
||||
{ label = "a_f_m_bevhills_02", value = "female"},
|
||||
{ label = "a_f_m_bodybuild_01", value = "female"},
|
||||
{ label = "a_f_m_business_02", value = "female"},
|
||||
{ label = "a_f_m_downtown_01", value = "female"},
|
||||
{ label = "a_f_m_eastsa_01", value = "female"},
|
||||
{ label = "a_f_m_eastsa_02", value = "female"},
|
||||
{ label = "a_f_m_fatbla_01", value = "female"},
|
||||
{ label = "a_f_m_fatcult_01", value = "female"},
|
||||
{ label = "a_f_m_fatwhite_01", value = "female"},
|
||||
{ label = "a_f_m_ktown_01", value = "female"},
|
||||
{ label = "a_f_m_ktown_02", value = "female"},
|
||||
{ label = "a_f_m_prolhost_01", value = "female"},
|
||||
{ label = "a_f_m_salton_01", value = "female"},
|
||||
{ label = "a_f_m_skidrow_01", value = "female"},
|
||||
{ label = "a_f_m_soucentmc_01", value = "female"},
|
||||
{ label = "a_f_m_soucent_01", value = "female"},
|
||||
{ label = "a_f_m_soucent_02", value = "female"},
|
||||
{ label = "a_f_m_tourist_01", value = "female"},
|
||||
{ label = "a_f_m_trampbeac_01", value = "female"},
|
||||
{ label = "a_f_m_tramp_01", value = "female"},
|
||||
{ label = "a_f_o_genstreet_01", value = "female"},
|
||||
{ label = "a_f_o_indian_01", value = "female"},
|
||||
{ label = "a_f_o_ktown_01", value = "female"},
|
||||
{ label = "a_f_o_salton_01", value = "female"},
|
||||
{ label = "a_f_o_soucent_01", value = "female"},
|
||||
{ label = "a_f_o_soucent_02", value = "female"},
|
||||
{ label = "a_f_y_beach_01", value = "female"},
|
||||
{ label = "a_f_y_bevhills_01", value = "female"},
|
||||
{ label = "a_f_y_bevhills_02", value = "female"},
|
||||
{ label = "a_f_y_bevhills_03", value = "female"},
|
||||
{ label = "a_f_y_bevhills_04", value = "female"},
|
||||
{ label = "a_f_y_business_01", value = "female"},
|
||||
{ label = "a_f_y_business_02", value = "female"},
|
||||
{ label = "a_f_y_business_03", value = "female"},
|
||||
{ label = "a_f_y_business_04", value = "female"},
|
||||
{ label = "a_f_y_eastsa_01", value = "female"},
|
||||
{ label = "a_f_y_eastsa_02", value = "female"},
|
||||
{ label = "a_f_y_eastsa_03", value = "female"},
|
||||
{ label = "a_f_y_epsilon_01", value = "female"},
|
||||
{ label = "a_f_y_fitness_01", value = "female"},
|
||||
{ label = "a_f_y_fitness_02", value = "female"},
|
||||
{ label = "a_f_y_genhot_01", value = "female"},
|
||||
{ label = "a_f_y_golfer_01", value = "female"},
|
||||
{ label = "a_f_y_hiker_01", value = "female"},
|
||||
{ label = "a_f_y_hipster_01", value = "female"},
|
||||
{ label = "a_f_y_hipster_02", value = "female"},
|
||||
{ label = "a_f_y_hipster_03", value = "female"},
|
||||
{ label = "a_f_y_hipster_04", value = "female"},
|
||||
{ label = "a_f_y_indian_01", value = "female"},
|
||||
{ label = "a_f_y_juggalo_01", value = "female"},
|
||||
{ label = "a_f_y_runner_01", value = "female"},
|
||||
{ label = "a_f_y_rurmeth_01", value = "female"},
|
||||
{ label = "a_f_y_scdressy_01", value = "female"},
|
||||
{ label = "a_f_y_skater_01", value = "female"},
|
||||
{ label = "a_f_y_soucent_01", value = "female"},
|
||||
{ label = "a_f_y_soucent_02", value = "female"},
|
||||
{ label = "a_f_y_soucent_03", value = "female"},
|
||||
{ label = "a_f_y_tennis_01", value = "female"},
|
||||
{ label = "a_f_y_tourist_01", value = "female"},
|
||||
{ label = "a_f_y_tourist_02", value = "female"},
|
||||
{ label = "a_f_y_vinewood_01", value = "female"},
|
||||
{ label = "a_f_y_vinewood_02", value = "female"},
|
||||
{ label = "a_f_y_vinewood_03", value = "female"},
|
||||
{ label = "a_f_y_vinewood_04", value = "female"},
|
||||
{ label = "a_f_y_yoga_01", value = "female"},
|
||||
{ label = "g_f_y_ballas_01", value = "female"},
|
||||
{ label = "g_f_y_families_01", value = "female"},
|
||||
{ label = "g_f_y_lost_01", value = "female"},
|
||||
{ label = "g_f_y_vagos_01", value = "female"},
|
||||
{ label = "mp_f_deadhooker", value = "female"},
|
||||
{ label = "mp_f_freemode_01", value = "female"},
|
||||
{ label = "mp_f_misty_01", value = "female"},
|
||||
--{ label = "mp_f_stripperlite", value = "female"},
|
||||
{ label = "mp_s_m_armoured_01", value = "female"},
|
||||
{ label = "s_f_m_fembarber", value = "female"},
|
||||
{ label = "s_f_m_maid_01", value = "female"},
|
||||
{ label = "s_f_m_shop_high", value = "female"},
|
||||
{ label = "s_f_m_sweatshop_01", value = "female"},
|
||||
{ label = "s_f_y_airhostess_01", value = "female"},
|
||||
{ label = "s_f_y_bartender_01", value = "female"},
|
||||
{ label = "s_f_y_baywatch_01", value = "female"},
|
||||
{ label = "s_f_y_cop_01", value = "female"},
|
||||
{ label = "s_f_y_factory_01", value = "female"},
|
||||
{ label = "s_f_y_hooker_01", value = "female"},
|
||||
{ label = "s_f_y_hooker_02", value = "female"},
|
||||
{ label = "s_f_y_hooker_03", value = "female"},
|
||||
{ label = "s_f_y_migrant_01", value = "female"},
|
||||
{ label = "s_f_y_movprem_01", value = "female"},
|
||||
{ label = "ig_kerrymcintosh", value = "female"},
|
||||
{ label = "ig_janet", value = "female"},
|
||||
{ label = "ig_jewelass", value = "female"},
|
||||
{ label = "ig_magenta", value = "female"},
|
||||
{ label = "ig_marnie", value = "female"},
|
||||
{ label = "ig_patricia", value = "female"},
|
||||
{ label = "ig_screen_writer", value = "female"},
|
||||
{ label = "ig_tanisha", value = "female"},
|
||||
{ label = "ig_tonya", value = "female"},
|
||||
{ label = "ig_tracydisanto", value = "female"},
|
||||
{ label = "u_f_m_corpse_01", value = "female"},
|
||||
{ label = "u_f_m_miranda", value = "female"},
|
||||
{ label = "u_f_m_promourn_01", value = "female"},
|
||||
{ label = "u_f_o_moviestar", value = "female"},
|
||||
{ label = "u_f_o_prolhost_01", value = "female"},
|
||||
{ label = "u_f_y_bikerchic", value = "female"},
|
||||
{ label = "u_f_y_comjane", value = "female"},
|
||||
{ label = "u_f_y_corpse_01", value = "female"},
|
||||
{ label = "u_f_y_corpse_02", value = "female"},
|
||||
{ label = "u_f_y_hotposh_01", value = "female"},
|
||||
{ label = "u_f_y_jewelass_01", value = "female"},
|
||||
{ label = "u_f_y_mistress", value = "female"},
|
||||
{ label = "u_f_y_poppymich", value = "female"},
|
||||
{ label = "u_f_y_princess", value = "female"},
|
||||
{ label = "u_f_y_spyactress", value = "female"},
|
||||
{ label = "ig_amandatownley", value = "female"},
|
||||
{ label = "ig_ashley", value = "female"},
|
||||
{ label = "ig_andreas", value = "female"},
|
||||
{ label = "ig_ballasog", value = "female"},
|
||||
{ label = "ig_maryannn", value = "female"},
|
||||
{ label = "ig_maude", value = "female"},
|
||||
{ label = "ig_michelle", value = "female"},
|
||||
{ label = "ig_mrs_thornhill", value = "female"},
|
||||
{ label = "ig_natalia", value = "female"},
|
||||
{ label = "s_f_y_scrubs_01", value = "female"},
|
||||
{ label = "s_f_y_sheriff_01", value = "female"},
|
||||
{ label = "s_f_y_shop_low", value = "female"},
|
||||
{ label = "s_f_y_shop_mid", value = "female"},
|
||||
{ label = "s_f_y_stripperlite", value = "female"},
|
||||
{ label = "s_f_y_stripper_01", value = "female"},
|
||||
{ label = "s_f_y_stripper_02", value = "female"},
|
||||
{ label = "ig_mrsphillips", value = "female"},
|
||||
{ label = "ig_mrs_thornhill", value = "female"},
|
||||
{ label = "ig_molly", value = "female"},
|
||||
{ label = "ig_natalia", value = "female"},
|
||||
{ label = "s_f_y_sweatshop_01", value = "female"},
|
||||
{ label = "ig_paige", value = "female"},
|
||||
{ label = "a_f_y_femaleagent", value = "female"},
|
||||
{ label = "a_f_y_hippie_01", value = "female"},
|
||||
|
||||
--- male
|
||||
{ label = "ig_trafficwarden", value = "male"},
|
||||
{ label = "ig_bankman", value = "male"},
|
||||
{ label = "ig_barry", value = "male"},
|
||||
{ label = "ig_bestmen", value = "male"},
|
||||
{ label = "ig_beverly", value = "male"},
|
||||
{ label = "ig_car3guy1", value = "male"},
|
||||
{ label = "ig_car3guy2", value = "male"},
|
||||
{ label = "ig_casey", value = "male"},
|
||||
{ label = "ig_chef", value = "male"},
|
||||
{ label = "ig_chengsr", value = "male"},
|
||||
{ label = "ig_chrisformage", value = "male"},
|
||||
{ label = "ig_clay", value = "male"},
|
||||
{ label = "ig_claypain", value = "male"},
|
||||
{ label = "ig_cletus", value = "male"},
|
||||
{ label = "ig_dale", value = "male"},
|
||||
{ label = "ig_dreyfuss", value = "male"},
|
||||
{ label = "ig_fbisuit_01", value = "male"},
|
||||
{ label = "ig_floyd", value = "male"},
|
||||
{ label = "ig_groom", value = "male"},
|
||||
{ label = "ig_hao", value = "male"},
|
||||
{ label = "ig_hunter", value = "male"},
|
||||
{ label = "csb_prolsec", value = "male"},
|
||||
{ label = "ig_jimmydisanto", value = "male"},
|
||||
{ label = "ig_joeminuteman", value = "male"},
|
||||
{ label = "ig_josef", value = "male"},
|
||||
{ label = "ig_josh", value = "male"},
|
||||
{ label = "ig_lamardavis", value = "male"},
|
||||
{ label = "ig_lazlow", value = "male"},
|
||||
{ label = "ig_lestercrest", value = "male"},
|
||||
{ label = "ig_lifeinvad_01", value = "male"},
|
||||
{ label = "ig_lifeinvad_02", value = "male"},
|
||||
{ label = "ig_manuel", value = "male"},
|
||||
{ label = "ig_milton", value = "male"},
|
||||
{ label = "ig_mrk", value = "male"},
|
||||
{ label = "ig_nervousron", value = "male"},
|
||||
{ label = "ig_nigel", value = "male"},
|
||||
{ label = "ig_old_man1a", value = "male"},
|
||||
{ label = "ig_old_man2", value = "male"},
|
||||
{ label = "ig_oneil", value = "male"},
|
||||
-- { label = "ig_orleans", value = "male"},
|
||||
{ label = "ig_ortega", value = "male"},
|
||||
{ label = "ig_paper", value = "male"},
|
||||
{ label = "ig_priest", value = "male"},
|
||||
{ label = "ig_prolsec_02", value = "male"},
|
||||
{ label = "ig_ramp_gang", value = "male"},
|
||||
{ label = "ig_ramp_hic", value = "male"},
|
||||
{ label = "ig_ramp_hipster", value = "male"},
|
||||
{ label = "ig_ramp_mex", value = "male"},
|
||||
{ label = "ig_roccopelosi", value = "male"},
|
||||
{ label = "ig_russiandrunk", value = "male"},
|
||||
{ label = "ig_siemonyetarian", value = "male"},
|
||||
{ label = "ig_solomon", value = "male"},
|
||||
{ label = "ig_stevehains", value = "male"},
|
||||
{ label = "ig_stretch", value = "male"},
|
||||
{ label = "ig_talina", value = "male"},
|
||||
{ label = "ig_taocheng", value = "male"},
|
||||
{ label = "ig_taostranslator", value = "male"},
|
||||
{ label = "ig_tenniscoach", value = "male"},
|
||||
{ label = "ig_terry", value = "male"},
|
||||
{ label = "ig_tomepsilon", value = "male"},
|
||||
{ label = "ig_tylerdix", value = "male"},
|
||||
{ label = "ig_wade", value = "male"},
|
||||
{ label = "ig_zimbor", value = "male"},
|
||||
{ label = "s_m_m_paramedic_01", value = "male"},
|
||||
{ label = "a_m_m_afriamer_01", value = "male"},
|
||||
{ label = "a_m_m_beach_01", value = "male"},
|
||||
{ label = "a_m_m_beach_02", value = "male"},
|
||||
{ label = "a_m_m_bevhills_01", value = "male"},
|
||||
{ label = "a_m_m_bevhills_02", value = "male"},
|
||||
{ label = "a_m_m_business_01", value = "male"},
|
||||
{ label = "a_m_m_eastsa_01", value = "male"},
|
||||
{ label = "a_m_m_eastsa_02", value = "male"},
|
||||
{ label = "a_m_m_farmer_01", value = "male"},
|
||||
{ label = "a_m_m_fatlatin_01", value = "male"},
|
||||
{ label = "a_m_m_genfat_01", value = "male"},
|
||||
{ label = "a_m_m_genfat_02", value = "male"},
|
||||
{ label = "a_m_m_golfer_01", value = "male"},
|
||||
{ label = "a_m_m_hasjew_01", value = "male"},
|
||||
{ label = "a_m_m_hillbilly_01", value = "male"},
|
||||
{ label = "a_m_m_hillbilly_02", value = "male"},
|
||||
{ label = "a_m_m_indian_01", value = "male"},
|
||||
{ label = "a_m_m_ktown_01", value = "male"},
|
||||
{ label = "a_m_m_malibu_01", value = "male"},
|
||||
{ label = "a_m_m_mexcntry_01", value = "male"},
|
||||
{ label = "a_m_m_mexlabor_01", value = "male"},
|
||||
{ label = "a_m_m_og_boss_01", value = "male"},
|
||||
{ label = "a_m_m_paparazzi_01", value = "male"},
|
||||
{ label = "a_m_m_polynesian_01", value = "male"},
|
||||
{ label = "a_m_m_prolhost_01", value = "male"},
|
||||
{ label = "a_m_m_rurmeth_01", value = "male"},
|
||||
{ label = "a_m_m_salton_01", value = "male"},
|
||||
{ label = "a_m_m_salton_02", value = "male"},
|
||||
{ label = "a_m_m_salton_03", value = "male"},
|
||||
{ label = "a_m_m_salton_04", value = "male"},
|
||||
{ label = "a_m_m_skater_01", value = "male"},
|
||||
{ label = "a_m_m_skidrow_01", value = "male"},
|
||||
{ label = "a_m_m_socenlat_01", value = "male"},
|
||||
{ label = "a_m_m_soucent_01", value = "male"},
|
||||
{ label = "a_m_m_soucent_02", value = "male"},
|
||||
{ label = "a_m_m_soucent_03", value = "male"},
|
||||
{ label = "a_m_m_soucent_04", value = "male"},
|
||||
{ label = "a_m_m_stlat_02", value = "male"},
|
||||
{ label = "a_m_m_tennis_01", value = "male"},
|
||||
{ label = "a_m_m_tourist_01", value = "male"},
|
||||
{ label = "a_m_m_trampbeac_01", value = "male"},
|
||||
{ label = "a_m_m_tramp_01", value = "male"},
|
||||
{ label = "a_m_m_tranvest_01", value = "male"},
|
||||
{ label = "a_m_m_tranvest_02", value = "male"},
|
||||
{ label = "a_m_o_beach_01", value = "male"},
|
||||
{ label = "a_m_o_genstreet_01", value = "male"},
|
||||
{ label = "a_m_o_ktown_01", value = "male"},
|
||||
{ label = "a_m_o_salton_01", value = "male"},
|
||||
{ label = "a_m_o_soucent_01", value = "male"},
|
||||
{ label = "a_m_o_soucent_02", value = "male"},
|
||||
{ label = "a_m_o_soucent_03", value = "male"},
|
||||
{ label = "a_m_o_tramp_01", value = "male"},
|
||||
{ label = "a_m_y_beachvesp_01", value = "male"},
|
||||
{ label = "a_m_y_beachvesp_02", value = "male"},
|
||||
{ label = "a_m_y_beach_01", value = "male"},
|
||||
{ label = "a_m_y_beach_02", value = "male"},
|
||||
{ label = "a_m_y_beach_03", value = "male"},
|
||||
{ label = "a_m_y_bevhills_01", value = "male"},
|
||||
{ label = "a_m_y_bevhills_02", value = "male"},
|
||||
{ label = "a_m_y_breakdance_01", value = "male"},
|
||||
{ label = "a_m_y_busicas_01", value = "male"},
|
||||
{ label = "a_m_y_business_01", value = "male"},
|
||||
{ label = "a_m_y_business_02", value = "male"},
|
||||
{ label = "a_m_y_business_03", value = "male"},
|
||||
{ label = "a_m_y_cyclist_01", value = "male"},
|
||||
{ label = "a_m_y_dhill_01", value = "male"},
|
||||
{ label = "a_m_y_downtown_01", value = "male"},
|
||||
{ label = "a_m_y_eastsa_01", value = "male"},
|
||||
{ label = "a_m_y_eastsa_02", value = "male"},
|
||||
{ label = "a_m_y_epsilon_01", value = "male"},
|
||||
{ label = "a_m_y_epsilon_02", value = "male"},
|
||||
{ label = "a_m_y_gay_01", value = "male"},
|
||||
{ label = "a_m_y_gay_02", value = "male"},
|
||||
{ label = "a_m_y_genstreet_01", value = "male"},
|
||||
{ label = "a_m_y_genstreet_02", value = "male"},
|
||||
{ label = "a_m_y_golfer_01", value = "male"},
|
||||
{ label = "a_m_y_hasjew_01", value = "male"},
|
||||
{ label = "a_m_y_hiker_01", value = "male"},
|
||||
{ label = "a_m_y_hipster_01", value = "male"},
|
||||
{ label = "a_m_y_hipster_02", value = "male"},
|
||||
{ label = "a_m_y_hipster_03", value = "male"},
|
||||
{ label = "a_m_y_indian_01", value = "male"},
|
||||
{ label = "a_m_y_jetski_01", value = "male"},
|
||||
{ label = "a_m_y_juggalo_01", value = "male"},
|
||||
{ label = "a_m_y_ktown_01", value = "male"},
|
||||
{ label = "a_m_y_ktown_02", value = "male"},
|
||||
{ label = "a_m_y_latino_01", value = "male"},
|
||||
{ label = "a_m_y_methhead_01", value = "male"},
|
||||
{ label = "a_m_y_mexthug_01", value = "male"},
|
||||
{ label = "a_m_y_motox_01", value = "male"},
|
||||
{ label = "a_m_y_motox_02", value = "male"},
|
||||
{ label = "a_m_y_musclbeac_01", value = "male"},
|
||||
{ label = "a_m_y_musclbeac_02", value = "male"},
|
||||
{ label = "a_m_y_polynesian_01", value = "male"},
|
||||
{ label = "a_m_y_roadcyc_01", value = "male"},
|
||||
{ label = "a_m_y_runner_01", value = "male"},
|
||||
{ label = "a_m_y_runner_02", value = "male"},
|
||||
{ label = "a_m_y_salton_01", value = "male"},
|
||||
{ label = "a_m_y_skater_01", value = "male"},
|
||||
{ label = "a_m_y_skater_02", value = "male"},
|
||||
{ label = "a_m_y_soucent_01", value = "male"},
|
||||
{ label = "a_m_y_soucent_02", value = "male"},
|
||||
{ label = "a_m_y_soucent_03", value = "male"},
|
||||
{ label = "a_m_y_soucent_04", value = "male"},
|
||||
{ label = "a_m_y_stbla_01", value = "male"},
|
||||
{ label = "a_m_y_stbla_02", value = "male"},
|
||||
{ label = "a_m_y_stlat_01", value = "male"},
|
||||
{ label = "a_m_y_stwhi_01", value = "male"},
|
||||
{ label = "a_m_y_stwhi_02", value = "male"},
|
||||
{ label = "a_m_y_sunbathe_01", value = "male"},
|
||||
{ label = "a_m_y_surfer_01", value = "male"},
|
||||
{ label = "a_m_y_vindouche_01", value = "male"},
|
||||
{ label = "a_m_y_vinewood_01", value = "male"},
|
||||
{ label = "a_m_y_vinewood_02", value = "male"},
|
||||
{ label = "a_m_y_vinewood_03", value = "male"},
|
||||
{ label = "a_m_y_vinewood_04", value = "male"},
|
||||
{ label = "a_m_y_yoga_01", value = "male"},
|
||||
{ label = "g_m_m_armboss_01", value = "male"},
|
||||
{ label = "g_m_m_armgoon_01", value = "male"},
|
||||
{ label = "g_m_m_armlieut_01", value = "male"},
|
||||
{ label = "g_m_m_chemwork_01", value = "male"},
|
||||
{ label = "g_m_m_chiboss_01", value = "male"},
|
||||
{ label = "g_m_m_chicold_01", value = "male"},
|
||||
{ label = "g_m_m_chigoon_01", value = "male"},
|
||||
{ label = "g_m_m_chigoon_02", value = "male"},
|
||||
{ label = "g_m_m_korboss_01", value = "male"},
|
||||
{ label = "g_m_m_mexboss_01", value = "male"},
|
||||
{ label = "g_m_m_mexboss_02", value = "male"},
|
||||
{ label = "g_m_y_armgoon_02", value = "male"},
|
||||
{ label = "g_m_y_azteca_01", value = "male"},
|
||||
{ label = "g_m_y_ballaeast_01", value = "male"},
|
||||
{ label = "g_m_y_ballaorig_01", value = "male"},
|
||||
{ label = "g_m_y_ballasout_01", value = "male"},
|
||||
{ label = "g_m_y_famca_01", value = "male"},
|
||||
{ label = "g_m_y_famdnf_01", value = "male"},
|
||||
{ label = "g_m_y_famfor_01", value = "male"},
|
||||
{ label = "g_m_y_korean_01", value = "male"},
|
||||
{ label = "g_m_y_korean_02", value = "male"},
|
||||
{ label = "g_m_y_korlieut_01", value = "male"},
|
||||
{ label = "g_m_y_lost_01", value = "male"},
|
||||
{ label = "g_m_y_lost_02", value = "male"},
|
||||
{ label = "g_m_y_lost_03", value = "male"},
|
||||
{ label = "g_m_y_mexgang_01", value = "male"},
|
||||
{ label = "g_m_y_mexgoon_01", value = "male"},
|
||||
{ label = "g_m_y_mexgoon_02", value = "male"},
|
||||
{ label = "g_m_y_mexgoon_03", value = "male"},
|
||||
{ label = "g_m_y_pologoon_01", value = "male"},
|
||||
{ label = "g_m_y_pologoon_02", value = "male"},
|
||||
{ label = "g_m_y_salvaboss_01", value = "male"},
|
||||
{ label = "g_m_y_salvagoon_01", value = "male"},
|
||||
{ label = "g_m_y_salvagoon_02", value = "male"},
|
||||
{ label = "g_m_y_salvagoon_03", value = "male"},
|
||||
{ label = "g_m_y_strpunk_01", value = "male"},
|
||||
{ label = "g_m_y_strpunk_02", value = "male"},
|
||||
{ label = "mp_m_claude_01", value = "male"},
|
||||
{ label = "mp_m_exarmy_01", value = "male"},
|
||||
{ label = "mp_m_shopkeep_01", value = "male"},
|
||||
{ label = "s_m_m_ammucountry", value = "male"},
|
||||
{ label = "s_m_m_autoshop_01", value = "male"},
|
||||
{ label = "s_m_m_autoshop_02", value = "male"},
|
||||
{ label = "s_m_m_bouncer_01", value = "male"},
|
||||
{ label = "s_m_m_chemsec_01", value = "male"},
|
||||
{ label = "s_m_m_cntrybar_01", value = "male"},
|
||||
{ label = "s_m_m_dockwork_01", value = "male"},
|
||||
{ label = "s_m_m_doctor_01", value = "male"},
|
||||
{ label = "s_m_m_fiboffice_01", value = "male"},
|
||||
{ label = "s_m_m_fiboffice_02", value = "male"},
|
||||
{ label = "s_m_m_gaffer_01", value = "male"},
|
||||
{ label = "s_m_m_gardener_01", value = "male"},
|
||||
{ label = "s_m_m_gentransport", value = "male"},
|
||||
{ label = "s_m_m_hairdress_01", value = "male"},
|
||||
{ label = "s_m_m_highsec_01", value = "male"},
|
||||
{ label = "s_m_m_highsec_02", value = "male"},
|
||||
{ label = "s_m_m_janitor", value = "male"},
|
||||
{ label = "s_m_m_lathandy_01", value = "male"},
|
||||
{ label = "s_m_m_lifeinvad_01", value = "male"},
|
||||
{ label = "s_m_m_linecook", value = "male"},
|
||||
{ label = "s_m_m_lsmetro_01", value = "male"},
|
||||
{ label = "s_m_m_mariachi_01", value = "male"},
|
||||
{ label = "s_m_m_marine_01", value = "male"},
|
||||
{ label = "s_m_m_marine_02", value = "male"},
|
||||
{ label = "s_m_m_migrant_01", value = "male"},
|
||||
{ label = "s_m_m_movalien_01", value = "male"},
|
||||
{ label = "s_m_m_movprem_01", value = "male"},
|
||||
{ label = "s_m_m_movspace_01", value = "male"},
|
||||
{ label = "s_m_m_pilot_01", value = "male"},
|
||||
{ label = "s_m_m_pilot_02", value = "male"},
|
||||
{ label = "s_m_m_postal_01", value = "male"},
|
||||
{ label = "s_m_m_postal_02", value = "male"},
|
||||
{ label = "s_m_m_scientist_01", value = "male"},
|
||||
{ label = "s_m_m_security_01", value = "male"},
|
||||
{ label = "s_m_m_strperf_01", value = "male"},
|
||||
{ label = "s_m_m_strpreach_01", value = "male"},
|
||||
{ label = "s_m_m_strvend_01", value = "male"},
|
||||
{ label = "s_m_m_trucker_01", value = "male"},
|
||||
{ label = "s_m_m_ups_01", value = "male"},
|
||||
{ label = "s_m_m_ups_02", value = "male"},
|
||||
{ label = "s_m_o_busker_01", value = "male"},
|
||||
{ label = "s_m_y_airworker", value = "male"},
|
||||
{ label = "s_m_y_ammucity_01", value = "male"},
|
||||
{ label = "s_m_y_armymech_01", value = "male"},
|
||||
{ label = "s_m_y_autopsy_01", value = "male"},
|
||||
{ label = "s_m_y_barman_01", value = "male"},
|
||||
{ label = "s_m_y_baywatch_01", value = "male"},
|
||||
{ label = "s_m_y_blackops_01", value = "male"},
|
||||
{ label = "s_m_y_blackops_02", value = "male"},
|
||||
{ label = "s_m_y_busboy_01", value = "male"},
|
||||
{ label = "s_m_y_chef_01", value = "male"},
|
||||
{ label = "s_m_y_clown_01", value = "male"},
|
||||
{ label = "s_m_y_construct_01", value = "male"},
|
||||
{ label = "s_m_y_construct_02", value = "male"},
|
||||
{ label = "s_m_y_cop_01", value = "male"},
|
||||
{ label = "s_m_y_dealer_01", value = "male"},
|
||||
{ label = "s_m_y_devinsec_01", value = "male"},
|
||||
{ label = "s_m_y_dockwork_01", value = "male"},
|
||||
{ label = "s_m_y_doorman_01", value = "male"},
|
||||
{ label = "s_m_y_dwservice_01", value = "male"},
|
||||
{ label = "s_m_y_dwservice_02", value = "male"},
|
||||
{ label = "s_m_y_factory_01", value = "male"},
|
||||
{ label = "s_m_y_garbage", value = "male"},
|
||||
{ label = "s_m_y_grip_01", value = "male"},
|
||||
{ label = "s_m_y_marine_01", value = "male"},
|
||||
{ label = "s_m_y_marine_02", value = "male"},
|
||||
{ label = "s_m_y_marine_03", value = "male"},
|
||||
{ label = "s_m_y_mime", value = "male"},
|
||||
{ label = "s_m_y_pestcont_01", value = "male"},
|
||||
{ label = "s_m_y_pilot_01", value = "male"},
|
||||
{ label = "s_m_y_prismuscl_01", value = "male"},
|
||||
{ label = "s_m_y_prisoner_01", value = "male"},
|
||||
{ label = "s_m_y_robber_01", value = "male"},
|
||||
{ label = "s_m_y_shop_mask", value = "male"},
|
||||
{ label = "s_m_y_strvend_01", value = "male"},
|
||||
{ label = "s_m_y_uscg_01", value = "male"},
|
||||
{ label = "s_m_y_valet_01", value = "male"},
|
||||
{ label = "s_m_y_waiter_01", value = "male"},
|
||||
{ label = "s_m_y_winclean_01", value = "male"},
|
||||
{ label = "s_m_y_xmech_01", value = "male"},
|
||||
{ label = "s_m_y_xmech_02", value = "male"},
|
||||
{ label = "u_m_m_aldinapoli", value = "male"},
|
||||
{ label = "u_m_m_bankman", value = "male"},
|
||||
{ label = "u_m_m_bikehire_01", value = "male"},
|
||||
{ label = "u_m_m_fibarchitect", value = "male"},
|
||||
{ label = "u_m_m_filmdirector", value = "male"},
|
||||
{ label = "u_m_m_glenstank_01", value = "male"},
|
||||
{ label = "u_m_m_griff_01", value = "male"},
|
||||
{ label = "u_m_m_jesus_01", value = "male"},
|
||||
{ label = "u_m_m_jewelsec_01", value = "male"},
|
||||
{ label = "u_m_m_jewelthief", value = "male"},
|
||||
{ label = "u_m_m_markfost", value = "male"},
|
||||
{ label = "u_m_m_partytarget", value = "male"},
|
||||
{ label = "u_m_m_prolsec_01", value = "male"},
|
||||
{ label = "u_m_m_promourn_01", value = "male"},
|
||||
{ label = "u_m_m_rivalpap", value = "male"},
|
||||
{ label = "u_m_m_spyactor", value = "male"},
|
||||
{ label = "u_m_m_willyfist", value = "male"},
|
||||
{ label = "u_m_o_finguru_01", value = "male"},
|
||||
{ label = "u_m_o_taphillbilly", value = "male"},
|
||||
{ label = "u_m_o_tramp_01", value = "male"},
|
||||
{ label = "u_m_y_abner", value = "male"},
|
||||
{ label = "u_m_y_antonb", value = "male"},
|
||||
{ label = "u_m_y_babyd", value = "male"},
|
||||
{ label = "u_m_y_baygor", value = "male"},
|
||||
{ label = "u_m_y_burgerdrug_01", value = "male"},
|
||||
{ label = "u_m_y_chip", value = "male"},
|
||||
{ label = "u_m_y_cyclist_01", value = "male"},
|
||||
{ label = "u_m_y_fibmugger_01", value = "male"},
|
||||
{ label = "u_m_y_guido_01", value = "male"},
|
||||
{ label = "u_m_y_gunvend_01", value = "male"},
|
||||
{ label = "u_m_y_imporage", value = "male"},
|
||||
{ label = "u_m_y_mani", value = "male"},
|
||||
{ label = "u_m_y_militarybum", value = "male"},
|
||||
{ label = "u_m_y_paparazzi", value = "male"},
|
||||
{ label = "u_m_y_party_01", value = "male"},
|
||||
{ label = "u_m_y_pogo_01", value = "male"},
|
||||
{ label = "u_m_y_prisoner_01", value = "male"},
|
||||
{ label = "u_m_y_proldriver_01", value = "male"},
|
||||
{ label = "u_m_y_rsranger_01", value = "male"},
|
||||
{ label = "u_m_y_sbike", value = "male"},
|
||||
{ label = "u_m_y_staggrm_01", value = "male"},
|
||||
{ label = "u_m_y_tattoo_01", value = "male"},
|
||||
{ label = "u_m_y_zombie_01", value = "male"},
|
||||
{ label = "u_m_y_hippie_01", value = "male"},
|
||||
{ label = "a_m_y_hippy_01", value = "male"},
|
||||
{ label = "a_m_y_stbla_m", value = "male"},
|
||||
{ label = "ig_terry_m", value = "male"},
|
||||
{ label = "a_m_m_ktown_m", value = "male"},
|
||||
{ label = "a_m_y_skater_m", value = "male"},
|
||||
{ label = "u_m_y_coop", value = "male"},
|
||||
{ label = "ig_car3guy1_m", value = "male"},
|
||||
{ label = "tony", value = "male"},
|
||||
{ label = "g_m_m_chigoon_02_m", value = "male"},
|
||||
{ label = "a_m_o_acult_01", value = "male"}
|
||||
}
|
36
resources/[ps]/ps-adminmenu/fxmanifest.lua
Normal file
36
resources/[ps]/ps-adminmenu/fxmanifest.lua
Normal file
@ -0,0 +1,36 @@
|
||||
fx_version 'cerulean'
|
||||
|
||||
game "gta5"
|
||||
|
||||
author "Project Sloth & OK1ez"
|
||||
version '1.1.2'
|
||||
description 'Admin Menu'
|
||||
repository 'https://github.com/Project-Sloth/ps-adminmenu'
|
||||
|
||||
lua54 'yes'
|
||||
|
||||
ui_page 'html/index.html'
|
||||
-- ui_page 'https://localhost:5173/' --for dev
|
||||
|
||||
client_script {
|
||||
'client/**',
|
||||
}
|
||||
|
||||
server_script {
|
||||
"server/**",
|
||||
"@oxmysql/lib/MySQL.lua",
|
||||
}
|
||||
|
||||
shared_script {
|
||||
'@ox_lib/init.lua',
|
||||
"shared/**",
|
||||
}
|
||||
|
||||
files {
|
||||
'html/**',
|
||||
'data/ped.lua',
|
||||
'data/object.lua',
|
||||
'locales/da.json',
|
||||
}
|
||||
|
||||
ox_lib 'locale' -- v3.8.0 or above
|
1
resources/[ps]/ps-adminmenu/html/index.css
Normal file
1
resources/[ps]/ps-adminmenu/html/index.css
Normal file
File diff suppressed because one or more lines are too long
26
resources/[ps]/ps-adminmenu/html/index.html
Normal file
26
resources/[ps]/ps-adminmenu/html/index.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<!-- FontAwesome Icons Import -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css">
|
||||
<!-- Material Icons Import -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<!-- Material Design Icons Import -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font/css/materialdesignicons.min.css">
|
||||
<!-- Line Awesome Icons Import -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/line-awesome/dist/line-awesome/css/line-awesome.min.css">
|
||||
<!-- Boostrap Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.min.css">
|
||||
|
||||
<title>ps-adminmenu</title>
|
||||
<script type="module" crossorigin src="./index.js"></script>
|
||||
<link rel="stylesheet" href="./index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
6
resources/[ps]/ps-adminmenu/html/index.js
Normal file
6
resources/[ps]/ps-adminmenu/html/index.js
Normal file
File diff suppressed because one or more lines are too long
97
resources/[ps]/ps-adminmenu/locales/da.json
Normal file
97
resources/[ps]/ps-adminmenu/locales/da.json
Normal file
@ -0,0 +1,97 @@
|
||||
{
|
||||
"already_plate": "Nummerpladen er allerede i brug",
|
||||
"amount_max": "Maksimum beløb er 999.999,-",
|
||||
"ban_expires": "Ban udløber: ",
|
||||
"ban_perm": "Du er blevet permanent udelukket!",
|
||||
"banreason": "Årsag: %s, indtil %s",
|
||||
"banned": "Du er blevet udelukket!",
|
||||
"blackout": "Blackout er %s",
|
||||
"blips_activated": "Blips aktiveret",
|
||||
"blips_deactivated": "Blips deaktiveret",
|
||||
"body_health": "HP: ",
|
||||
"bucket_get": "Spiller %s er i bucket: %s",
|
||||
"bucket_set": "Routing sat til bucket: %s",
|
||||
"bucket_set_for_target": "Routing sat til bucket: %s til bucket: %s",
|
||||
"cannot_store_veh": "Kan ikke opbevare denne bil i din garage",
|
||||
"cant_spectate_yourself": "Du kan ikke se dig selv",
|
||||
"command_admin_desc": "Toggle Admin Menu",
|
||||
"command_noclip_desc": "Toggle NoClip",
|
||||
"copy_heading": "Kopierede vinkel",
|
||||
"copy_vector2": "Kopierede vector2",
|
||||
"copy_vector3": "Kopierede vector3",
|
||||
"copy_vector4": "Kopierede vector4",
|
||||
"deFrozen": "Du har sat %s fri",
|
||||
"empty_input": "Input var tomt.",
|
||||
"eng_health": "Motorhelbred: ",
|
||||
"entered_vehicle": "Gik ind i køretøj",
|
||||
"ent_id": "Entity ID: ",
|
||||
"explode_player": "Personen sprang i luften :P",
|
||||
"Frozen": "%s er blevet frosset",
|
||||
"gangset": "Du har givet %s banden: %s med grad %s",
|
||||
"give_item": "Gav %s til %s",
|
||||
"give_item_all": "Gav %s til alle spillere",
|
||||
"give_money": "Gav %s %s",
|
||||
"give_money_all": "Gav %s til alle spillere",
|
||||
"give_money_all_crypto": "Gav %s Crypto/s til alle spillere",
|
||||
"give_money_crypto": "Gav %s Crypto/s til %s",
|
||||
"givecar.plates_alreadyused": "Kan ikke give køretøj. Nummerplade %s er allerede i brug på et andet køretøj",
|
||||
"givecar.success.source": "Gav køretøjet %s til %s.",
|
||||
"givecar.success.target": "Du har modtaget et nyt køretøj med nummerplade %s. Du kan finde den ved %s.",
|
||||
"godmode": "Gudmode er %s",
|
||||
"hash": "Hash: ",
|
||||
"inf_ammo_toggled": "Uendelig ammunition slået til",
|
||||
"invisible": "Usynlighed: %s",
|
||||
"invcleared": "%s's inventar blev tømt",
|
||||
"jobset": "Du har givet %s jobbet: %s med grad %s",
|
||||
"kicked": "Du blev kicked!",
|
||||
"model": "Model: ",
|
||||
"names_activated": "Navne aktiveret",
|
||||
"names_deactivated": "Navne deaktiveret",
|
||||
"net_id": "Net ID: ",
|
||||
"net_id_not_registered": "Net ID ikke registreret",
|
||||
"new_staffchat": "Ny Staff-besked",
|
||||
"no_free_seats": "Der er ingen ledige pladser",
|
||||
"no_perms": "Du har ikke tilladelse til at udføre denne handling",
|
||||
"no_waypoint": "Intet waypoint sat",
|
||||
"no_weapon": "Spilleren har ikke en pistol.",
|
||||
"noclip_disabled": "No-clip slået fra",
|
||||
"noclip_enabled": "No-clip slået til",
|
||||
"not_enough_money": "Ikke nok penge til at fjerne penge fra spilleren",
|
||||
"not_in_veh": "Du er ikke i et køretøj..",
|
||||
"not_in_vehicle": "Du er ikke i et køretøj",
|
||||
"not_online": "Spilleren er ikke online",
|
||||
"ped_coords": "Ped Koordinater: ",
|
||||
"plate_max": "Nummerpladen kan maks være 8 tegn",
|
||||
"plate_invalid": "Ugyldig nummerplade",
|
||||
"player_not_found": "Spilleren blev ikke fundet",
|
||||
"player_not_in_veh": "Spilleren er ikke i et køretøj",
|
||||
"player_perms": "%s fik [ %s ] tilladelser.",
|
||||
"playerbanned": "Du har udelukket %s i %s med årsag: %s",
|
||||
"playerdrunk": "Gjorde spilleren fuld: ",
|
||||
"reason": "Årsag: ",
|
||||
"refueled_vehicle": "Tankede køretøjet op",
|
||||
"restarted_resource": "Genstartede ressource",
|
||||
"removed_stress_player": "Fjernede stress fra spiller",
|
||||
"set_on_fire": "Person sat i brand :P",
|
||||
"set_wepaon_ammo": "Gav %s ammunition!",
|
||||
"started_resource": "Startede ressource",
|
||||
"status_title": "CFX Status",
|
||||
"stopped_resource": "Stoppede ressource",
|
||||
"state_changed": "Du har sat køretøjets tilstand.",
|
||||
"take_money": "Fjernede %s fra %s",
|
||||
"take_money_crypto": "Fjernede %s Crypto/s fra %s",
|
||||
"target_same_bucket": "Prøvede at placere %s i samme bucket",
|
||||
"teleported_waypoint": "Teleporterede til waypoint.",
|
||||
"toggled_cuffs": "Toggled håndjern",
|
||||
"toggle_dev": "Toggled Dev Mode",
|
||||
"tp_error": "Fejl under teleportering.",
|
||||
"u_veh_owner": "Dette køretøj er allerede dit..",
|
||||
"veh_fixed": "Køretøj repareret for %s",
|
||||
"veh_owner": "Køretøjet er nu dit!",
|
||||
"vehicle_dev_data": "Køretøjs-information",
|
||||
"vehicle_max_modded": "Køretøjet er top tunet",
|
||||
"vehicle_not_driver": "Ikke i førersædet",
|
||||
"warned": "Du er blevet advaret ",
|
||||
"warngiven": "Advaret: ",
|
||||
"weatherType": "Vejret er ændret til: %s"
|
||||
}
|
24
resources/[ps]/ps-adminmenu/server/chat.lua
Normal file
24
resources/[ps]/ps-adminmenu/server/chat.lua
Normal file
@ -0,0 +1,24 @@
|
||||
local messages = {}
|
||||
|
||||
-- Staff Chat
|
||||
RegisterNetEvent('ps-adminmenu:server:sendMessageServer', function(message, citizenid, fullname)
|
||||
if not CheckPerms('mod') then return end
|
||||
|
||||
local time = os.time() * 1000
|
||||
local players = QBCore.Functions.GetPlayers()
|
||||
|
||||
for i = 1, #players, 1 do
|
||||
local player = players[i]
|
||||
if QBCore.Functions.IsOptin(player) then
|
||||
QBCore.Functions.Notify(player, locale("new_staffchat", 'info', 7500))
|
||||
end
|
||||
end
|
||||
|
||||
messages[#messages + 1] = { message = message, citizenid = citizenid, fullname = fullname, time = time }
|
||||
end)
|
||||
|
||||
|
||||
lib.callback.register('ps-adminmenu:callback:GetMessages', function()
|
||||
if not CheckPerms('mod') then return {} end
|
||||
return messages
|
||||
end)
|
109
resources/[ps]/ps-adminmenu/server/inventory.lua
Normal file
109
resources/[ps]/ps-adminmenu/server/inventory.lua
Normal file
@ -0,0 +1,109 @@
|
||||
-- Clear Inventory
|
||||
RegisterNetEvent('ps-adminmenu:server:ClearInventory', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local player = selectedData["Player"].value
|
||||
local Player = QBCore.Functions.GetPlayer(player)
|
||||
|
||||
if not Player then
|
||||
return QBCore.Functions.Notify(source, locale("not_online"), 'error', 7500)
|
||||
end
|
||||
|
||||
if Config.Inventory == 'ox_inventory' then
|
||||
exports.ox_inventory:ClearInventory(player)
|
||||
else
|
||||
exports[Config.Inventory]:ClearInventory(player, nil)
|
||||
end
|
||||
|
||||
QBCore.Functions.Notify(src,
|
||||
locale("invcleared", Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname),
|
||||
'success', 7500)
|
||||
end)
|
||||
|
||||
-- Clear Inventory Offline
|
||||
RegisterNetEvent('ps-adminmenu:server:ClearInventoryOffline', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local citizenId = selectedData["Citizen ID"].value
|
||||
local Player = QBCore.Functions.GetPlayerByCitizenId(citizenId)
|
||||
|
||||
if Player then
|
||||
if Config.Inventory == 'ox_inventory' then
|
||||
exports.ox_inventory:ClearInventory(Player.PlayerData.source)
|
||||
else
|
||||
exports[Config.Inventory]:ClearInventory(Player.PlayerData.source, nil)
|
||||
end
|
||||
QBCore.Functions.Notify(src,
|
||||
locale("invcleared", Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname),
|
||||
'success', 7500)
|
||||
else
|
||||
MySQL.Async.fetchAll("SELECT * FROM players WHERE citizenid = @citizenid", { ['@citizenid'] = citizenId },
|
||||
function(result)
|
||||
if result and result[1] then
|
||||
MySQL.Async.execute("UPDATE players SET inventory = '{}' WHERE citizenid = @citizenid",
|
||||
{ ['@citizenid'] = citizenId })
|
||||
QBCore.Functions.Notify(src, "Spillerens inventar blev tømt", 'success', 7500)
|
||||
else
|
||||
QBCore.Functions.Notify(src, locale("player_not_found"), 'error', 7500)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Open Inv [ox side]
|
||||
RegisterNetEvent('ps-adminmenu:server:OpenInv', function(data)
|
||||
exports.ox_inventory:forceOpenInventory(source, 'player', data)
|
||||
end)
|
||||
|
||||
-- Open Stash [ox side]
|
||||
RegisterNetEvent('ps-adminmenu:server:OpenStash', function(data)
|
||||
exports.ox_inventory:forceOpenInventory(source, 'stash', data)
|
||||
end)
|
||||
|
||||
-- Open Trunk [ox side]
|
||||
RegisterNetEvent('ps-adminmenu:server:OpenTrunk', function(data)
|
||||
exports.ox_inventory:forceOpenInventory(source, 'trunk', data)
|
||||
end)
|
||||
|
||||
-- Give Item
|
||||
RegisterNetEvent('ps-adminmenu:server:GiveItem', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local target = selectedData["Player"].value
|
||||
local item = selectedData["Item"].value
|
||||
local amount = selectedData["Amount"].value
|
||||
local Player = QBCore.Functions.GetPlayer(target)
|
||||
|
||||
if not item or not amount then return end
|
||||
if not Player then
|
||||
return QBCore.Functions.Notify(source, locale("not_online"), 'error', 7500)
|
||||
end
|
||||
|
||||
Player.Functions.AddItem(item, amount)
|
||||
QBCore.Functions.Notify(source,
|
||||
locale("give_item", tonumber(amount) .. " " .. item,
|
||||
Player.PlayerData.charinfo.firstname .. " " .. Player.PlayerData.charinfo.lastname), "success", 7500)
|
||||
end)
|
||||
|
||||
-- Give Item to All
|
||||
RegisterNetEvent('ps-adminmenu:server:GiveItemAll', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local item = selectedData["Item"].value
|
||||
local amount = selectedData["Amount"].value
|
||||
local players = QBCore.Functions.GetPlayers()
|
||||
|
||||
if not item or not amount then return end
|
||||
|
||||
for _, id in pairs(players) do
|
||||
local Player = QBCore.Functions.GetPlayer(id)
|
||||
Player.Functions.AddItem(item, amount)
|
||||
QBCore.Functions.Notify(source, locale("give_item_all", amount .. " " .. item), "success", 7500)
|
||||
end
|
||||
end)
|
9
resources/[ps]/ps-adminmenu/server/main.lua
Normal file
9
resources/[ps]/ps-adminmenu/server/main.lua
Normal file
@ -0,0 +1,9 @@
|
||||
QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
lib.addCommand('admin', {
|
||||
help = 'Åben admin-menuen',
|
||||
restricted = 'qbcore.mod'
|
||||
}, function(source)
|
||||
TriggerClientEvent('ps-adminmenu:client:OpenUI', source)
|
||||
end)
|
||||
-- Callbacks
|
330
resources/[ps]/ps-adminmenu/server/misc.lua
Normal file
330
resources/[ps]/ps-adminmenu/server/misc.lua
Normal file
@ -0,0 +1,330 @@
|
||||
-- Ban Player
|
||||
RegisterNetEvent('ps-adminmenu:server:BanPlayer', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local player = selectedData["Player"].value
|
||||
local reason = selectedData["Reason"].value or ""
|
||||
local time = selectedData["Duration"].value
|
||||
|
||||
local banTime = tonumber(os.time() + time)
|
||||
local timeTable = os.date('*t', banTime)
|
||||
|
||||
MySQL.insert('INSERT INTO bans (name, license, discord, ip, reason, expire, bannedby) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
||||
{ GetPlayerName(player), QBCore.Functions.GetIdentifier(player, 'license'), QBCore.Functions.GetIdentifier(
|
||||
player, 'discord'), 'ip:0.0.0.0', reason, banTime, GetPlayerName(source) })
|
||||
|
||||
if time == 2147483647 then
|
||||
DropPlayer(player, locale("banned") .. '\n' .. locale("reason") .. reason .. locale("ban_perm"))
|
||||
else
|
||||
DropPlayer(player,
|
||||
locale("banned") ..
|
||||
'\n' ..
|
||||
locale("reason") ..
|
||||
reason ..
|
||||
'\n' ..
|
||||
locale("ban_expires") ..
|
||||
timeTable['day'] ..
|
||||
'/' .. timeTable['month'] .. '/' .. timeTable['year'] .. ' ' .. timeTable['hour'] .. ':' .. timeTable['min'])
|
||||
end
|
||||
|
||||
QBCore.Functions.Notify(source, locale("playerbanned", player, banTime, reason), 'success', 7500)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:server:ServerMessage' , function(data, selectedData)
|
||||
local target = -1
|
||||
local from = 'Staff'
|
||||
local message = selectedData["Message"].value
|
||||
local time = selectedData["Duration"].value or 10
|
||||
|
||||
if message == "" then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if selectedData["Player"] then
|
||||
target = selectedData["Player"].value
|
||||
from = GetPlayerName(source)
|
||||
QBCore.Functions.Notify(source, 'Besked sendt til '..GetPlayerName(target).."!")
|
||||
end
|
||||
|
||||
TriggerClientEvent('ox_lib:notify', -1, {
|
||||
id = 'AdminNotification',
|
||||
title = 'Info fra '..from,
|
||||
description = message,
|
||||
-- type = 'inform',
|
||||
duration = (time * 1000),
|
||||
position = 'top',
|
||||
iconColor = '#ffffff',
|
||||
iconAnimation = 'beatFade',
|
||||
style = {
|
||||
backgroundColor = '#3377ff',
|
||||
color = '#ffffff',
|
||||
['.description'] = {
|
||||
color = '#ffffff'
|
||||
}
|
||||
},
|
||||
})
|
||||
TriggerClientEvent('InteractSound_CL:PlayOnAll', target, "announcement", 0.4)
|
||||
end)
|
||||
|
||||
-- Warn Player
|
||||
RegisterNetEvent('ps-adminmenu:server:WarnPlayer', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local targetId = selectedData["Player"].value
|
||||
local target = QBCore.Functions.GetPlayer(targetId)
|
||||
local reason = selectedData["Reason"].value
|
||||
local sender = QBCore.Functions.GetPlayer(source)
|
||||
local warnId = 'ADVAR-' .. math.random(1000, 9999)
|
||||
if target ~= nil then
|
||||
QBCore.Functions.Notify(target.PlayerData.source,
|
||||
locale("warned") .. ", for: " .. locale("reason") .. ": " .. reason, 'info', 10000)
|
||||
QBCore.Functions.Notify(source,
|
||||
locale("warngiven") .. GetPlayerName(target.PlayerData.source) .. ", for: " .. reason)
|
||||
MySQL.insert('INSERT INTO player_warns (senderIdentifier, targetIdentifier, reason, warnId) VALUES (?, ?, ?, ?)',
|
||||
{
|
||||
sender.PlayerData.license,
|
||||
target.PlayerData.license,
|
||||
reason,
|
||||
warnId
|
||||
})
|
||||
else
|
||||
TriggerClientEvent('QBCore:Notify', source, locale("not_online"), 'error')
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:server:KickPlayer', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local src = source
|
||||
local target = QBCore.Functions.GetPlayer(selectedData["Player"].value)
|
||||
local reason = selectedData["Reason"].value
|
||||
|
||||
if not target then
|
||||
QBCore.Functions.Notify(src, locale("not_online"), 'error', 7500)
|
||||
return
|
||||
end
|
||||
|
||||
DropPlayer(target.PlayerData.source, locale("kicked") .. '\n' .. locale("reason") .. reason)
|
||||
end)
|
||||
|
||||
-- Revive Player
|
||||
RegisterNetEvent('ps-adminmenu:server:Revive', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local player = selectedData["Player"].value
|
||||
|
||||
TriggerClientEvent('hospital:client:Revive', player)
|
||||
end)
|
||||
|
||||
-- Revive All
|
||||
RegisterNetEvent('ps-adminmenu:server:ReviveAll', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
TriggerClientEvent('hospital:client:Revive', -1)
|
||||
end)
|
||||
|
||||
-- Revive Radius
|
||||
RegisterNetEvent('ps-adminmenu:server:ReviveRadius', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local ped = GetPlayerPed(src)
|
||||
local pos = GetEntityCoords(ped)
|
||||
local players = QBCore.Functions.GetPlayers()
|
||||
|
||||
for k, v in pairs(players) do
|
||||
local target = GetPlayerPed(v)
|
||||
local targetPos = GetEntityCoords(target)
|
||||
local dist = #(pos - targetPos)
|
||||
|
||||
if dist < 15.0 then
|
||||
TriggerClientEvent("hospital:client:Revive", v)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Set RoutingBucket
|
||||
RegisterNetEvent('ps-adminmenu:server:SetBucket', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local player = selectedData["Player"].value
|
||||
local bucket = selectedData["Bucket"].value
|
||||
local currentBucket = GetPlayerRoutingBucket(player)
|
||||
|
||||
if bucket == currentBucket then
|
||||
return QBCore.Functions.Notify(src, locale("target_same_bucket", player), 'error', 7500)
|
||||
end
|
||||
|
||||
SetPlayerRoutingBucket(player, bucket)
|
||||
QBCore.Functions.Notify(src, locale("bucket_set_for_target", player, bucket), 'success', 7500)
|
||||
end)
|
||||
|
||||
-- Get RoutingBucket
|
||||
RegisterNetEvent('ps-adminmenu:server:GetBucket', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local player = selectedData["Player"].value
|
||||
local currentBucket = GetPlayerRoutingBucket(player)
|
||||
|
||||
QBCore.Functions.Notify(src, locale("bucket_get", player, currentBucket), 'success', 7500)
|
||||
end)
|
||||
|
||||
-- Give Money
|
||||
RegisterNetEvent('ps-adminmenu:server:GiveMoney', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local target, amount, moneyType = selectedData["Player"].value, selectedData["Amount"].value,
|
||||
selectedData["Type"].value
|
||||
local Player = QBCore.Functions.GetPlayer(tonumber(target))
|
||||
|
||||
if Player == nil then
|
||||
return QBCore.Functions.Notify(src, locale("not_online"), 'error', 7500)
|
||||
end
|
||||
|
||||
Player.Functions.AddMoney(tostring(moneyType), tonumber(amount))
|
||||
QBCore.Functions.Notify(src,
|
||||
locale((moneyType == "crypto" and "give_money_crypto" or "give_money"), tonumber(amount),
|
||||
Player.PlayerData.charinfo.firstname .. " " .. Player.PlayerData.charinfo.lastname), "success")
|
||||
end)
|
||||
|
||||
-- Give Money to all
|
||||
RegisterNetEvent('ps-adminmenu:server:GiveMoneyAll', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local amount, moneyType = selectedData["Amount"].value, selectedData["Type"].value
|
||||
local players = QBCore.Functions.GetPlayers()
|
||||
|
||||
for _, v in pairs(players) do
|
||||
local Player = QBCore.Functions.GetPlayer(tonumber(v))
|
||||
Player.Functions.AddMoney(tostring(moneyType), tonumber(amount))
|
||||
QBCore.Functions.Notify(src,
|
||||
locale((moneyType == "crypto" and "give_money_all_crypto" or "give_money_all"), tonumber(amount)), "success")
|
||||
end
|
||||
end)
|
||||
|
||||
-- Take Money
|
||||
RegisterNetEvent('ps-adminmenu:server:TakeMoney', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local target, amount, moneyType = selectedData["Player"].value, selectedData["Amount"].value,
|
||||
selectedData["Type"].value
|
||||
local Player = QBCore.Functions.GetPlayer(tonumber(target))
|
||||
|
||||
if Player == nil then
|
||||
return QBCore.Functions.Notify(src, locale("not_online"), 'error', 7500)
|
||||
end
|
||||
|
||||
if Player.PlayerData.money[moneyType] >= tonumber(amount) then
|
||||
Player.Functions.RemoveMoney(moneyType, tonumber(amount), "state-fees")
|
||||
else
|
||||
QBCore.Functions.Notify(src, locale("not_enough_money"), "primary")
|
||||
end
|
||||
|
||||
QBCore.Functions.Notify(src,
|
||||
locale((moneyType == "crypto" and "take_money_crypto" or "take_money"), tonumber(amount) .. ",-",
|
||||
Player.PlayerData.charinfo.firstname .. " " .. Player.PlayerData.charinfo.lastname), "success")
|
||||
end)
|
||||
|
||||
-- Blackout
|
||||
local Blackout = false
|
||||
RegisterNetEvent('ps-adminmenu:server:ToggleBlackout', function(data)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
Blackout = not Blackout
|
||||
|
||||
local src = source
|
||||
|
||||
if Blackout then
|
||||
TriggerClientEvent('QBCore:Notify', src, locale("blackout", "enabled"), 'primary')
|
||||
while Blackout do
|
||||
Wait(0)
|
||||
exports["qb-weathersync"]:setBlackout(true)
|
||||
end
|
||||
exports["qb-weathersync"]:setBlackout(false)
|
||||
TriggerClientEvent('QBCore:Notify', src, locale("blackout", "disabled"), 'primary')
|
||||
end
|
||||
end)
|
||||
|
||||
-- Toggle Cuffs
|
||||
RegisterNetEvent('ps-adminmenu:server:CuffPlayer', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local target = selectedData["Player"].value
|
||||
|
||||
TriggerClientEvent('ps-adminmenu:client:ToggleCuffs', target)
|
||||
QBCore.Functions.Notify(source, locale("toggled_cuffs"), 'success')
|
||||
end)
|
||||
|
||||
-- Give Clothing Menu
|
||||
RegisterNetEvent('ps-adminmenu:server:ClothingMenu', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local target = tonumber(selectedData["Player"].value)
|
||||
|
||||
if target == nil then
|
||||
return QBCore.Functions.Notify(src, locale("not_online"), 'error', 7500)
|
||||
end
|
||||
|
||||
if target == src then
|
||||
TriggerClientEvent("ps-adminmenu:client:CloseUI", src)
|
||||
end
|
||||
|
||||
TriggerClientEvent('qb-clothing:client:openMenu', target)
|
||||
end)
|
||||
|
||||
-- Force to character setup
|
||||
RegisterNetEvent('ps-adminmenu:server:CharacterCreation', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local target = tonumber(selectedData["Player"].value)
|
||||
|
||||
if target == nil then
|
||||
return QBCore.Functions.Notify(src, locale("not_online"), 'error', 7500)
|
||||
end
|
||||
|
||||
if target == src then
|
||||
TriggerClientEvent("ps-adminmenu:client:CloseUI", src)
|
||||
end
|
||||
|
||||
TriggerClientEvent('hp_charcreator:openCreator', target, nil, true)
|
||||
end)
|
||||
|
||||
-- Set Ped
|
||||
RegisterNetEvent("ps-adminmenu:server:setPed", function(data, selectedData)
|
||||
local src = source
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then
|
||||
QBCore.Functions.Notify(src, locale("no_perms"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
local ped = selectedData["Ped Models"].label
|
||||
local tsrc = selectedData["Player"].value
|
||||
local Player = QBCore.Functions.GetPlayer(tsrc)
|
||||
|
||||
if not Player then
|
||||
QBCore.Functions.Notify(locale("not_online"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
TriggerClientEvent("ps-adminmenu:client:setPed", Player.PlayerData.source, ped)
|
||||
end)
|
151
resources/[ps]/ps-adminmenu/server/players.lua
Normal file
151
resources/[ps]/ps-adminmenu/server/players.lua
Normal file
@ -0,0 +1,151 @@
|
||||
local function getVehicles(cid)
|
||||
local result = MySQL.query.await(
|
||||
'SELECT vehicle, plate, fuel, engine, body FROM player_vehicles WHERE citizenid = ?', { cid })
|
||||
local vehicles = {}
|
||||
|
||||
for k, v in pairs(result) do
|
||||
local vehicleData = QBCore.Shared.Vehicles[v.vehicle]
|
||||
|
||||
if vehicleData then
|
||||
vehicles[#vehicles + 1] = {
|
||||
id = k,
|
||||
cid = cid,
|
||||
label = vehicleData.name,
|
||||
brand = vehicleData.brand,
|
||||
model = vehicleData.model,
|
||||
plate = v.plate,
|
||||
fuel = v.fuel,
|
||||
engine = v.engine,
|
||||
body = v.body
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return vehicles
|
||||
end
|
||||
|
||||
local function getPlayers()
|
||||
local players = {}
|
||||
local GetPlayers = QBCore.Functions.GetQBPlayers()
|
||||
|
||||
for k, v in pairs(GetPlayers) do
|
||||
local playerData = v.PlayerData
|
||||
local vehicles = getVehicles(playerData.citizenid)
|
||||
|
||||
players[#players + 1] = {
|
||||
id = k,
|
||||
name = playerData.charinfo.firstname .. ' ' .. playerData.charinfo.lastname,
|
||||
cid = playerData.citizenid,
|
||||
license = QBCore.Functions.GetIdentifier(k, 'license'),
|
||||
discord = QBCore.Functions.GetIdentifier(k, 'discord'),
|
||||
steam = QBCore.Functions.GetIdentifier(k, 'steam'),
|
||||
job = playerData.job.label,
|
||||
grade = playerData.job.grade.level,
|
||||
dob = playerData.charinfo.birthdate,
|
||||
cash = playerData.money.cash,
|
||||
bank = playerData.money.bank,
|
||||
phone = playerData.charinfo.phone,
|
||||
vehicles = vehicles
|
||||
}
|
||||
end
|
||||
|
||||
table.sort(players, function(a, b) return a.id < b.id end)
|
||||
|
||||
return players
|
||||
end
|
||||
|
||||
lib.callback.register('ps-adminmenu:callback:GetPlayers', function(source)
|
||||
return getPlayers()
|
||||
end)
|
||||
|
||||
-- Set Job
|
||||
RegisterNetEvent('ps-adminmenu:server:SetJob', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local src = source
|
||||
local playerId, Job, Grade = selectedData["Player"].value, selectedData["Job"].value, selectedData["Grade"].value
|
||||
local Player = QBCore.Functions.GetPlayer(playerId)
|
||||
local name = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname
|
||||
local jobInfo = QBCore.Shared.Jobs[Job]
|
||||
local grade = jobInfo["grades"][selectedData["Grade"].value]
|
||||
|
||||
if not jobInfo then
|
||||
TriggerClientEvent('QBCore:Notify', source, "Ikke et gyldigt job", 'error')
|
||||
return
|
||||
end
|
||||
|
||||
if not grade then
|
||||
TriggerClientEvent('QBCore:Notify', source, "Ikke et gyldigt job", 'error')
|
||||
return
|
||||
end
|
||||
|
||||
Player.Functions.SetJob(tostring(Job), tonumber(Grade))
|
||||
if Config.RenewedPhone then
|
||||
exports['qb-phone']:hireUser(tostring(Job), Player.PlayerData.citizenid, tonumber(Grade))
|
||||
end
|
||||
|
||||
QBCore.Functions.Notify(src, locale("jobset", name, Job, Grade), 'success', 5000)
|
||||
end)
|
||||
|
||||
-- Set Gang
|
||||
RegisterNetEvent('ps-adminmenu:server:SetGang', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local src = source
|
||||
local playerId, Gang, Grade = selectedData["Player"].value, selectedData["Gang"].value, selectedData["Grade"].value
|
||||
local Player = QBCore.Functions.GetPlayer(playerId)
|
||||
local name = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname
|
||||
local GangInfo = QBCore.Shared.Gangs[Gang]
|
||||
local grade = GangInfo["grades"][selectedData["Grade"].value]
|
||||
|
||||
if not GangInfo then
|
||||
TriggerClientEvent('QBCore:Notify', source, "Ikke en gyldig bande", 'error')
|
||||
return
|
||||
end
|
||||
|
||||
if not grade then
|
||||
TriggerClientEvent('QBCore:Notify', source, "Ikke en gyldig bande", 'error')
|
||||
return
|
||||
end
|
||||
|
||||
Player.Functions.SetGang(tostring(Gang), tonumber(Grade))
|
||||
QBCore.Functions.Notify(src, locale("gangset", name, Gang, Grade), 'success', 5000)
|
||||
end)
|
||||
|
||||
-- Set Perms
|
||||
RegisterNetEvent("ps-adminmenu:server:SetPerms", function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local src = source
|
||||
local rank = selectedData["Permissions"].value
|
||||
local targetId = selectedData["Player"].value
|
||||
local tPlayer = QBCore.Functions.GetPlayer(tonumber(targetId))
|
||||
|
||||
if not tPlayer then
|
||||
QBCore.Functions.Notify(src, locale("not_online"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
local name = tPlayer.PlayerData.charinfo.firstname .. ' ' .. tPlayer.PlayerData.charinfo.lastname
|
||||
|
||||
QBCore.Functions.AddPermission(tPlayer.PlayerData.source, tostring(rank))
|
||||
QBCore.Functions.Notify(tPlayer.PlayerData.source, locale("player_perms", name, rank), 'success', 5000)
|
||||
end)
|
||||
|
||||
-- Remove Stress
|
||||
RegisterNetEvent("ps-adminmenu:server:RemoveStress", function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local src = source
|
||||
local targetId = selectedData['Player (Optional)'] and tonumber(selectedData['Player (Optional)'].value) or src
|
||||
local tPlayer = QBCore.Functions.GetPlayer(tonumber(targetId))
|
||||
|
||||
if not tPlayer then
|
||||
QBCore.Functions.Notify(src, locale("not_online"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
TriggerClientEvent('ps-adminmenu:client:removeStress', targetId)
|
||||
|
||||
QBCore.Functions.Notify(tPlayer.PlayerData.source, locale("removed_stress_player"), 'success', 5000)
|
||||
end)
|
32
resources/[ps]/ps-adminmenu/server/regcommands.lua
Normal file
32
resources/[ps]/ps-adminmenu/server/regcommands.lua
Normal file
@ -0,0 +1,32 @@
|
||||
local commandsTable, addedCommands = {}, {}
|
||||
local blacklistCommands = {
|
||||
"sv_", "adhesive_", "citizen_", "con_", "endpoint_", "fileserver", "load_server",
|
||||
"mysql_connection", "net_tcp", "netPort", "netlib", "onesync", "onesync_",
|
||||
"rateLimiter_", "svgui", "web_base", "temp_", "txAdmin", "txa",
|
||||
}
|
||||
|
||||
local function isCommandBlacklisted(commandName)
|
||||
for _, bcommand in pairs(blacklistCommands) do
|
||||
if string.match(commandName, '^' .. bcommand) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
lib.callback.register('ps-adminmenu:callback:GetCommands', function()
|
||||
if not CheckPerms(Config.ShowCommandsPerms) then return {} end
|
||||
|
||||
local allCommands = GetRegisteredCommands()
|
||||
|
||||
for _, command in ipairs(allCommands) do
|
||||
if not isCommandBlacklisted(command.name) and not addedCommands[command.name] then
|
||||
commandsTable[#commandsTable + 1] = {
|
||||
name = '/' .. command.name
|
||||
}
|
||||
addedCommands[command.name] = true -- prevent duplicates
|
||||
end
|
||||
end
|
||||
|
||||
return commandsTable
|
||||
end)
|
45
resources/[ps]/ps-adminmenu/server/resources.lua
Normal file
45
resources/[ps]/ps-adminmenu/server/resources.lua
Normal file
@ -0,0 +1,45 @@
|
||||
local resources = {}
|
||||
|
||||
lib.callback.register('ps-adminmenu:callback:GetResources', function(source)
|
||||
local totalResources = GetNumResources()
|
||||
|
||||
resources = {}
|
||||
|
||||
for i = 0, totalResources - 1 do
|
||||
local resourceName = GetResourceByFindIndex(i)
|
||||
local author = GetResourceMetadata(resourceName, "author")
|
||||
local version = GetResourceMetadata(resourceName, "version")
|
||||
local description = GetResourceMetadata(resourceName, "description")
|
||||
local resourceState = GetResourceState(resourceName)
|
||||
|
||||
resources[#resources + 1] = {
|
||||
name = resourceName,
|
||||
author = author,
|
||||
version = version,
|
||||
description = description,
|
||||
resourceState = resourceState,
|
||||
}
|
||||
end
|
||||
|
||||
return resources
|
||||
end)
|
||||
|
||||
|
||||
lib.callback.register('ps-adminmenu:callback:ChangeResourceState', function(source, data, perms)
|
||||
if not CheckPerms(Config.ResourcePerms) then return end
|
||||
|
||||
if data.state == "start" then
|
||||
StartResource(data.name)
|
||||
print("Started " .. data.name)
|
||||
elseif data.state == "stop" then
|
||||
StopResource(data.name)
|
||||
print("Stopped " .. data.name)
|
||||
elseif data.state == "restart" then
|
||||
StopResource(data.name)
|
||||
Wait(200)
|
||||
StartResource(data.name)
|
||||
print("Restarted " .. data.name)
|
||||
end
|
||||
|
||||
return resources
|
||||
end)
|
42
resources/[ps]/ps-adminmenu/server/spectate.lua
Normal file
42
resources/[ps]/ps-adminmenu/server/spectate.lua
Normal file
@ -0,0 +1,42 @@
|
||||
local spectating = {}
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:server:SpectateTarget', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local player = selectedData["Player"].value
|
||||
|
||||
local type = "1"
|
||||
if player == source then return QBCore.Functions.Notify(source, locale("cant_spectate_yourself"), 'error', 7500) end
|
||||
if spectating[source] then type = "0" end
|
||||
TriggerEvent('ps-adminmenu:spectate', player, type == "1", source, data.perms)
|
||||
CheckRoutingbucket(source, player)
|
||||
end)
|
||||
|
||||
AddEventHandler('ps-adminmenu:spectate', function(target, on, source, perms)
|
||||
local tPed = GetPlayerPed(target)
|
||||
local data = {}
|
||||
data.perms = perms
|
||||
if DoesEntityExist(tPed) then
|
||||
if not on then
|
||||
TriggerClientEvent('ps-adminmenu:cancelSpectate', source)
|
||||
spectating[source] = false
|
||||
FreezeEntityPosition(GetPlayerPed(source), false)
|
||||
TriggerClientEvent('ps-adminmenu:client:toggleNames', source, data)
|
||||
elseif on then
|
||||
TriggerClientEvent('ps-adminmenu:requestSpectate', source, NetworkGetNetworkIdFromEntity(tPed), target,
|
||||
GetPlayerName(target))
|
||||
spectating[source] = true
|
||||
TriggerClientEvent('ps-adminmenu:client:toggleNames', source, data)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ps-adminmenu:spectate:teleport', function(target)
|
||||
local source = source
|
||||
local ped = GetPlayerPed(target)
|
||||
if DoesEntityExist(ped) then
|
||||
local targetCoords = GetEntityCoords(ped)
|
||||
SetEntityCoords(GetPlayerPed(source), targetCoords.x, targetCoords.y, targetCoords.z - 10)
|
||||
FreezeEntityPosition(GetPlayerPed(source), true)
|
||||
end
|
||||
end)
|
28
resources/[ps]/ps-adminmenu/server/teleport.lua
Normal file
28
resources/[ps]/ps-adminmenu/server/teleport.lua
Normal file
@ -0,0 +1,28 @@
|
||||
-- Teleport To Player
|
||||
RegisterNetEvent('ps-adminmenu:server:TeleportToPlayer', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local player = selectedData["Player"].value
|
||||
local targetPed = GetPlayerPed(player)
|
||||
local coords = GetEntityCoords(targetPed)
|
||||
|
||||
CheckRoutingbucket(src, player)
|
||||
TriggerClientEvent('ps-adminmenu:client:TeleportToPlayer', src, coords)
|
||||
end)
|
||||
|
||||
-- Bring Player
|
||||
RegisterNetEvent('ps-adminmenu:server:BringPlayer', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local targetPed = selectedData["Player"].value
|
||||
local admin = GetPlayerPed(src)
|
||||
local coords = GetEntityCoords(admin)
|
||||
local target = GetPlayerPed(targetPed)
|
||||
|
||||
CheckRoutingbucket(targetPed, src)
|
||||
SetEntityCoords(target, coords)
|
||||
end)
|
50
resources/[ps]/ps-adminmenu/server/trolls.lua
Normal file
50
resources/[ps]/ps-adminmenu/server/trolls.lua
Normal file
@ -0,0 +1,50 @@
|
||||
-- Freeze Player
|
||||
local frozen = false
|
||||
RegisterNetEvent('ps-adminmenu:server:FreezePlayer', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local src = source
|
||||
|
||||
local target = selectedData["Player"].value
|
||||
|
||||
local ped = GetPlayerPed(target)
|
||||
local Player = QBCore.Functions.GetPlayer(target)
|
||||
|
||||
if not frozen then
|
||||
frozen = true
|
||||
FreezeEntityPosition(ped, true)
|
||||
QBCore.Functions.Notify(src,
|
||||
locale("Frozen",
|
||||
Player.PlayerData.charinfo.firstname ..
|
||||
" " .. Player.PlayerData.charinfo.lastname .. " | " .. Player.PlayerData.citizenid), 'Success', 7500)
|
||||
else
|
||||
frozen = false
|
||||
FreezeEntityPosition(ped, false)
|
||||
QBCore.Functions.Notify(src,
|
||||
locale("deFrozen",
|
||||
Player.PlayerData.charinfo.firstname ..
|
||||
" " .. Player.PlayerData.charinfo.lastname .. " | " .. Player.PlayerData.citizenid), 'Success', 7500)
|
||||
end
|
||||
if Player == nil then return QBCore.Functions.Notify(src, locale("not_online"), 'error', 7500) end
|
||||
end)
|
||||
|
||||
-- Drunk Player
|
||||
RegisterNetEvent('ps-adminmenu:server:DrunkPlayer', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
|
||||
local src = source
|
||||
local target = selectedData["Player"].value
|
||||
local targetPed = GetPlayerPed(target)
|
||||
local Player = QBCore.Functions.GetPlayer(target)
|
||||
|
||||
if not Player then
|
||||
return QBCore.Functions.Notify(src, locale("not_online"), 'error', 7500)
|
||||
end
|
||||
|
||||
TriggerClientEvent('ps-adminmenu:client:InitiateDrunkEffect', target)
|
||||
QBCore.Functions.Notify(src,
|
||||
locale("playerdrunk",
|
||||
Player.PlayerData.charinfo.firstname ..
|
||||
" " .. Player.PlayerData.charinfo.lastname .. " | " .. Player.PlayerData.citizenid), 'Success', 7500)
|
||||
end)
|
77
resources/[ps]/ps-adminmenu/server/utils.lua
Normal file
77
resources/[ps]/ps-adminmenu/server/utils.lua
Normal file
@ -0,0 +1,77 @@
|
||||
local function noPerms(source)
|
||||
QBCore.Functions.Notify(source, "You are not Admin or God.", 'error')
|
||||
end
|
||||
|
||||
--- @param perms string
|
||||
function CheckPerms(perms)
|
||||
local hasPerms = QBCore.Functions.HasPermission(source, perms)
|
||||
|
||||
if not hasPerms then
|
||||
return noPerms(source)
|
||||
end
|
||||
|
||||
return hasPerms
|
||||
end
|
||||
|
||||
function CheckDataFromKey(key)
|
||||
local actions = Config.Actions[key]
|
||||
if actions then
|
||||
local data = nil
|
||||
|
||||
if actions.event then
|
||||
data = actions
|
||||
end
|
||||
|
||||
if actions.dropdown then
|
||||
for _, v in pairs(actions.dropdown) do
|
||||
if v.event then
|
||||
local new = v
|
||||
new.perms = actions.perms
|
||||
data = new
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
local playerActions = Config.PlayerActions[key]
|
||||
if playerActions then
|
||||
return playerActions
|
||||
end
|
||||
|
||||
local otherActions = Config.OtherActions[key]
|
||||
if otherActions then
|
||||
return otherActions
|
||||
end
|
||||
end
|
||||
|
||||
---@param plate string
|
||||
---@return boolean
|
||||
function CheckAlreadyPlate(plate)
|
||||
local vPlate = QBCore.Shared.Trim(plate)
|
||||
local result = MySQL.single.await("SELECT plate FROM player_vehicles WHERE plate = ?", { vPlate })
|
||||
if result and result.plate then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
lib.callback.register('ps-adminmenu:callback:CheckPerms', function(source, perms)
|
||||
return CheckPerms(perms)
|
||||
end)
|
||||
|
||||
lib.callback.register('ps-adminmenu:callback:CheckAlreadyPlate', function(_, vPlate)
|
||||
return CheckAlreadyPlate(vPlate)
|
||||
end)
|
||||
|
||||
--- @param source number
|
||||
--- @param target number
|
||||
function CheckRoutingbucket(source, target)
|
||||
local sourceBucket = GetPlayerRoutingBucket(source)
|
||||
local targetBucket = GetPlayerRoutingBucket(target)
|
||||
|
||||
if sourceBucket == targetBucket then return end
|
||||
|
||||
SetPlayerRoutingBucket(source, targetBucket)
|
||||
QBCore.Functions.Notify(source, locale("bucket_set", targetBucket), 'error', 7500)
|
||||
end
|
152
resources/[ps]/ps-adminmenu/server/vehicle.lua
Normal file
152
resources/[ps]/ps-adminmenu/server/vehicle.lua
Normal file
@ -0,0 +1,152 @@
|
||||
-- Admin Car
|
||||
RegisterNetEvent('ps-adminmenu:server:SaveCar', function(mods, vehicle, _, plate)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
local result = MySQL.query.await('SELECT plate FROM player_vehicles WHERE plate = ?', { plate })
|
||||
|
||||
if result[1] == nil then
|
||||
MySQL.insert(
|
||||
'INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, state) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
||||
{
|
||||
Player.PlayerData.license,
|
||||
Player.PlayerData.citizenid,
|
||||
vehicle.model,
|
||||
vehicle.hash,
|
||||
json.encode(mods),
|
||||
plate,
|
||||
0
|
||||
})
|
||||
TriggerClientEvent('QBCore:Notify', src, locale("veh_owner"), 'success', 5000)
|
||||
else
|
||||
TriggerClientEvent('QBCore:Notify', src, locale("u_veh_owner"), 'error', 3000)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Give Car
|
||||
RegisterNetEvent("ps-adminmenu:server:givecar", function(data, selectedData)
|
||||
local src = source
|
||||
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then
|
||||
QBCore.Functions.Notify(src, locale("no_perms"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
local vehmodel = selectedData['Vehicle'].value
|
||||
local vehicleData = lib.callback.await("ps-adminmenu:client:getvehData", src, vehmodel)
|
||||
|
||||
if not next(vehicleData) then
|
||||
return
|
||||
end
|
||||
|
||||
local tsrc = selectedData['Player'].value
|
||||
local plate = selectedData['Plate (Optional)'] and selectedData['Plate (Optional)'].value or vehicleData.plate
|
||||
local garage = selectedData['Garage (Optional)'] and selectedData['Garage (Optional)'].value or Config.DefaultGarage
|
||||
local Player = QBCore.Functions.GetPlayer(tsrc)
|
||||
|
||||
if plate and #plate < 1 then
|
||||
plate = vehicleData.plate
|
||||
end
|
||||
|
||||
if garage and #garage < 1 then
|
||||
garage = Config.DefaultGarage
|
||||
end
|
||||
|
||||
if plate:len() > 8 then
|
||||
QBCore.Functions.Notify(src, locale("plate_max"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
if not Player then
|
||||
QBCore.Functions.Notify(src, locale("not_online"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
if CheckAlreadyPlate(plate) then
|
||||
QBCore.Functions.Notify(src, locale("givecar.error.plates_alreadyused", plate:upper()), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
MySQL.insert(
|
||||
'INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
{
|
||||
Player.PlayerData.license,
|
||||
Player.PlayerData.citizenid,
|
||||
vehmodel,
|
||||
joaat(vehmodel),
|
||||
json.encode(vehicleData),
|
||||
plate,
|
||||
garage,
|
||||
1
|
||||
})
|
||||
|
||||
QBCore.Functions.Notify(src,
|
||||
locale("givecar.success.source", QBCore.Shared.Vehicles[vehmodel].name,
|
||||
("%s %s"):format(Player.PlayerData.charinfo.firstname, Player.PlayerData.charinfo.lastname)), "success", 5000)
|
||||
QBCore.Functions.Notify(Player.PlayerData.source, locale("givecar.success.target", plate:upper(), garage), "success",
|
||||
5000)
|
||||
end)
|
||||
|
||||
-- Give Car
|
||||
RegisterNetEvent("ps-adminmenu:server:SetVehicleState", function(data, selectedData)
|
||||
local src = source
|
||||
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then
|
||||
QBCore.Functions.Notify(src, locale("no_perms"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
local plate = string.upper(selectedData['Plate'].value)
|
||||
local state = tonumber(selectedData['State'].value)
|
||||
|
||||
if plate:len() > 8 then
|
||||
QBCore.Functions.Notify(src, locale("plate_max"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
if not CheckAlreadyPlate(plate) then
|
||||
QBCore.Functions.Notify(src, locale("plate_doesnt_exist"), "error", 5000)
|
||||
return
|
||||
end
|
||||
|
||||
MySQL.update('UPDATE player_vehicles SET state = ?, depotprice = ? WHERE plate = ?', { state, 0, plate })
|
||||
|
||||
QBCore.Functions.Notify(src, locale("state_changed"), "success", 5000)
|
||||
end)
|
||||
|
||||
-- Change Plate
|
||||
RegisterNetEvent('ps-adminmenu:server:ChangePlate', function(newPlate, currentPlate)
|
||||
local newPlate = newPlate:upper()
|
||||
|
||||
if Config.Inventory == 'ox_inventory' then
|
||||
exports.ox_inventory:UpdateVehicle(currentPlate, newPlate)
|
||||
end
|
||||
|
||||
MySQL.Sync.execute('UPDATE player_vehicles SET plate = ? WHERE plate = ?', { newPlate, currentPlate })
|
||||
MySQL.Sync.execute('UPDATE trunkitems SET plate = ? WHERE plate = ?', { newPlate, currentPlate })
|
||||
MySQL.Sync.execute('UPDATE gloveboxitems SET plate = ? WHERE plate = ?', { newPlate, currentPlate })
|
||||
end)
|
||||
|
||||
lib.callback.register('ps-adminmenu:server:GetVehicleByPlate', function(source, plate)
|
||||
local result = MySQL.query.await('SELECT vehicle FROM player_vehicles WHERE plate = ?', { plate })
|
||||
local veh = result[1] and result[1].vehicle or {}
|
||||
return veh
|
||||
end)
|
||||
|
||||
-- Fix Vehicle for player
|
||||
RegisterNetEvent('ps-adminmenu:server:FixVehFor', function(data, selectedData)
|
||||
local data = CheckDataFromKey(data)
|
||||
if not data or not CheckPerms(data.perms) then return end
|
||||
local src = source
|
||||
local playerId = selectedData['Player'].value
|
||||
local Player = QBCore.Functions.GetPlayer(tonumber(playerId))
|
||||
if Player then
|
||||
local name = Player.PlayerData.charinfo.firstname .. " " .. Player.PlayerData.charinfo.lastname
|
||||
TriggerClientEvent('iens:repaira', Player.PlayerData.source)
|
||||
TriggerClientEvent('vehiclemod:client:fixEverything', Player.PlayerData.source)
|
||||
QBCore.Functions.Notify(src, locale("veh_fixed", name), 'success', 7500)
|
||||
else
|
||||
TriggerClientEvent('QBCore:Notify', src, locale("not_online"), "error")
|
||||
end
|
||||
end)
|
776
resources/[ps]/ps-adminmenu/shared/config.lua
Normal file
776
resources/[ps]/ps-adminmenu/shared/config.lua
Normal file
@ -0,0 +1,776 @@
|
||||
Config = Config or {}
|
||||
|
||||
Config.Fuel = "qb-fuel" -- "ps-fuel", "LegacyFuel"
|
||||
Config.ResourcePerms = 'admin' -- permission to control resource(start stop restart)
|
||||
Config.ShowCommandsPerms = 'admin' -- permission to show all commands
|
||||
Config.RenewedPhone = false -- if you use qb-phone from renewed. (multijob)
|
||||
|
||||
-- Key Bindings
|
||||
Config.Keybindings = true
|
||||
Config.AdminKey = "PageDown"
|
||||
Config.NoclipKey = "PageUp"
|
||||
|
||||
-- Give Car
|
||||
Config.DefaultGarage = "pillboxgarage"
|
||||
|
||||
Config.Actions = {
|
||||
["admin_car"] = {
|
||||
label = "Admin Car",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:Admincar",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["ban_player"] = {
|
||||
label = "Ban Player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Reason", option = "text" },
|
||||
{
|
||||
label = "Duration",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "Permanent", value = "2147483647" },
|
||||
{ label = "10 Minutes", value = "600" },
|
||||
{ label = "30 Minutes", value = "1800" },
|
||||
{ label = "1 Hour", value = "3600" },
|
||||
{ label = "6 Hours", value = "21600" },
|
||||
{ label = "12 Hours", value = "43200" },
|
||||
{ label = "1 Day", value = "86400" },
|
||||
{ label = "3 Days", value = "259200" },
|
||||
{ label = "1 Week", value = "604800" },
|
||||
{ label = "3 Week", value = "1814400" },
|
||||
},
|
||||
},
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:BanPlayer" },
|
||||
},
|
||||
},
|
||||
|
||||
["server_info"] = {
|
||||
label = "Send server-wide message",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Message", option = "text" },
|
||||
{
|
||||
label = "Duration",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "20 sekunder", value = "20" },
|
||||
{ label = "15 sekunder", value = "15" },
|
||||
{ label = "10 sekunder", value = "10" },
|
||||
{ label = "5 sekunder", value = "5" },
|
||||
},
|
||||
},
|
||||
{ label = "Send besked", option = "button", type = "server", event = "ps-adminmenu:server:ServerMessage" },
|
||||
},
|
||||
},
|
||||
|
||||
["player_info"] = {
|
||||
label = "Send message to player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Message", option = "text" },
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{
|
||||
label = "Duration",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "20 sekunder", value = "20" },
|
||||
{ label = "15 sekunder", value = "15" },
|
||||
{ label = "10 sekunder", value = "10" },
|
||||
{ label = "5 sekunder", value = "5" },
|
||||
},
|
||||
},
|
||||
{ label = "Send besked", option = "button", type = "server", event = "ps-adminmenu:server:ServerMessage" },
|
||||
},
|
||||
},
|
||||
|
||||
["bring_player"] = {
|
||||
label = "Bring Player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:BringPlayer" },
|
||||
},
|
||||
},
|
||||
|
||||
["change_weather"] = {
|
||||
label = "Change Weather",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{
|
||||
label = "Weather",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "Extrasunny", value = "Extrasunny" },
|
||||
{ label = "Clear", value = "Clear" },
|
||||
{ label = "Neutral", value = "Neutral" },
|
||||
{ label = "Smog", value = "Smog" },
|
||||
{ label = "Foggy", value = "Foggy" },
|
||||
{ label = "Overcast", value = "Overcast" },
|
||||
{ label = "Clouds", value = "Clouds" },
|
||||
{ label = "Clearing", value = "Clearing" },
|
||||
{ label = "Rain", value = "Rain" },
|
||||
{ label = "Thunder", value = "Thunder" },
|
||||
{ label = "Snow", value = "Snow" },
|
||||
{ label = "Blizzard", value = "Blizzard" },
|
||||
{ label = "Snowlight", value = "Snowlight" },
|
||||
{ label = "Xmas", value = "Xmas" },
|
||||
{ label = "Halloween", value = "Halloween" },
|
||||
},
|
||||
},
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:ChangeWeather" },
|
||||
},
|
||||
},
|
||||
|
||||
["change_time"] = {
|
||||
label = "Change Time",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{
|
||||
label = "Time Events",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "Sunrise", value = "06" },
|
||||
{ label = "Morning", value = "09" },
|
||||
{ label = "Noon", value = "12" },
|
||||
{ label = "Sunset", value = "21" },
|
||||
{ label = "Evening", value = "22" },
|
||||
{ label = "Night", value = "24" },
|
||||
},
|
||||
},
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:ChangeTime" },
|
||||
},
|
||||
},
|
||||
|
||||
["change_plate"] = {
|
||||
label = "Change Plate",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Plate", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:ChangePlate" },
|
||||
},
|
||||
},
|
||||
|
||||
["clear_inventory"] = {
|
||||
label = "Clear Inventory",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:ClearInventory" },
|
||||
},
|
||||
},
|
||||
|
||||
["clear_inventory_offline"] = {
|
||||
label = "Clear Inventory Offline",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Citizen ID", option = "text", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:ClearInventoryOffline" },
|
||||
},
|
||||
},
|
||||
|
||||
["clothing_menu"] = {
|
||||
label = "Give Clothing Menu",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:ClothingMenu" },
|
||||
},
|
||||
},
|
||||
|
||||
["character_menu"] = {
|
||||
label = "Force character creation",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
},
|
||||
{ label = "Force character creation", option = "button", type = "server", event = "ps-adminmenu:server:CharacterCreation"},
|
||||
},
|
||||
|
||||
["set_ped"] = {
|
||||
label = "Set Ped",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Ped Models", option = "dropdown", data = "pedlist" },
|
||||
},
|
||||
{ label = "Set Ped", option = "button", type = "server", event = "ps-adminmenu:server:setPed" },
|
||||
},
|
||||
|
||||
["copy_coords"] = {
|
||||
label = "Copy Coords",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{
|
||||
label = "Copy Coords",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "Copy Vector2", value = "vector2" },
|
||||
{ label = "Copy Vector3", value = "vector3" },
|
||||
{ label = "Copy Vector4", value = "vector4" },
|
||||
{ label = "Copy Heading", value = "heading" },
|
||||
},
|
||||
},
|
||||
{ label = "Copy to Clipboard", option = "button", type = "client", event = "ps-adminmenu:client:copyToClipboard"},
|
||||
},
|
||||
},
|
||||
|
||||
["delete_vehicle"] = {
|
||||
label = "Delete Vehicle",
|
||||
type = "command",
|
||||
event = "dv",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["freeze_player"] = {
|
||||
label = "Freeze Player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:FreezePlayer" },
|
||||
},
|
||||
},
|
||||
|
||||
["drunk_player"] = {
|
||||
label = "Make Player Drunk",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:DrunkPlayer" },
|
||||
},
|
||||
},
|
||||
|
||||
["remove_stress"] = {
|
||||
label = "Remove Stress",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player (Optional)", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:RemoveStress" },
|
||||
},
|
||||
},
|
||||
|
||||
["set_ammo"] = {
|
||||
label = "Set Ammo",
|
||||
perms = "admin",
|
||||
dropdown = {
|
||||
{ label = "Ammo Ammount", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:SetAmmo" },
|
||||
},
|
||||
},
|
||||
|
||||
-- ["nui_focus"] = {
|
||||
-- label = "Give NUI Focus",
|
||||
-- perms = "mod",
|
||||
-- dropdown = {
|
||||
-- { label = "Player", option = "dropdown", data = "players" },
|
||||
-- { label = "Confirm", option = "button", type = "client", event = "" },
|
||||
-- },
|
||||
-- },
|
||||
|
||||
["god_mode"] = {
|
||||
label = "God Mode",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:ToggleGodmode",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["give_car"] = {
|
||||
label = "Give Car",
|
||||
perms = "admin",
|
||||
dropdown = {
|
||||
{ label = "Vehicle", option = "dropdown", data = "vehicles" },
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Plate (Optional)", option = "text" },
|
||||
{ label = "Garage (Optional)", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:givecar" },
|
||||
}
|
||||
},
|
||||
|
||||
["invisible"] = {
|
||||
label = "Invisible",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:ToggleInvisible",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["blackout"] = {
|
||||
label = "Toggle Blackout",
|
||||
type = "server",
|
||||
event = "ps-adminmenu:server:ToggleBlackout",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["toggle_duty"] = {
|
||||
label = "Toggle Duty",
|
||||
type = "server",
|
||||
event = "QBCore:ToggleDuty",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["toggle_laser"] = {
|
||||
label = "Toggle Laser",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:ToggleLaser",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["set_perms"] = {
|
||||
label = "Set Perms",
|
||||
perms = "admin",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{
|
||||
label = "Permissions",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "Mod", value = "mod" },
|
||||
{ label = "Admin", value = "admin" },
|
||||
{ label = "God", value = "god" },
|
||||
},
|
||||
},
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:SetPerms" },
|
||||
},
|
||||
},
|
||||
|
||||
["set_bucket"] = {
|
||||
label = "Set Routing Bucket",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Bucket", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:SetBucket" },
|
||||
},
|
||||
},
|
||||
|
||||
["get_bucket"] = {
|
||||
label = "Get Routing Bucket",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:GetBucket" },
|
||||
},
|
||||
},
|
||||
|
||||
["mute_player"] = {
|
||||
label = "Mute Player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:MutePlayer" },
|
||||
},
|
||||
},
|
||||
|
||||
["noclip"] = {
|
||||
label = "Noclip",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:ToggleNoClip",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["debug"] = {
|
||||
label = "Debug",
|
||||
type = "client",
|
||||
event = "hud:enabledebug",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["open_inventory"] = {
|
||||
label = "Open Inventory",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:openInventory" },
|
||||
},
|
||||
},
|
||||
|
||||
["open_stash"] = {
|
||||
label = "Open Stash",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Stash", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:openStash" },
|
||||
},
|
||||
},
|
||||
|
||||
["open_trunk"] = {
|
||||
label = "Open Trunk",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Plate", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:openTrunk" },
|
||||
},
|
||||
},
|
||||
|
||||
["change_vehicle_state"] = {
|
||||
label = "Set Vehicle Garage State",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Plate", option = "text" },
|
||||
{
|
||||
label = "State",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "In", value = "1" },
|
||||
{ label = "Out", value = "0" },
|
||||
},
|
||||
},
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:SetVehicleState" },
|
||||
},
|
||||
},
|
||||
|
||||
["revive_all"] = {
|
||||
label = "Revive All",
|
||||
type = "server",
|
||||
event = "ps-adminmenu:server:ReviveAll",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["revive_player"] = {
|
||||
label = "Revive Player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:Revive" },
|
||||
},
|
||||
},
|
||||
|
||||
["revive_radius"] = {
|
||||
label = "Revive Radius",
|
||||
type = "server",
|
||||
event = "ps-adminmenu:server:ReviveRadius",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["refuel_vehicle"] = {
|
||||
label = "Refuel Vehicle",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:RefuelVehicle",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["set_job"] = {
|
||||
label = "Set Job",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Job", option = "dropdown", data = "jobs" },
|
||||
{ label = "Grade", option = "text", data = "grades" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:SetJob" },
|
||||
},
|
||||
},
|
||||
|
||||
["set_gang"] = {
|
||||
label = "Set Gang",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Gang", option = "dropdown", data = "gangs" },
|
||||
{ label = "Grade", option = "text", data = "grades" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:SetGang" },
|
||||
},
|
||||
},
|
||||
|
||||
["give_money"] = {
|
||||
label = "Give Money",
|
||||
perms = "admin",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Amount", option = "text" },
|
||||
{
|
||||
label = "Type",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "Cash", value = "cash" },
|
||||
{ label = "Bank", value = "bank" },
|
||||
{ label = "Crypto", value = "crypto" },
|
||||
},
|
||||
},
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:GiveMoney" },
|
||||
},
|
||||
},
|
||||
|
||||
["give_money_all"] = {
|
||||
label = "Give Money to All",
|
||||
perms = "admin",
|
||||
dropdown = {
|
||||
{ label = "Amount", option = "text" },
|
||||
{
|
||||
label = "Type",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "Cash", value = "cash" },
|
||||
{ label = "Bank", value = "bank" },
|
||||
{ label = "Crypto", value = "crypto" },
|
||||
},
|
||||
},
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:GiveMoneyAll" },
|
||||
},
|
||||
},
|
||||
|
||||
["remove_money"] = {
|
||||
label = "Remove Money",
|
||||
perms = "admin",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Amount", option = "text" },
|
||||
{
|
||||
label = "Type",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "Cash", value = "cash" },
|
||||
{ label = "Bank", value = "bank" },
|
||||
},
|
||||
},
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:TakeMoney" },
|
||||
},
|
||||
},
|
||||
|
||||
["give_item"] = {
|
||||
label = "Give Item",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Item", option = "dropdown", data = "items" },
|
||||
{ label = "Amount", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:GiveItem" },
|
||||
},
|
||||
},
|
||||
|
||||
["give_item_all"] = {
|
||||
label = "Give Item to All",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Item", option = "dropdown", data = "items" },
|
||||
{ label = "Amount", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:GiveItemAll" },
|
||||
},
|
||||
},
|
||||
|
||||
["spawn_vehicle"] = {
|
||||
label = "Spawn Vehicle",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Vehicle", option = "dropdown", data = "vehicles" },
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:SpawnVehicle" },
|
||||
},
|
||||
},
|
||||
|
||||
["fix_vehicle"] = {
|
||||
label = "Fix Vehicle",
|
||||
type = "command",
|
||||
event = "fix",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["fix_vehicle_for"] = {
|
||||
label = "Fix Vehicle for player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:FixVehFor" },
|
||||
},
|
||||
},
|
||||
|
||||
["spectate_player"] = {
|
||||
label = "Spectate Player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:SpectateTarget" },
|
||||
},
|
||||
},
|
||||
|
||||
["telport_to_player"] = {
|
||||
label = "Teleport to Player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:TeleportToPlayer" },
|
||||
},
|
||||
},
|
||||
|
||||
["telport_to_coords"] = {
|
||||
label = "Teleport to Coords",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Coords", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:TeleportToCoords" },
|
||||
},
|
||||
},
|
||||
|
||||
["teleport_to_location"] = {
|
||||
label = "Teleport to Location",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Location", option = "dropdown", data = "locations" },
|
||||
{ label = "Confirm", option = "button", type = "client", event = "ps-adminmenu:client:TeleportToLocation" },
|
||||
},
|
||||
},
|
||||
|
||||
["teleport_to_marker"] = {
|
||||
label = "Teleport to Marker",
|
||||
type = "command",
|
||||
event = "tpm",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["teleport_back"] = {
|
||||
label = "Teleport Back",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:TeleportBack",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["vehicle_dev"] = {
|
||||
label = "Vehicle Dev Menu",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:ToggleVehDevMenu",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["toggle_coords"] = {
|
||||
label = "Toggle Coords",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:ToggleCoords",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["toggle_blips"] = {
|
||||
label = "Toggle Blips",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:toggleBlips",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["toggle_names"] = {
|
||||
label = "Toggle Names",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:toggleNames",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["toggle_cuffs"] = {
|
||||
label = "Toggle Cuffs",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:CuffPlayer" },
|
||||
},
|
||||
},
|
||||
|
||||
["max_mods"] = {
|
||||
label = "Max Vehicle Mods",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:maxmodVehicle",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["warn_player"] = {
|
||||
label = "Warn Player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Reason", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:WarnPlayer" },
|
||||
},
|
||||
},
|
||||
|
||||
["infinite_ammo"] = {
|
||||
label = "Infinite Ammo",
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:setInfiniteAmmo",
|
||||
perms = "mod",
|
||||
},
|
||||
|
||||
["kick_player"] = {
|
||||
label = "Kick Player",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{ label = "Reason", option = "text" },
|
||||
{ label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:KickPlayer" },
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
["play_sound"] = {
|
||||
label = "Play Sound",
|
||||
perms = "mod",
|
||||
dropdown = {
|
||||
{ label = "Player", option = "dropdown", data = "players" },
|
||||
{
|
||||
label = "Sound",
|
||||
option = "dropdown",
|
||||
data = {
|
||||
{ label = "Alert", value = "alert" },
|
||||
{ label = "Cuff", value = "cuff" },
|
||||
{ label = "Air Wrench", value = "airwrench" },
|
||||
},
|
||||
},
|
||||
{ label = "Play Sound", option = "button", type = "client", event = "ps-adminmenu:client:PlaySound" },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Config.PlayerActions = {
|
||||
["teleportToPlayer"] = {
|
||||
label = "Teleport to Player",
|
||||
type = "server",
|
||||
event = "ps-adminmenu:server:TeleportToPlayer",
|
||||
perms = "mod",
|
||||
},
|
||||
["bringPlayer"] = {
|
||||
label = "Bring Player",
|
||||
type = "server",
|
||||
event = "ps-adminmenu:server:BringPlayer",
|
||||
perms = "mod",
|
||||
},
|
||||
["revivePlayer"] = {
|
||||
label = "Revive Player",
|
||||
event = "ps-adminmenu:server:Revive",
|
||||
perms = "mod",
|
||||
type = "server"
|
||||
},
|
||||
["spawnPersonalVehicle"] = {
|
||||
label = "Spawn Personal Vehicle",
|
||||
event = "ps-adminmenu:client:SpawnPersonalVehicle",
|
||||
perms = "mod",
|
||||
type = "client"
|
||||
},
|
||||
["banPlayer"] = {
|
||||
label = "Ban Player",
|
||||
event = "ps-adminmenu:server:BanPlayer",
|
||||
perms = "mod",
|
||||
type = "server"
|
||||
},
|
||||
["kickPlayer"] = {
|
||||
label = "Kick Player",
|
||||
event = "ps-adminmenu:server:KickPlayer",
|
||||
perms = "mod",
|
||||
type = "server"
|
||||
}
|
||||
}
|
||||
|
||||
Config.OtherActions = {
|
||||
["toggleDevmode"] = {
|
||||
type = "client",
|
||||
event = "ps-adminmenu:client:ToggleDev",
|
||||
perms = "admin",
|
||||
label = "Toggle Devmode"
|
||||
}
|
||||
}
|
||||
|
||||
AddEventHandler("onResourceStart", function()
|
||||
Wait(100)
|
||||
if GetResourceState('ox_inventory') == 'started' then
|
||||
Config.Inventory = 'ox_inventory'
|
||||
elseif GetResourceState('ps-inventory') == 'started' then
|
||||
Config.Inventory = 'ps-inventory'
|
||||
elseif GetResourceState('lj-inventory') == 'started' then
|
||||
Config.Inventory = 'lj-inventory'
|
||||
elseif GetResourceState('qb-inventory') == 'started' then
|
||||
Config.Inventory = 'qb-inventory'
|
||||
end
|
||||
end)
|
4
resources/[ps]/ps-adminmenu/ui/.gitignore
vendored
Normal file
4
resources/[ps]/ps-adminmenu/ui/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/node_modules/
|
||||
|
||||
|
||||
.DS_Store
|
6
resources/[ps]/ps-adminmenu/ui/.prettierrc
Normal file
6
resources/[ps]/ps-adminmenu/ui/.prettierrc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": true,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
44
resources/[ps]/ps-adminmenu/ui/README.md
Normal file
44
resources/[ps]/ps-adminmenu/ui/README.md
Normal file
@ -0,0 +1,44 @@
|
||||
# Svelte + TS + Vite
|
||||
|
||||
This template should help get you started developing with Svelte and TypeScript in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
|
||||
|
||||
## Need an official Svelte framework?
|
||||
|
||||
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
|
||||
|
||||
## Technical considerations
|
||||
|
||||
**Why use this over SvelteKit?**
|
||||
|
||||
- It brings its own routing solution which might not be preferable for some users.
|
||||
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
|
||||
`vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example.
|
||||
|
||||
This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
|
||||
|
||||
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
|
||||
|
||||
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
|
||||
|
||||
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
|
||||
|
||||
**Why enable `allowJs` in the TS template?**
|
||||
|
||||
While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
|
||||
|
||||
**Why is HMR not preserving my local component state?**
|
||||
|
||||
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
|
||||
|
||||
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
|
||||
|
||||
```ts
|
||||
// store.ts
|
||||
// An extremely simple external store
|
||||
import { writable } from 'svelte/store'
|
||||
export default writable(0)
|
||||
```
|
24
resources/[ps]/ps-adminmenu/ui/index.html
Normal file
24
resources/[ps]/ps-adminmenu/ui/index.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<!-- FontAwesome Icons Import -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css">
|
||||
<!-- Material Icons Import -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<!-- Material Design Icons Import -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font/css/materialdesignicons.min.css">
|
||||
<!-- Line Awesome Icons Import -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/line-awesome/dist/line-awesome/css/line-awesome.min.css">
|
||||
<!-- Boostrap Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.min.css">
|
||||
|
||||
<title>ps-adminmenu</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
3638
resources/[ps]/ps-adminmenu/ui/package-lock.json
generated
Normal file
3638
resources/[ps]/ps-adminmenu/ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
resources/[ps]/ps-adminmenu/ui/package.json
Normal file
24
resources/[ps]/ps-adminmenu/ui/package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "would_you_rather",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^2.4.3",
|
||||
"@tsconfig/svelte": "^5.0.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.27",
|
||||
"svelte": "^4.1.2",
|
||||
"svelte-check": "^3.4.6",
|
||||
"svelte-preprocess": "^5.0.4",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tslib": "^2.6.1",
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.4.9"
|
||||
}
|
||||
}
|
1353
resources/[ps]/ps-adminmenu/ui/pnpm-lock.yaml
generated
Normal file
1353
resources/[ps]/ps-adminmenu/ui/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
7
resources/[ps]/ps-adminmenu/ui/postcss.config.js
Normal file
7
resources/[ps]/ps-adminmenu/ui/postcss.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import tailwind from 'tailwindcss';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import tailwindConfig from './tailwind.config.cjs';
|
||||
|
||||
export default {
|
||||
plugins: [tailwind(tailwindConfig), autoprefixer],
|
||||
}
|
38
resources/[ps]/ps-adminmenu/ui/src/App.svelte
Normal file
38
resources/[ps]/ps-adminmenu/ui/src/App.svelte
Normal file
@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import VisibilityProvider from '@providers/VisibilityProvider.svelte'
|
||||
import { BROWSER_MODE, RESOURCE_NAME } from '@store/stores'
|
||||
import DebugBrowser from '@providers/DebugBrowser.svelte'
|
||||
import AlwaysListener from '@providers/AlwaysListener.svelte'
|
||||
import Main from './layout/Main.svelte'
|
||||
import VehicleDev from '@components/VehicleDev.svelte'
|
||||
import { VEHICLE_DEV } from '@store/vehicle_dev'
|
||||
import ToggleCoords from '@components/ToggleCoords.svelte'
|
||||
import { TOGGLE_COORDS } from '@store/togglecoords'
|
||||
import { ENTITY_INFO } from '@store/entityInfo'
|
||||
import EntityInformation from '@components/EntityInformation.svelte'
|
||||
|
||||
|
||||
$RESOURCE_NAME = 'ps-adminmenu'
|
||||
</script>
|
||||
|
||||
<VisibilityProvider>
|
||||
<Main />
|
||||
</VisibilityProvider>
|
||||
|
||||
{#if $VEHICLE_DEV?.show}
|
||||
<VehicleDev />
|
||||
{/if}
|
||||
|
||||
{#if $TOGGLE_COORDS?.show}
|
||||
<ToggleCoords />
|
||||
{/if}
|
||||
|
||||
{#if $ENTITY_INFO?.show}
|
||||
<EntityInformation />
|
||||
{/if}
|
||||
|
||||
<AlwaysListener />
|
||||
{#if $BROWSER_MODE}
|
||||
<DebugBrowser />
|
||||
<div class="absolute w-screen h-screen bg-neutral-800" />
|
||||
{/if}
|
160
resources/[ps]/ps-adminmenu/ui/src/components/Autofill.svelte
Normal file
160
resources/[ps]/ps-adminmenu/ui/src/components/Autofill.svelte
Normal file
@ -0,0 +1,160 @@
|
||||
<script>
|
||||
import { GANG_DATA, ITEM_DATA, JOB_DATA, LOCATION_DATA, VEHICLE_DATA, PED_LIST } from "@store/data"
|
||||
import { PLAYER } from "@store/players"
|
||||
import { SendNUI } from "@utils/SendNUI"
|
||||
import { onMount } from "svelte"
|
||||
import { slide } from "svelte/transition"
|
||||
|
||||
export let action
|
||||
export let label_title
|
||||
export let data
|
||||
|
||||
export let selectedData;
|
||||
|
||||
let search = ""
|
||||
let searchInputFocused = false;
|
||||
let DataDropdownActive = false;
|
||||
|
||||
function selectData(label, value) {
|
||||
search = label;
|
||||
DataDropdownActive = false;
|
||||
|
||||
selectedData({
|
||||
label: label,
|
||||
value: value,
|
||||
id: label_title
|
||||
});
|
||||
}
|
||||
|
||||
function handleInputFocus() {
|
||||
searchInputFocused = true;
|
||||
DataDropdownActive = true;
|
||||
search = ""
|
||||
}
|
||||
|
||||
function handleInputBlur() {
|
||||
if (!searchInputFocused) {
|
||||
DataDropdownActive = false;
|
||||
}
|
||||
searchInputFocused = false;
|
||||
}
|
||||
|
||||
async function GetPlayers() {
|
||||
const players = await SendNUI("getPlayers");
|
||||
PLAYER.set(players);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (data === "players") {
|
||||
GetPlayers();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="w-[22vh] flex flex-col bg-secondary rounded-[0.5vh] border-[0.1vh] border-primary">
|
||||
<div class="w-full h-[3.8vh] px-[1vh] flex justify-between items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={label_title}
|
||||
on:focus={handleInputFocus}
|
||||
on:blur={handleInputBlur}
|
||||
bind:value={search}
|
||||
class="h-full w-[90%] bg-transparent"
|
||||
/>
|
||||
<i class="fas fa-angle-{DataDropdownActive ? "down" : "right"} text-[1.2vh]"></i>
|
||||
</div>
|
||||
|
||||
{#if DataDropdownActive}
|
||||
<button
|
||||
class="w-full rounded-b-[0.5vh] flex flex-col max-h-[15vh] overflow-y-auto border-t border-primary scroll-visible"
|
||||
on:mouseenter={() => { searchInputFocused = true }}
|
||||
on:blur={() => { searchInputFocused = false }}
|
||||
transition:slide={{ duration: 150 }}
|
||||
>
|
||||
{#if data === "players"}
|
||||
{#each $PLAYER.filter(i => i.name.toLowerCase().includes(search.toLowerCase())) as i}
|
||||
<button
|
||||
class="w-full p-[0.5vh] flex justify-start text-start px-[1vh] gap-[0.5vh] hover:bg-tertiary"
|
||||
on:click={() =>
|
||||
selectData(i.name, i.id)
|
||||
}
|
||||
>
|
||||
<p>{i.name}</p>
|
||||
<p>({i.id})</p>
|
||||
</button>
|
||||
{/each}
|
||||
{:else if data === "vehicles"}
|
||||
{#each $VEHICLE_DATA.filter(i => i.label.toLowerCase().includes(search.toLowerCase()) || i.value.toLowerCase().includes(search.toLowerCase())) as i}
|
||||
<button
|
||||
class="w-full p-[0.5vh] flex justify-start text-start px-[1vh] gap-[0.5vh] hover:bg-tertiary"
|
||||
on:click={() => selectData(i.label, i.value)}
|
||||
>
|
||||
<p>{i.label}</p>
|
||||
<p>({i.value})</p>
|
||||
</button>
|
||||
{/each}
|
||||
{:else if data === "items"}
|
||||
{#each $ITEM_DATA.filter(i => i.label.toLowerCase().includes(search.toLowerCase()) || i.value.toLowerCase().includes(search.toLowerCase())) as i}
|
||||
<button
|
||||
class="w-full p-[0.5vh] flex justify-start text-start px-[1vh] gap-[0.5vh] hover:bg-tertiary"
|
||||
on:click={() => selectData(i.label, i.value)}
|
||||
>
|
||||
<p>{i.label}</p>
|
||||
<p>({i.value})</p>
|
||||
</button>
|
||||
{/each}
|
||||
{:else if data === "jobs"}
|
||||
{#each $JOB_DATA.filter(i => i.label.toLowerCase().includes(search.toLowerCase()) || i.value.toLowerCase().includes(search.toLowerCase())) as i}
|
||||
<button
|
||||
class="w-full p-[0.5vh] flex justify-start text-start px-[1vh] gap-[0.5vh] hover:bg-tertiary"
|
||||
on:click={() => selectData(i.label, i.value)}
|
||||
>
|
||||
<p>{i.label}</p>
|
||||
<p>({i.value})</p>
|
||||
</button>
|
||||
{/each}
|
||||
{:else if data === "gangs"}
|
||||
{#each $GANG_DATA.filter(i => i.label.toLowerCase().includes(search.toLowerCase()) || i.value.toLowerCase().includes(search.toLowerCase())) as i}
|
||||
<button
|
||||
class="w-full p-[0.5vh] flex justify-start text-start px-[1vh] gap-[0.5vh] hover:bg-tertiary"
|
||||
on:click={() => selectData(i.label, i.value)}
|
||||
>
|
||||
<p>{i.label}</p>
|
||||
<p>({i.value})</p>
|
||||
</button>
|
||||
{/each}
|
||||
{:else if data === "locations"}
|
||||
{#each $LOCATION_DATA.filter(i => i.label.toLowerCase().includes(search.toLowerCase()) || i.value.toLowerCase().includes(search.toLowerCase())) as i}
|
||||
<button
|
||||
class="w-full p-[0.5vh] flex justify-start text-start px-[1vh] gap-[0.5vh] hover:bg-tertiary"
|
||||
on:click={() => selectData(i.label, i.value)}
|
||||
>
|
||||
<p>{i.label}</p>
|
||||
<p>({i.value})</p>
|
||||
</button>
|
||||
{/each}
|
||||
{:else if data === "pedlist"}
|
||||
{#each $PED_LIST.filter(i => i.label.toLowerCase().includes(search.toLowerCase()) || i.value.toLowerCase().includes(search.toLowerCase())) as i}
|
||||
<button
|
||||
class="w-full p-[0.5vh] flex justify-start text-start px-[1vh] gap-[0.5vh] hover:bg-tertiary"
|
||||
on:click={() => selectData(i.label, i.value)}
|
||||
>
|
||||
<p>{i.label}</p>
|
||||
<p>({i.value})</p>
|
||||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each data.filter(i => i.label.toLowerCase().includes(search.toLowerCase()) || i.value.toLowerCase().includes(search.toLowerCase())) as i}
|
||||
<button
|
||||
class="w-full p-[0.5vh] flex justify-start text-start px-[1vh] gap-[0.5vh] hover:bg-tertiary"
|
||||
on:click={() => selectData(i.label, i.value)}
|
||||
>
|
||||
<p>{i.label}</p>
|
||||
<!-- <p>({i.value})</p> -->
|
||||
</button>
|
||||
{/each}
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -0,0 +1,164 @@
|
||||
<script>
|
||||
import {createEventDispatcher} from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let dropdownValues, label, selectedValue, id="array-dd", insideLabel = "";
|
||||
|
||||
let isOpen = false;
|
||||
|
||||
function toggleDropdown() {
|
||||
isOpen = !isOpen;
|
||||
|
||||
const selectWrapper = document.getElementById('select');
|
||||
if(isOpen) {
|
||||
selectWrapper.style.borderBottomLeftRadius = '0';
|
||||
selectWrapper.style.borderBottomRightRadius = '0';
|
||||
} else {
|
||||
selectWrapper.style.borderBottomLeftRadius = '0.2vw';
|
||||
selectWrapper.style.borderBottomRightRadius = '0.2vw';
|
||||
}
|
||||
}
|
||||
|
||||
function selectDropdownValue(value) {
|
||||
selectedValue = value;
|
||||
dispatch('selected-dropdown', value);
|
||||
isOpen = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="dropdown" id={id}>
|
||||
{#if label.trim() !== ''}
|
||||
<label for="dd">{label}: </label>
|
||||
{/if}
|
||||
<div class="dropdown-wrapper">
|
||||
<div class="select-wrapper" id="select" on:click={toggleDropdown}>
|
||||
{#if selectedValue.trim() !== ''}
|
||||
<div class="select-wrapper-selected-value">
|
||||
{#if insideLabel.trim() !== ""}
|
||||
<p class="inside-label">{insideLabel}</p>
|
||||
{/if}
|
||||
<p class="selected-value-text">{selectedValue}</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div></div>
|
||||
{/if}
|
||||
<i class="fas fa-chevron-down dropdown-chevron"></i>
|
||||
</div>
|
||||
{#if isOpen}
|
||||
<div class="options-wrapper">
|
||||
{#if dropdownValues.length < 1}
|
||||
<div class="no-items-found">
|
||||
No items found
|
||||
</div>
|
||||
{:else}
|
||||
{#each dropdownValues as ddValue}
|
||||
<div class="option-child" on:click={() => {selectDropdownValue(ddValue)}}>
|
||||
<p>
|
||||
{ddValue}
|
||||
{#if selectedValue === ddValue}
|
||||
<i class="fas fa-check icon"></i>
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
.dropdown > label {
|
||||
margin: 0 0.2vw;
|
||||
color: var(--light-text);
|
||||
}
|
||||
|
||||
.dropdown-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
min-width: 6vw;
|
||||
width: fit-content;
|
||||
/* padding: 0 0.2vw; */
|
||||
|
||||
background: linear-gradient(0deg, #242424, #242424), linear-gradient(0deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1));
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
|
||||
color: var(--app-name);
|
||||
border-radius: 0.2vw;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
width: auto;
|
||||
height: 1.7vw;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding-left: 0.2vw;
|
||||
padding-right: 0.3vw;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.select-wrapper-selected-value {
|
||||
font-size: 0.7vw;
|
||||
padding: 0.25vw;
|
||||
margin-right: 0.2vw;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.select-wrapper-selected-value > .inside-label {
|
||||
color: var(--less-light-border-color);
|
||||
margin-right: 0.3vw;
|
||||
}
|
||||
.select-wrapper-selected-value > .selected-value-text {
|
||||
padding-top: 0.05vw;
|
||||
}
|
||||
|
||||
.dropdown-chevron {
|
||||
text-align: center;
|
||||
font-size: 0.5vw;
|
||||
margin-top: 0.6vw;
|
||||
|
||||
color: var(--less-light-border-color);
|
||||
}
|
||||
|
||||
.options-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.options-wrapper > .no-items-found {
|
||||
font-size: 0.7vw;
|
||||
padding: 0.17vw 0.6vw 0.17vw 0.6vw;
|
||||
}
|
||||
|
||||
.option-child {
|
||||
cursor: pointer;
|
||||
|
||||
padding: 0.17vw 0.8vw 0.17vw 0.45vw;
|
||||
min-height: 1.7vw;
|
||||
border-radius: 0.2vw;
|
||||
font-size: 0.7vw;
|
||||
}
|
||||
|
||||
.option-child > p {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.option-child > p > .icon {
|
||||
font-size: 0.65vw;
|
||||
padding-top: 0.35vw;
|
||||
margin-left: 0.5vw;
|
||||
}
|
||||
|
||||
.option-child:hover {
|
||||
background-color: var(--black-two-opaque-color);
|
||||
}
|
||||
|
||||
/** ARRAY DROPDOWN - end **/
|
||||
</style>
|
@ -0,0 +1,21 @@
|
||||
<script>
|
||||
import { ENTITY_INFO } from "@store/entityInfo"
|
||||
import { fly } from "svelte/transition"
|
||||
</script>
|
||||
|
||||
<div class="w-screen h-screen flex items-center">
|
||||
<div class="w-[25vh] bg-primary flex flex-col gap-[2vh] rounded-[0.5vh] p-[2vh] ml-[2vh] font-medium" transition:fly={{ x: -100 }}>
|
||||
<div class="h-[2vh] w-full flex items-center gap-[1vh] text-[1.5vh]">
|
||||
<i class="fas fa-code"></i>
|
||||
<p>Entity Information</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Model: {$ENTITY_INFO?.name}</p>
|
||||
<p>Hash: {$ENTITY_INFO?.hash}</p>
|
||||
<br>
|
||||
<p>C - Copy Information</p>
|
||||
<p>E - Delete Entity</p>
|
||||
<p>ESC - Close</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
26
resources/[ps]/ps-adminmenu/ui/src/components/Header.svelte
Normal file
26
resources/[ps]/ps-adminmenu/ui/src/components/Header.svelte
Normal file
@ -0,0 +1,26 @@
|
||||
<script>
|
||||
import { MENU_WIDE } from '@store/stores'
|
||||
|
||||
export let title
|
||||
export let hasSearch = false
|
||||
export let hasLargeMenu = false
|
||||
export let onSearchInput = null
|
||||
export let search = null
|
||||
</script>
|
||||
|
||||
<p class="my-[2vh] font-medium text-[2vh]">{title}</p>
|
||||
|
||||
{#if hasSearch}
|
||||
<div
|
||||
class="w-full h-[4.5vh] rounded-[0.5vh] flex items-center justify-center gap-[1vh] bg-tertiary"
|
||||
>
|
||||
<i class="fas fa-magnifying-glass text-[1.5vh]" />
|
||||
<input
|
||||
on:input={onSearchInput}
|
||||
bind:value={search}
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
class="h-full px-[1vh] bg-transparent text-[1.7vh] {hasLargeMenu ? $MENU_WIDE ? 'w-[94%]' : 'w-[80%]' : 'w-[80%]'}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
@ -0,0 +1,5 @@
|
||||
<div class="fixed top-0 left-0 bottom-0 right-0 flex items-center justify-center bg-black bg-opacity-75">
|
||||
<div class=" bg-tertiary rounded-[0.5vh] flex flex-col px-[2vh] py-[1.5vh] gap-[0.8vh]">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,7 @@
|
||||
<div class="w-full h-full flex justify-center items-center opacity-50">
|
||||
<div
|
||||
class="inline-block h-[10rem] w-[10rem] animate-spin rounded-full border-8 border-solid border-secondary border-r-tertiary align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]"
|
||||
role="status">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,19 @@
|
||||
<script>
|
||||
import { TOGGLE_COORDS } from "@store/togglecoords"
|
||||
import { fly } from "svelte/transition"
|
||||
</script>
|
||||
|
||||
<div class="w-screen h-screen flex items-center">
|
||||
<div class="w-[25vh] bg-primary flex flex-col gap-[2vh] rounded-[0.5vh] p-[2vh] ml-[2vh] font-medium" transition:fly={{ x: -100 }}>
|
||||
<div class="h-[2vh] w-full flex items-center gap-[1vh] text-[1.5vh]">
|
||||
<i class="fas fa-code"></i>
|
||||
<p>Coords Information</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>X: {$TOGGLE_COORDS?.x}</p>
|
||||
<p>Y: {$TOGGLE_COORDS?.y}</p>
|
||||
<p>Z: {$TOGGLE_COORDS?.z}</p>
|
||||
<p>Heading: {$TOGGLE_COORDS?.heading}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,22 @@
|
||||
<script>
|
||||
import { VEHICLE_DEV } from "@store/vehicle_dev"
|
||||
import { fly } from "svelte/transition"
|
||||
</script>
|
||||
|
||||
<div class="w-screen h-screen flex items-center">
|
||||
<div class="w-[25vh] bg-primary flex flex-col gap-[2vh] rounded-[0.5vh] p-[2vh] ml-[2vh] font-medium" transition:fly={{ x: -100 }}>
|
||||
<div class="h-[2vh] w-full flex items-center gap-[1vh] text-[1.5vh]">
|
||||
<i class="fas fa-code"></i>
|
||||
<p>Vehicle Information</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Model: {$VEHICLE_DEV?.name}</p>
|
||||
<p>Hash: {$VEHICLE_DEV?.model}</p>
|
||||
<p>NetID: {$VEHICLE_DEV?.netID}</p>
|
||||
<p>Plate: {$VEHICLE_DEV?.plate}</p>
|
||||
<p>Fuel: {$VEHICLE_DEV?.fuel}</p>
|
||||
<p>Engine: {$VEHICLE_DEV?.engine_health}</p>
|
||||
<p>Body: {$VEHICLE_DEV?.body_health}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
30
resources/[ps]/ps-adminmenu/ui/src/layout/Main.svelte
Normal file
30
resources/[ps]/ps-adminmenu/ui/src/layout/Main.svelte
Normal file
@ -0,0 +1,30 @@
|
||||
<script>
|
||||
import { fly } from 'svelte/transition'
|
||||
import { MENU_WIDE, ACTIVE_PAGE } from '@store/stores'
|
||||
import Sidebar from './Sidebar/Sidebar.svelte'
|
||||
|
||||
import Actions from '@pages/Actions/Actions.svelte'
|
||||
import Server from '@pages/Server/Server.svelte'
|
||||
import StaffChat from '@pages/Chat/Chat.svelte'
|
||||
import Players from '@pages/Players/Players.svelte'
|
||||
import Commands from '@pages/Commands/Commands.svelte'
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="h-[85vh] flex rounded-[0.5vh] bg-primary {!$MENU_WIDE ? 'w-[40vh] mr-[5vh] ' : 'w-[106vh]'}" transition:fly={{ x: 100 }}
|
||||
>
|
||||
<Sidebar />
|
||||
<div class="h-full flex {!$MENU_WIDE ? 'w-[33vh]' : 'w-[99vh]'}">
|
||||
{#if $ACTIVE_PAGE == 'Actions'}
|
||||
<Actions />
|
||||
{:else if $ACTIVE_PAGE == 'Server'}
|
||||
<Server />
|
||||
{:else if $ACTIVE_PAGE == 'Staffchat'}
|
||||
<StaffChat />
|
||||
{:else if $ACTIVE_PAGE == 'Players'}
|
||||
<Players />
|
||||
{:else if $ACTIVE_PAGE == 'Commands'}
|
||||
<Commands />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,43 @@
|
||||
<script>
|
||||
import { ACTIVE_PAGE } from '@store/stores'
|
||||
|
||||
export let icon
|
||||
export let value
|
||||
export let tooltiptext
|
||||
</script>
|
||||
|
||||
<button
|
||||
title={tooltiptext}
|
||||
class="w-[4vh] h-[4vh] rounded-[0.5vh] hover:bg-tertiary {$ACTIVE_PAGE == value ? 'bg-tertiary' : ''}
|
||||
relative
|
||||
before:content-[attr(data-tip)]
|
||||
before:absolute
|
||||
before:-right-3 before:top-1/2
|
||||
before:w-max before:max-w-xs
|
||||
before:px-[1vh]
|
||||
before:py-[0.5vh]
|
||||
before:-translate-x-full before:-translate-y-1/2
|
||||
before:bg-tertiary before:text-white
|
||||
before:rounded-md before:opacity-0
|
||||
before:translate-all
|
||||
|
||||
after:absolute
|
||||
after:-right-3 after:top-1/2
|
||||
after:-translate-x-0 after:-translate-y-1/2
|
||||
after:h-0 after:w-0
|
||||
after:border-t-transparent
|
||||
after:border-l-transparent
|
||||
after:border-b-transparent
|
||||
after:border-r-tertiary
|
||||
after:opacity-0
|
||||
after:transition-all
|
||||
|
||||
hover:before:opacity-100 hover:after:opacity-100
|
||||
"
|
||||
data-tip={tooltiptext}
|
||||
on:click={() => {
|
||||
ACTIVE_PAGE.set(value)
|
||||
}}
|
||||
>
|
||||
<i class={icon} />
|
||||
</button>
|
@ -0,0 +1,74 @@
|
||||
<script>
|
||||
import { DEV_MODE, MENU_WIDE } from '@store/stores'
|
||||
import Button from './Components/Button.svelte'
|
||||
import { SendNUI } from '@utils/SendNUI'
|
||||
|
||||
let navigation = [
|
||||
{ value: 'Staffchat', icon: 'fas fa-message' },
|
||||
{ value: 'Players', icon: 'fas fa-users' },
|
||||
{ value: 'Server', icon: 'fas fa-server' },
|
||||
{ value: 'Commands', icon: 'fas fa-slash' },
|
||||
{ value: 'Actions', icon: 'fas fa-wand-magic-sparkles' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="w-[7vh] h-full flex flex-col gap-y-[1vh] items-center py-[1.4vh] border-r-[0.2vh] border-tertiary"
|
||||
>
|
||||
<div class="mb-auto">
|
||||
<button
|
||||
class="w-[4vh] h-[4vh] rounded-[0.5vh] hover:bg-tertiary"
|
||||
on:click={() => MENU_WIDE.update((wide) => !wide)}
|
||||
>
|
||||
<i
|
||||
class="fas"
|
||||
class:fa-angle-right={$MENU_WIDE}
|
||||
class:fa-angle-left={!$MENU_WIDE}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#each navigation as nav}
|
||||
<Button tooltiptext={nav.value} icon={nav.icon} value={nav.value} />
|
||||
{/each}
|
||||
|
||||
<button
|
||||
class="w-[4vh] h-[4vh] rounded-[0.5vh] hover:bg-tertiary {$DEV_MODE
|
||||
? 'text-accent'
|
||||
: ''}
|
||||
relative
|
||||
before:content-[attr(data-tip)]
|
||||
before:absolute
|
||||
before:-right-3 before:top-1/2
|
||||
before:w-max before:max-w-xs
|
||||
before:px-[1vh]
|
||||
before:py-[0.5vh]
|
||||
before:-translate-x-full before:-translate-y-1/2
|
||||
before:bg-tertiary before:text-white
|
||||
before:rounded-md before:opacity-0
|
||||
before:translate-all
|
||||
|
||||
after:absolute
|
||||
after:-right-3 after:top-1/2
|
||||
after:-translate-x-0 after:-translate-y-1/2
|
||||
after:h-0 after:w-0
|
||||
after:border-t-transparent
|
||||
after:border-l-transparent
|
||||
after:border-b-transparent
|
||||
after:border-r-tertiary
|
||||
after:opacity-0
|
||||
after:transition-all
|
||||
|
||||
hover:before:opacity-100 hover:after:opacity-100
|
||||
"
|
||||
data-tip="Dev Mode"
|
||||
on:click={() => {
|
||||
DEV_MODE.update((wide) => !wide)
|
||||
SendNUI('clickButton', {
|
||||
data: 'toggleDevmode',
|
||||
})
|
||||
}}
|
||||
>
|
||||
<i class="fas fa-code"></i>
|
||||
</button>
|
||||
</div>
|
8
resources/[ps]/ps-adminmenu/ui/src/main.ts
Normal file
8
resources/[ps]/ps-adminmenu/ui/src/main.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import App from './App.svelte'
|
||||
import './styles.css'
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById('app')
|
||||
})
|
||||
|
||||
export default app
|
@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { MENU_WIDE, searchActions } from '@store/stores'
|
||||
import { ACTION, ALL_ACTIONS } from '@store/actions'
|
||||
|
||||
import Header from '@components/Header.svelte'
|
||||
import Tabs from './components/Tabs.svelte'
|
||||
import Button from './components/Button.svelte'
|
||||
import Dropdown from './components/Dropdown.svelte'
|
||||
import ButtonState from '@pages/Server/components/ButtonState.svelte'
|
||||
|
||||
</script>
|
||||
|
||||
<div class="h-full w-[99vh] px-[2vh]">
|
||||
<Header
|
||||
title={'Actions'}
|
||||
hasSearch={true}
|
||||
hasLargeMenu={true}
|
||||
onSearchInput={event => $searchActions = event.target.value}
|
||||
search={$searchActions}
|
||||
/>
|
||||
<Tabs />
|
||||
<div class="w-full h-[77%] flex flex-col gap-[1vh] mt-[1vh] overflow-auto scroll-visble">
|
||||
{#if $ACTION}
|
||||
{#each Object.entries($ACTION)
|
||||
.filter(([actionKey, actionValue]) => {
|
||||
if ($ALL_ACTIONS) {
|
||||
return actionValue.label.toLowerCase().includes($searchActions.toLowerCase());
|
||||
} else {
|
||||
return localStorage.getItem(`favorite-${actionKey}`) === 'true';
|
||||
}
|
||||
})
|
||||
.sort(([aKey, aValue], [bKey, bValue]) =>
|
||||
aValue.label.localeCompare(bValue.label)
|
||||
) as [actionKey, actionValue]}
|
||||
{#if actionValue.dropdown}
|
||||
<Dropdown data={actionValue} id={actionKey} />
|
||||
{:else}
|
||||
<Button data={actionValue} id={actionKey} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,27 @@
|
||||
<script>
|
||||
import { SendNUI } from '@utils/SendNUI'
|
||||
import Favorite from './Favorite.svelte'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
export let data
|
||||
export let id
|
||||
|
||||
onMount(() => {
|
||||
// console.log("button", data)
|
||||
})
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="min-h-[4.5vh] w-full flex items-center px-[1.5vh] rounded-[0.5vh] bg-tertiary hover:bg-opacity-90"
|
||||
on:click={() => {
|
||||
// console.log(data.event)
|
||||
SendNUI('clickButton', {
|
||||
data: id,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div class="flex items-center gap-[1vh]">
|
||||
<Favorite data={id} />
|
||||
<p>{data.label}</p>
|
||||
</div>
|
||||
</button>
|
@ -0,0 +1,83 @@
|
||||
<script>
|
||||
import DropdownComponent from '@components/DropdownComponent.svelte'
|
||||
import { SendNUI } from '@utils/SendNUI'
|
||||
import { slide } from 'svelte/transition'
|
||||
import Autofill from '@components/Autofill.svelte'
|
||||
import Favorite from './Favorite.svelte'
|
||||
import Input from './Input.svelte'
|
||||
|
||||
export let data
|
||||
export let id
|
||||
|
||||
let dropdownActive
|
||||
|
||||
let selectedDataArray = {}
|
||||
|
||||
function SelectData(selectedData) {
|
||||
// console.log('selected', selectedData)
|
||||
selectedDataArray[selectedData.id] = selectedData
|
||||
}
|
||||
|
||||
function sendData(event, type) {
|
||||
if (event) {
|
||||
data.event = event
|
||||
data.type = type
|
||||
}
|
||||
// console.log('data', data)
|
||||
// console.log('event', data.event)
|
||||
// console.log('type', data.type)
|
||||
SendNUI('clickButton', {
|
||||
data: id,
|
||||
selectedData: selectedDataArray,
|
||||
})
|
||||
// console.log(selectedDataArray)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class=" bg-tertiary rounded-[0.5vh] {dropdownActive
|
||||
? ''
|
||||
: 'hover:bg-opacity-90'}"
|
||||
>
|
||||
<button
|
||||
class="w-full h-[4.5vh] flex items-center justify-between px-[1.5vh]"
|
||||
on:click={() => (dropdownActive = !dropdownActive)}
|
||||
>
|
||||
<div class="flex items-center gap-[1vh]">
|
||||
<Favorite data={id} />
|
||||
<p>{data.label}</p>
|
||||
</div>
|
||||
<i class="fas fa-angle-{dropdownActive ? 'down' : 'right'}" />
|
||||
</button>
|
||||
|
||||
{#if dropdownActive}
|
||||
<div
|
||||
class="w-full rounded-b-[1vh] p-[1.5vh] flex flex-col gap-[1vh] justify-start items-start"
|
||||
transition:slide={{ duration: 150 }}
|
||||
>
|
||||
{#if data.dropdown}
|
||||
{#each data.dropdown as i}
|
||||
{#if i.option === 'text'}
|
||||
<Input data={i} selectedData={SelectData} />
|
||||
{:else if i.option === 'dropdown'}
|
||||
<Autofill
|
||||
action={i}
|
||||
label_title={i.label}
|
||||
data={i.data}
|
||||
selectedData={SelectData}
|
||||
/>
|
||||
{:else if i.option === 'button'}
|
||||
<button
|
||||
class="h-[3.8vh] px-[1.5vh] rounded-[0.5vh] bg-secondary hover:bg-opacity-90 border-[0.1vh] border-primary"
|
||||
on:click={() => {
|
||||
sendData(i.event, i.type)
|
||||
}}
|
||||
>
|
||||
<p>{i.label}</p>
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
@ -0,0 +1,24 @@
|
||||
<script>
|
||||
import { onMount } from "svelte"
|
||||
|
||||
export let data
|
||||
|
||||
let favorite = localStorage.getItem(`favorite-${data}`) === 'true';
|
||||
|
||||
const toggleFavorite = () => {
|
||||
event.stopPropagation();
|
||||
favorite = !favorite;
|
||||
localStorage.setItem(`favorite-${data}`, favorite);
|
||||
// console.log(data, "is now", favorite ? "favorited" : "unfavorited");
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
// console.log(data)
|
||||
})
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="{favorite ? 'fas' : 'far'} fa-star"
|
||||
on:click={toggleFavorite}
|
||||
>
|
||||
</button>
|
@ -0,0 +1,30 @@
|
||||
<script>
|
||||
export let data
|
||||
export let selectedData
|
||||
|
||||
function selectData(label, value) {
|
||||
selectedData({
|
||||
label: label,
|
||||
value: value,
|
||||
id: label
|
||||
});
|
||||
}
|
||||
|
||||
let input = ""
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<div class="w-[22vh] flex flex-col bg-secondary rounded-[0.5vh] border-[0.1vh] border-primary">
|
||||
<div class="w-full h-[3.8vh] pl-[1vh] flex justify-between">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={data.label}
|
||||
bind:value={input}
|
||||
on:input={e => input = e.target.value}
|
||||
on:blur={() => selectData(data.label, input)}
|
||||
on:click={() => selectData(data.label, input)}
|
||||
class="h-full w-[90%] bg-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,24 @@
|
||||
<script>
|
||||
import { ALL_ACTIONS } from '@store/actions'
|
||||
</script>
|
||||
|
||||
<div class="mt-[1vh] w-full h-[4.5vh] flex gap-[1vh] font-medium">
|
||||
<button
|
||||
class="w-full h-full hover:bg-tertiary rounded-[0.5vh] border-[0.2vh] border-tertiary {$ALL_ACTIONS ? 'bg-tertiary' : ' '}"
|
||||
on:click={() => {
|
||||
ALL_ACTIONS.set(true)
|
||||
// console.log($ALL_ACTIONS)
|
||||
}}
|
||||
>
|
||||
All Actions
|
||||
</button>
|
||||
<button
|
||||
class="w-full h-full hover:bg-tertiary rounded-[0.5vh] border-[0.2vh] border-tertiary {!$ALL_ACTIONS ? 'bg-tertiary' : ' '}"
|
||||
on:click={() => {
|
||||
ALL_ACTIONS.set(false)
|
||||
// console.log($ALL_ACTIONS)
|
||||
}}
|
||||
>
|
||||
Favorites
|
||||
</button>
|
||||
</div>
|
15
resources/[ps]/ps-adminmenu/ui/src/pages/Chat/Chat.svelte
Normal file
15
resources/[ps]/ps-adminmenu/ui/src/pages/Chat/Chat.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
import Header from "@components/Header.svelte"
|
||||
import SendInput from "./components/SendInput.svelte"
|
||||
import RecieveInput from "./components/RecieveInput.svelte"
|
||||
|
||||
let search = "";
|
||||
</script>
|
||||
|
||||
<div class="h-full w-full px-[2vh] pb-[2vh] flex flex-col ">
|
||||
<Header title={"Staff Chat"}/>
|
||||
<div id='chatList' class="w-full h-[84%] overflow-auto">
|
||||
<RecieveInput />
|
||||
</div>
|
||||
<SendInput />
|
||||
</div>
|
@ -0,0 +1,40 @@
|
||||
<script>
|
||||
import { MENU_WIDE } from '@store/stores'
|
||||
import { Message, Messages } from '@store/staffchat'
|
||||
import { PLAYER, PLAYER_DATA } from "@store/players";
|
||||
import { timeAgo } from "@utils/timeAgo"
|
||||
import { SendNUI } from '@utils/SendNUI'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
function refreshMsg() {
|
||||
SendNUI("GetMessages");
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const RefreshMsgInterval = setInterval(() => {
|
||||
refreshMsg()
|
||||
}, 1000)
|
||||
return () => clearInterval(RefreshMsgInterval)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
||||
<div>
|
||||
{#if $Message && $Messages}
|
||||
{#each $Message as message}
|
||||
<div class="w-full flex flex-col text-[1.3vh] text-gray-400 {$PLAYER && $PLAYER_DATA.cid === message.citizenid ? "items-end" : "items-start"}">
|
||||
<div class="w-fit flex justify-between items-center text-gray-400 font-medium -mb-[0.5vh]">
|
||||
<p class="text-[1.2vh]">{message.fullname}</p>
|
||||
</div>
|
||||
<div class="w-fit max-w-[85%] mt-[0.5vh] p-[1vh] break-words text-start rounded-lg {$PLAYER && $PLAYER_DATA.cid === message.citizenid ? "bg-accent" : "bg-tertiary"}">
|
||||
<p>{message.message}</p>
|
||||
</div>
|
||||
<p class="text-[1vh] ml-[0.5vh]">{timeAgo(Number(message.time))}</p>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</div>
|
@ -0,0 +1,47 @@
|
||||
<script>
|
||||
import { MENU_WIDE } from '@store/stores'
|
||||
import { SendNUI } from '@utils/SendNUI'
|
||||
|
||||
let message = "";
|
||||
|
||||
function sendMessage() {
|
||||
if (!message.trim()) return;
|
||||
SendNUI("SendMessage", {
|
||||
message: message,
|
||||
});
|
||||
// console.log("Message sent", message)
|
||||
message = "";
|
||||
setTimeout(() => {
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
}
|
||||
function scrollToBottom() {
|
||||
let chatList = document.getElementById("chatList");
|
||||
chatList.scroll({
|
||||
top: chatList.scrollHeight,
|
||||
behavior: 'auto'
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="mt-auto w-full h-[4.5vh] rounded-[0.5vh] flex items-center justify-center gap-[1vh] bg-tertiary"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Your message here"
|
||||
on:keydown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
sendMessage();
|
||||
}
|
||||
}}
|
||||
bind:value={message}
|
||||
class="h-full px-[1vh] bg-transparent text-[1.7vh] {$MENU_WIDE ? 'w-[94%]' : 'w-[80%]'}"
|
||||
/>
|
||||
<button
|
||||
class="h-full w-[5vh] rounded-r-[0.5vh] hover:bg-secondary"
|
||||
on:click={sendMessage}
|
||||
>
|
||||
<i class="fas fa-paper-plane text-[1.5vh]" />
|
||||
</button>
|
||||
</div>
|
@ -0,0 +1,37 @@
|
||||
<script>
|
||||
import { MENU_WIDE } from '@store/stores'
|
||||
import { COMMANDS } from '@store/server'
|
||||
|
||||
import Header from '@components/Header.svelte'
|
||||
import CommandsCard from './components/CommandsCard.svelte'
|
||||
|
||||
let search = ''
|
||||
|
||||
let SortedCommands = $COMMANDS ? $COMMANDS.slice().sort((a, b) => a.name.localeCompare(b.name)) : []
|
||||
</script>
|
||||
|
||||
<div class="h-full w-[33vh] px-[2vh]">
|
||||
<Header
|
||||
title={'Commands'}
|
||||
hasSearch={true}
|
||||
onSearchInput={event => (search = event.target.value)}
|
||||
/>
|
||||
<div class="w-full h-[84%] flex flex-col gap-[1vh] mt-[1vh] overflow-auto">
|
||||
{#if $COMMANDS}
|
||||
{#if $COMMANDS && $COMMANDS.filter((commands) => commands.name.toLowerCase().includes(search.toLowerCase())).length === 0}
|
||||
<div class="text-tertiary text-center text-[1.7vh] font-medium mt-[1vh]">No Commands Found.</div>
|
||||
{:else}
|
||||
<small class="font-medium">Total Commands: {SortedCommands.length}</small>
|
||||
{#each SortedCommands.filter((commands) => commands.name.toLowerCase().includes(search.toLowerCase())) as commands}
|
||||
<CommandsCard label={commands.name} />
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $MENU_WIDE}
|
||||
<div class="h-full w-[66vh] border-l-[0.2vh] border-tertiary px-[2vh]">
|
||||
<Header title={'Dashboard'} />
|
||||
</div>
|
||||
{/if}
|
@ -0,0 +1,10 @@
|
||||
<script>
|
||||
export let label
|
||||
</script>
|
||||
|
||||
<button class="w-full flex justify-between rounded-[0.5vh] bg-tertiary items-center">
|
||||
<div class="flex items-center p-[2vh]">
|
||||
<i class="fas fa-angle-right mr-[1vh]"></i>
|
||||
<p class="text-[1.5vh]">{label ? label : ''}</p>
|
||||
</div>
|
||||
</button>
|
486
resources/[ps]/ps-adminmenu/ui/src/pages/Players/Players.svelte
Normal file
486
resources/[ps]/ps-adminmenu/ui/src/pages/Players/Players.svelte
Normal file
@ -0,0 +1,486 @@
|
||||
<script lang="ts">
|
||||
import { MENU_WIDE } from '@store/stores'
|
||||
import { PLAYER, PLAYER_VEHICLES, SELECTED_PLAYER } from '@store/players'
|
||||
import Header from '@components/Header.svelte'
|
||||
import Button from './components/Button.svelte'
|
||||
import { onMount } from 'svelte'
|
||||
import { SendNUI } from '@utils/SendNUI'
|
||||
import Spinner from '@components/Spinner.svelte'
|
||||
import Autofill from '@components/Autofill.svelte'
|
||||
import Modal from '@components/Modal.svelte'
|
||||
import Input from '@pages/Actions/components/Input.svelte'
|
||||
|
||||
let search = ''
|
||||
let loading = false
|
||||
let banPlayer = false
|
||||
let kickPlayer = false
|
||||
|
||||
onMount(async () => {
|
||||
loading = true
|
||||
const players = await SendNUI('getPlayers')
|
||||
PLAYER.set(players)
|
||||
loading = false
|
||||
})
|
||||
|
||||
let selectedDataArray = {}
|
||||
|
||||
function SelectData(selectedData) {
|
||||
// console.log('selected', selectedData)
|
||||
selectedDataArray[selectedData.id] = selectedData
|
||||
// console.log('selectedDataArray', selectedDataArray)
|
||||
}
|
||||
|
||||
let banData = [
|
||||
{ label: 'Permanent', value: '2147483647' },
|
||||
{ label: '10 Minutes', value: '600' },
|
||||
{ label: '30 Minutes', value: '1800' },
|
||||
{ label: '1 Hour', value: '3600' },
|
||||
{ label: '6 Hours', value: '21600' },
|
||||
{ label: '12 Hours', value: '43200' },
|
||||
{ label: '1 Day', value: '86400' },
|
||||
{ label: '3 Days', value: '259200' },
|
||||
{ label: '1 Week', value: '604800' },
|
||||
{ label: '3 Weeks', value: '1814400' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<div class="h-full w-[33vh] px-[2vh]">
|
||||
<Header
|
||||
title={'Players'}
|
||||
hasSearch={true}
|
||||
onSearchInput={(event) => (search = event.target.value)}
|
||||
/>
|
||||
<div class="w-full h-[84%] flex flex-col gap-[1vh] mt-[1vh] overflow-auto">
|
||||
{#if loading}
|
||||
<Spinner />
|
||||
{:else if $PLAYER}
|
||||
{#if $PLAYER && $PLAYER.filter((player) => player.name
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase())).length === 0}
|
||||
<div
|
||||
class="text-tertiary text-center text-[1.7vh] font-medium mt-[1vh]"
|
||||
>
|
||||
No Player Found.
|
||||
</div>
|
||||
{:else}
|
||||
{#each $PLAYER.filter((player) => player.name
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase())) as player}
|
||||
<Button {player} />
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $MENU_WIDE}
|
||||
<div class="h-full w-[66vh] border-l-[0.2vh] border-tertiary p-[2vh]">
|
||||
{#if !$SELECTED_PLAYER}
|
||||
<div
|
||||
class="h-full w-full flex flex-col items-center justify-center"
|
||||
>
|
||||
<div class="text-4xl text-tertiary">No Player Selected.</div>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-[2vh] font-medium">
|
||||
ID: {$SELECTED_PLAYER.id} - {$SELECTED_PLAYER.name}
|
||||
</p>
|
||||
<div class="w-full h-[96.5%] pt-[2vh] flex flex-col gap-[1vh]">
|
||||
<p class="font-medium text-[1.7vh]">Quick Actions</p>
|
||||
<div class="w-full bg-tertiary flex rounded-[0.5vh]">
|
||||
<button
|
||||
title="Kick Player"
|
||||
class="h-[4.5vh] w-full rounded-l-[0.5vh] hover:bg-secondary
|
||||
relative
|
||||
before:content-[attr(data-tip)]
|
||||
before:absolute
|
||||
before:px-3 before:py-2
|
||||
before:left-1/2 before:-top-3
|
||||
before:w-max before:max-w-xs
|
||||
before:-translate-x-1/2 before:-translate-y-full
|
||||
before:bg-tertiary before:text-white
|
||||
before:rounded-md before:opacity-0
|
||||
before:translate-all
|
||||
|
||||
after:absolute
|
||||
after:left-1/2 after:-top-3
|
||||
after:h-0 after:w-0
|
||||
after:-translate-x-1/2 after:border-8
|
||||
after:border-t-tertiary
|
||||
after:border-l-transparent
|
||||
after:border-b-transparent
|
||||
after:border-r-transparent
|
||||
after:opacity-0
|
||||
after:transition-all
|
||||
|
||||
hover:before:opacity-100 hover:after:opacity-100
|
||||
"
|
||||
data-tip="Kick Player"
|
||||
on:click={() => (kickPlayer = true)}
|
||||
>
|
||||
<i class="fas fa-user-minus"></i>
|
||||
</button>
|
||||
<button
|
||||
title="Ban Player"
|
||||
class="h-[4.5vh] w-full hover:bg-secondary
|
||||
relative
|
||||
before:content-[attr(data-tip)]
|
||||
before:absolute
|
||||
before:px-3 before:py-2
|
||||
before:left-1/2 before:-top-3
|
||||
before:w-max before:max-w-xs
|
||||
before:-translate-x-1/2 before:-translate-y-full
|
||||
before:bg-tertiary before:text-white
|
||||
before:rounded-md before:opacity-0
|
||||
before:translate-all
|
||||
|
||||
after:absolute
|
||||
after:left-1/2 after:-top-3
|
||||
after:h-0 after:w-0
|
||||
after:-translate-x-1/2 after:border-8
|
||||
after:border-t-tertiary
|
||||
after:border-l-transparent
|
||||
after:border-b-transparent
|
||||
after:border-r-transparent
|
||||
after:opacity-0
|
||||
after:transition-all
|
||||
|
||||
hover:before:opacity-100 hover:after:opacity-100
|
||||
"
|
||||
data-tip="Ban Player"
|
||||
on:click={() => (banPlayer = true)}
|
||||
>
|
||||
<i class="fas fa-ban"></i>
|
||||
</button>
|
||||
<button
|
||||
title="Teleport To Player"
|
||||
class="h-[4.5vh] w-full hover:bg-secondary
|
||||
relative
|
||||
before:content-[attr(data-tip)]
|
||||
before:absolute
|
||||
before:px-3 before:py-2
|
||||
before:left-1/2 before:-top-3
|
||||
before:w-max before:max-w-xs
|
||||
before:-translate-x-1/2 before:-translate-y-full
|
||||
before:bg-tertiary before:text-white
|
||||
before:rounded-md before:opacity-0
|
||||
before:translate-all
|
||||
|
||||
after:absolute
|
||||
after:left-1/2 after:-top-3
|
||||
after:h-0 after:w-0
|
||||
after:-translate-x-1/2 after:border-8
|
||||
after:border-t-tertiary
|
||||
after:border-l-transparent
|
||||
after:border-b-transparent
|
||||
after:border-r-transparent
|
||||
after:opacity-0
|
||||
after:transition-all
|
||||
|
||||
hover:before:opacity-100 hover:after:opacity-100
|
||||
"
|
||||
data-tip="Teleport To Player"
|
||||
on:click={() =>
|
||||
SendNUI('clickButton', {
|
||||
data: 'teleportToPlayer',
|
||||
selectedData: {
|
||||
['Player']: {
|
||||
value: $SELECTED_PLAYER.id,
|
||||
},
|
||||
},
|
||||
})}
|
||||
>
|
||||
<i class="fas fa-person-walking-arrow-right"></i>
|
||||
</button>
|
||||
<button
|
||||
title="Bring Player"
|
||||
class="h-[4.5vh] w-full hover:bg-secondary
|
||||
relative
|
||||
before:content-[attr(data-tip)]
|
||||
before:absolute
|
||||
before:px-3 before:py-2
|
||||
before:left-1/2 before:-top-3
|
||||
before:w-max before:max-w-xs
|
||||
before:-translate-x-1/2 before:-translate-y-full
|
||||
before:bg-tertiary before:text-white
|
||||
before:rounded-md before:opacity-0
|
||||
before:translate-all
|
||||
|
||||
after:absolute
|
||||
after:left-1/2 after:-top-3
|
||||
after:h-0 after:w-0
|
||||
after:-translate-x-1/2 after:border-8
|
||||
after:border-t-tertiary
|
||||
after:border-l-transparent
|
||||
after:border-b-transparent
|
||||
after:border-r-transparent
|
||||
after:opacity-0
|
||||
after:transition-all
|
||||
|
||||
hover:before:opacity-100 hover:after:opacity-100
|
||||
"
|
||||
data-tip="Bring Player"
|
||||
on:click={() =>
|
||||
SendNUI('clickButton', {
|
||||
data: 'bringPlayer',
|
||||
selectedData: {
|
||||
['Player']: {
|
||||
value: $SELECTED_PLAYER.id,
|
||||
},
|
||||
},
|
||||
})}
|
||||
>
|
||||
<i class="fas fa-person-walking-arrow-loop-left"></i>
|
||||
</button>
|
||||
<button
|
||||
title="Revive Player"
|
||||
class="h-[4.5vh] w-full hover:bg-secondary
|
||||
relative
|
||||
before:content-[attr(data-tip)]
|
||||
before:absolute
|
||||
before:px-3 before:py-2
|
||||
before:left-1/2 before:-top-3
|
||||
before:w-max before:max-w-xs
|
||||
before:-translate-x-1/2 before:-translate-y-full
|
||||
before:bg-tertiary before:text-white
|
||||
before:rounded-md before:opacity-0
|
||||
before:translate-all
|
||||
|
||||
after:absolute
|
||||
after:left-1/2 after:-top-3
|
||||
after:h-0 after:w-0
|
||||
after:-translate-x-1/2 after:border-8
|
||||
after:border-t-tertiary
|
||||
after:border-l-transparent
|
||||
after:border-b-transparent
|
||||
after:border-r-transparent
|
||||
after:opacity-0
|
||||
after:transition-all
|
||||
|
||||
hover:before:opacity-100 hover:after:opacity-100
|
||||
"
|
||||
data-tip="Revive Player"
|
||||
on:click={() =>
|
||||
SendNUI('clickButton', {
|
||||
data: 'revivePlayer',
|
||||
selectedData: {
|
||||
['Player']: {
|
||||
value: $SELECTED_PLAYER.id,
|
||||
},
|
||||
},
|
||||
})}
|
||||
>
|
||||
<i class="fas fa-heart-pulse"></i>
|
||||
</button>
|
||||
<button
|
||||
title="Spectate Player"
|
||||
class="h-[4.5vh] w-full hover:bg-secondary
|
||||
relative
|
||||
before:content-[attr(data-tip)]
|
||||
before:absolute
|
||||
before:px-3 before:py-2
|
||||
before:left-1/2 before:-top-3
|
||||
before:w-max before:max-w-xs
|
||||
before:-translate-x-1/2 before:-translate-y-full
|
||||
before:bg-tertiary before:text-white
|
||||
before:rounded-md before:opacity-0
|
||||
before:translate-all
|
||||
|
||||
after:absolute
|
||||
after:left-1/2 after:-top-3
|
||||
after:h-0 after:w-0
|
||||
after:-translate-x-1/2 after:border-8
|
||||
after:border-t-tertiary
|
||||
after:border-l-transparent
|
||||
after:border-b-transparent
|
||||
after:border-r-transparent
|
||||
after:opacity-0
|
||||
after:transition-all
|
||||
|
||||
hover:before:opacity-100 hover:after:opacity-100
|
||||
"
|
||||
data-tip="Spectate Player"
|
||||
on:click={() =>
|
||||
SendNUI('clickButton', {
|
||||
data: 'spectate_player',
|
||||
selectedData: {
|
||||
['Player']: {
|
||||
value: $SELECTED_PLAYER.id,
|
||||
},
|
||||
},
|
||||
})}
|
||||
>
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="h-[90%] overflow-auto flex flex-col gap-[1vh] select-text"
|
||||
>
|
||||
<p class="font-medium text-[1.7vh]">Licenses</p>
|
||||
<div
|
||||
class="w-full bg-tertiary rounded-[0.5vh] p-[1.5vh] text-[1.5vh]"
|
||||
>
|
||||
<p>
|
||||
{$SELECTED_PLAYER.discord.replace(
|
||||
'discord:',
|
||||
'Discord: ',
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{$SELECTED_PLAYER.license.replace(
|
||||
'license:',
|
||||
'License: ',
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{$SELECTED_PLAYER.fivem
|
||||
? $SELECTED_PLAYER.fivem
|
||||
: ''}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{$SELECTED_PLAYER.steam
|
||||
? $SELECTED_PLAYER.steam
|
||||
: ''}
|
||||
</p>
|
||||
</div>
|
||||
<p class="font-medium text-[1.7vh]">Information</p>
|
||||
<div
|
||||
class="w-full bg-tertiary rounded-[0.5vh] p-[1.5vh] text-[1.5vh]"
|
||||
>
|
||||
<p>CID: {$SELECTED_PLAYER.cid}</p>
|
||||
<p>Name: {$SELECTED_PLAYER.name}</p>
|
||||
<p>Job: {$SELECTED_PLAYER.job}</p>
|
||||
<p>Cash: ${$SELECTED_PLAYER.cash}</p>
|
||||
<p>Bank: ${$SELECTED_PLAYER.bank}</p>
|
||||
<p>Phone: {$SELECTED_PLAYER.phone}</p>
|
||||
</div>
|
||||
<p class="font-medium text-[1.7vh]">Vehicles</p>
|
||||
{#each $SELECTED_PLAYER.vehicles as vehicle}
|
||||
<div
|
||||
class="w-full bg-tertiary flex flex-row rounded-[0.5vh] p-[1.5vh] text-[1.5vh]"
|
||||
>
|
||||
<div>
|
||||
<p class=" font-medium text-[1.7vh]">
|
||||
{vehicle.label}
|
||||
</p>
|
||||
<p>Plate: {vehicle.plate}</p>
|
||||
</div>
|
||||
<div class="ml-auto h-full flex items-center">
|
||||
<button
|
||||
class="bg-secondary px-[1vh] py-[0.5vh] rounded-[0.5vh] border border-primary"
|
||||
on:click={() =>
|
||||
SendNUI('clickButton', {
|
||||
data: 'spawnPersonalVehicle',
|
||||
selectedData: {
|
||||
['VehiclePlate']: {
|
||||
value: vehicle.plate,
|
||||
},
|
||||
},
|
||||
})}
|
||||
>
|
||||
Spawn
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if banPlayer}
|
||||
<Modal>
|
||||
<div class="flex justify-between">
|
||||
<p class="font-medium text-[1.8vh]">Ban {$SELECTED_PLAYER.name}</p>
|
||||
<button
|
||||
class="hover:text-accent"
|
||||
on:click={() => (banPlayer = false)}
|
||||
>
|
||||
<i class="fas fa-xmark"></i>
|
||||
</button>
|
||||
</div>
|
||||
<Input
|
||||
data={{
|
||||
label: 'Reason',
|
||||
value: 'reason',
|
||||
id: 'reason',
|
||||
}}
|
||||
selectedData={SelectData}
|
||||
/>
|
||||
<Autofill
|
||||
action={{
|
||||
label: 'Duration',
|
||||
value: 'duration',
|
||||
id: 'duration',
|
||||
}}
|
||||
label_title="Duration"
|
||||
data={banData}
|
||||
selectedData={SelectData}
|
||||
/>
|
||||
<button
|
||||
class="h-[3.8vh] px-[1.5vh] rounded-[0.5vh] bg-secondary hover:bg-opacity-90 border-[0.1vh] border-primary"
|
||||
on:click={() => {
|
||||
// console.log('Time: ', selectedDataArray['Duration'].value)
|
||||
// console.log('reason: ', selectedDataArray['Reason'].value)
|
||||
SendNUI('clickButton', {
|
||||
data: 'banPlayer',
|
||||
selectedData: {
|
||||
['Player']: {
|
||||
value: $SELECTED_PLAYER.id,
|
||||
},
|
||||
['Duration']: {
|
||||
value: selectedDataArray['Duration'].value,
|
||||
},
|
||||
['Reason']: {
|
||||
value: selectedDataArray['Reason'].value,
|
||||
},
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
<p>Ban</p>
|
||||
</button>
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
{#if kickPlayer}
|
||||
<Modal>
|
||||
<div class="flex justify-between">
|
||||
<p class="font-medium text-[1.8vh]">Kick {$SELECTED_PLAYER.name}</p>
|
||||
<button
|
||||
class="hover:text-accent"
|
||||
on:click={() => (kickPlayer = false)}
|
||||
>
|
||||
<i class="fas fa-xmark"></i>
|
||||
</button>
|
||||
</div>
|
||||
<Input
|
||||
data={{
|
||||
label: 'Reason',
|
||||
value: 'reason',
|
||||
id: 'reason',
|
||||
}}
|
||||
selectedData={SelectData}
|
||||
/>
|
||||
<button
|
||||
class="h-[3.8vh] px-[1.5vh] rounded-[0.5vh] bg-secondary hover:bg-opacity-90 border-[0.1vh] border-primary"
|
||||
on:click={() => {
|
||||
SendNUI('clickButton', {
|
||||
data: 'kickPlayer',
|
||||
selectedData: {
|
||||
['Player']: {
|
||||
value: $SELECTED_PLAYER.id,
|
||||
},
|
||||
['Reason']: {
|
||||
value: $SELECTED_PLAYER.id,
|
||||
},
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
<p>Kick</p>
|
||||
</button>
|
||||
</Modal>
|
||||
{/if}
|
@ -0,0 +1,26 @@
|
||||
<script>
|
||||
import { PLAYER_VEHICLES, SELECTED_PLAYER } from '@store/players'
|
||||
import { MENU_WIDE } from '@store/stores'
|
||||
import { SendNUI } from '@utils/SendNUI'
|
||||
|
||||
export let player
|
||||
|
||||
async function SelectPlayer(player) {
|
||||
SELECTED_PLAYER.set(player)
|
||||
MENU_WIDE.set(true)
|
||||
const vehicles = await SendNUI('getVehicle', { cid: $SELECTED_PLAYER.cid });
|
||||
PLAYER_VEHICLES.set(vehicles);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="h-[4.5vh] w-full flex items-center px-[1.5vh] rounded-[0.5vh] bg-tertiary hover:bg-opacity-90"
|
||||
on:click={() => {
|
||||
SelectPlayer(player)
|
||||
}}
|
||||
>
|
||||
<div class="w-full flex items-center justify-between gap-[1vh]">
|
||||
<p>{player.id} - {player.name}</p>
|
||||
<i class="fas fa-angle-right" />
|
||||
</div>
|
||||
</button>
|
@ -0,0 +1,42 @@
|
||||
<script>
|
||||
import { MENU_WIDE } from '@store/stores'
|
||||
import { RESOURCE } from '@store/server'
|
||||
|
||||
import Header from '@components/Header.svelte'
|
||||
import ResourceCard from './components/ResourceCard.svelte'
|
||||
|
||||
let search = ''
|
||||
|
||||
let SortedResources = $RESOURCE ? $RESOURCE.slice().sort((a, b) => a.name.localeCompare(b.name)) : []
|
||||
</script>
|
||||
|
||||
<div class="h-full w-[33vh] px-[2vh]">
|
||||
<Header
|
||||
title={'Server'}
|
||||
hasSearch={true}
|
||||
onSearchInput={event => (search = event.target.value)}
|
||||
/>
|
||||
<div class="w-full h-[84%] flex flex-col gap-[1vh] mt-[1vh] overflow-auto">
|
||||
{#if $RESOURCE}
|
||||
{#if $RESOURCE && $RESOURCE.filter((resource) => resource.name.toLowerCase().includes(search.toLowerCase())).length === 0}
|
||||
<div class="text-tertiary text-center text-[1.7vh] font-medium mt-[1vh]">No Resource Found.</div>
|
||||
{:else}
|
||||
{#each SortedResources.filter((resource) => resource.name.toLowerCase().includes(search.toLowerCase())) as resource}
|
||||
<ResourceCard
|
||||
label={resource.name}
|
||||
version={resource.version}
|
||||
author={resource.author}
|
||||
description={resource.description}
|
||||
state={resource.resourceState}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $MENU_WIDE}
|
||||
<div class="h-full w-[66vh] border-l-[0.2vh] border-tertiary px-[2vh]">
|
||||
<Header title={'Dashboard'} />
|
||||
</div>
|
||||
{/if}
|
@ -0,0 +1,21 @@
|
||||
<script>
|
||||
import { RESOURCE } from '@store/server'
|
||||
import { SendNUI } from '@utils/SendNUI'
|
||||
|
||||
export let resource
|
||||
export let icon
|
||||
export let state
|
||||
|
||||
async function changeState() {
|
||||
event.stopPropagation();
|
||||
const data = await SendNUI('setResourceState', { name: resource, state: state });
|
||||
RESOURCE.set(data);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="w-[3vh] h-[3vh] rounded-[0.5vh] bg-secondary hover:bg-primary"
|
||||
on:click={changeState}
|
||||
>
|
||||
<i class={icon} />
|
||||
</button>
|
@ -0,0 +1,38 @@
|
||||
<script>
|
||||
import { slide } from 'svelte/transition'
|
||||
import ButtonState from './ButtonState.svelte'
|
||||
|
||||
export let label
|
||||
export let version
|
||||
export let author
|
||||
export let description
|
||||
|
||||
export let state
|
||||
|
||||
let dropdownActive
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="w-full flex justify-between rounded-[0.5vh] bg-tertiary items-center"
|
||||
on:click={() => (dropdownActive = !dropdownActive)}
|
||||
>
|
||||
<div class="h-full p-[2vh] -mr-[8vh] flex flex-col items-start text-start">
|
||||
<p class="text-[1.8vh] font-medium">{label ? label : ''}</p>
|
||||
<p class="text-gray-400">{version ? 'Version: ' + version : ''}</p>
|
||||
<p class="text-gray-400">{author ? 'Author: ' + author : ''}</p>
|
||||
{#if dropdownActive}
|
||||
<div transition:slide={{ duration: 150 }}>
|
||||
<p class="text-gray-400">{description ? description : ''}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-[1vh] h-full py-[1.8vh] pr-[1.8vh]">
|
||||
{#if state == 'started'}
|
||||
<ButtonState icon="fas fa-stop" resource={label} state={'stop'} />
|
||||
<ButtonState icon="fas fa-arrows-rotate" resource={label} state={'restart'} />
|
||||
{:else}
|
||||
<ButtonState icon="fas fa-play" resource={label} state={'start'} />
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
@ -0,0 +1,68 @@
|
||||
<script lang="ts">
|
||||
import { ACTION } from '@store/actions'
|
||||
import { ITEM_DATA, VEHICLE_DATA, JOB_DATA, GANG_DATA, LOCATION_DATA, PED_LIST } from '@store/data'
|
||||
import { PLAYER, PLAYER_DATA } from '@store/players'
|
||||
import { RESOURCE, RESOURCES, COMMANDS } from '@store/server'
|
||||
import { VEHICLE_DEV } from '@store/vehicle_dev'
|
||||
import { TOGGLE_COORDS } from '@store/togglecoords'
|
||||
import { Message, Messages } from "@store/staffchat";
|
||||
import { ReceiveNUI } from '@utils/ReceiveNUI'
|
||||
import { debugData } from '@utils/debugData'
|
||||
import { ENTITY_INFO } from '@store/entityInfo'
|
||||
|
||||
|
||||
debugData([
|
||||
{
|
||||
action: 'setVisible',
|
||||
data: true,
|
||||
},
|
||||
])
|
||||
|
||||
debugData([
|
||||
{
|
||||
action: 'setBrowserMode',
|
||||
data: true
|
||||
},
|
||||
])
|
||||
|
||||
ReceiveNUI('setupUI', (data: any) => {
|
||||
$ACTION = data.actions
|
||||
$RESOURCE = data.resources
|
||||
$PLAYER_DATA = data.playerData
|
||||
$COMMANDS = data.commands
|
||||
})
|
||||
|
||||
ReceiveNUI('setResourceData', (data: any) => {
|
||||
$RESOURCE = data
|
||||
})
|
||||
|
||||
ReceiveNUI('setPlayersData', (data: any) => {
|
||||
$PLAYER = data
|
||||
})
|
||||
|
||||
ReceiveNUI('data', (data: any) => {
|
||||
$VEHICLE_DATA = data.vehicles
|
||||
$ITEM_DATA = data.items
|
||||
$JOB_DATA = data.jobs
|
||||
$GANG_DATA = data.gangs
|
||||
$LOCATION_DATA = data.locations
|
||||
$PED_LIST = data.pedlist
|
||||
})
|
||||
|
||||
ReceiveNUI('showVehicleMenu', (data: any) => {
|
||||
$VEHICLE_DEV = data
|
||||
})
|
||||
|
||||
ReceiveNUI('showCoordsMenu', (data: any) => {
|
||||
$TOGGLE_COORDS = data
|
||||
})
|
||||
|
||||
ReceiveNUI('showEntityInfo', (data: any) => {
|
||||
$ENTITY_INFO = data
|
||||
})
|
||||
ReceiveNUI('setMessages', (data: any) => {
|
||||
Message.set(data)
|
||||
Messages.set($Message[0])
|
||||
});
|
||||
|
||||
</script>
|
192
resources/[ps]/ps-adminmenu/ui/src/providers/BackdropFix.svelte
Normal file
192
resources/[ps]/ps-adminmenu/ui/src/providers/BackdropFix.svelte
Normal file
@ -0,0 +1,192 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
|
||||
const vertexShaders = `
|
||||
attribute vec2 a_position;
|
||||
attribute vec2 a_texcoord;
|
||||
uniform mat3 u_matrix;
|
||||
varying vec2 textureCoordinate;
|
||||
void main() {
|
||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
textureCoordinate = a_texcoord;
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShaderSource = `
|
||||
varying highp vec2 textureCoordinate;
|
||||
uniform sampler2D external_texture;
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(external_texture, textureCoordinate);
|
||||
}
|
||||
`;
|
||||
|
||||
const makeShader = (gl: WebGLRenderingContext, type: number, src: string) => {
|
||||
const shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
const infoLog = gl.getShaderInfoLog(shader);
|
||||
|
||||
if (infoLog) {
|
||||
console.error(infoLog);
|
||||
}
|
||||
|
||||
return shader;
|
||||
};
|
||||
|
||||
const createTexture = (gl: WebGLRenderingContext) => {
|
||||
const tex = gl.createTexture();
|
||||
|
||||
const texPixels = new Uint8Array([0, 0, 255, 255]);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex);
|
||||
gl.texImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
gl.RGBA,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
texPixels
|
||||
);
|
||||
|
||||
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
|
||||
// Magic hook sequence
|
||||
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
|
||||
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
||||
|
||||
// Reset
|
||||
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
|
||||
return tex;
|
||||
};
|
||||
|
||||
const createBuffers = (gl: WebGLRenderingContext) => {
|
||||
const vertexBuff = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuff);
|
||||
gl.bufferData(
|
||||
gl.ARRAY_BUFFER,
|
||||
new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
|
||||
gl.STATIC_DRAW
|
||||
);
|
||||
|
||||
const texBuff = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, texBuff);
|
||||
gl.bufferData(
|
||||
gl.ARRAY_BUFFER,
|
||||
new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]),
|
||||
gl.STATIC_DRAW
|
||||
);
|
||||
|
||||
return { vertexBuff, texBuff };
|
||||
};
|
||||
|
||||
const createProgram = (gl: WebGLRenderingContext) => {
|
||||
const vertexShader = makeShader(gl, gl.VERTEX_SHADER, vertexShaders);
|
||||
const fragmentShader = makeShader(
|
||||
gl,
|
||||
gl.FRAGMENT_SHADER,
|
||||
fragmentShaderSource
|
||||
);
|
||||
|
||||
const program = gl.createProgram();
|
||||
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
gl.useProgram(program);
|
||||
|
||||
const vloc = gl.getAttribLocation(program, "a_position");
|
||||
const tloc = gl.getAttribLocation(program, "a_texcoord");
|
||||
|
||||
return { program, vloc, tloc };
|
||||
};
|
||||
|
||||
const createGameView = (canvas: HTMLCanvasElement) => {
|
||||
// console.log(canvas);
|
||||
const gl = canvas.getContext("webgl", {
|
||||
antialias: false,
|
||||
depth: false,
|
||||
stencil: false,
|
||||
alpha: false,
|
||||
desynchronized: true,
|
||||
failIfMajorPerformanceCaveat: false,
|
||||
}) as WebGLRenderingContext;
|
||||
|
||||
let render = () => {};
|
||||
|
||||
function createStuff() {
|
||||
const tex = createTexture(gl);
|
||||
const { program, vloc, tloc } = createProgram(gl);
|
||||
const { vertexBuff, texBuff } = createBuffers(gl);
|
||||
|
||||
gl.useProgram(program);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex);
|
||||
|
||||
gl.uniform1i(gl.getUniformLocation(program, "external_texture"), 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuff);
|
||||
gl.vertexAttribPointer(vloc, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.enableVertexAttribArray(vloc);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, texBuff);
|
||||
gl.vertexAttribPointer(tloc, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.enableVertexAttribArray(tloc);
|
||||
|
||||
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
const gameView = {
|
||||
canvas,
|
||||
gl,
|
||||
animationFrame: void 0,
|
||||
resize: (width: number, height: number) => {
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.canvas.width = width;
|
||||
gl.canvas.height = height;
|
||||
},
|
||||
};
|
||||
|
||||
render = () => {
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
gl.finish();
|
||||
|
||||
gameView.animationFrame = requestAnimationFrame(render);
|
||||
};
|
||||
|
||||
createStuff();
|
||||
|
||||
return gameView;
|
||||
};
|
||||
|
||||
let canvasRef: HTMLCanvasElement;
|
||||
|
||||
onMount(() => {
|
||||
createGameView(canvasRef);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<canvas
|
||||
bind:this={canvasRef}
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
452
resources/[ps]/ps-adminmenu/ui/src/providers/DebugBrowser.svelte
Normal file
452
resources/[ps]/ps-adminmenu/ui/src/providers/DebugBrowser.svelte
Normal file
@ -0,0 +1,452 @@
|
||||
<script>
|
||||
import { debugData } from '../utils/debugData';
|
||||
|
||||
let show = false;
|
||||
//EXAMPLE
|
||||
|
||||
const debugActions = [
|
||||
{
|
||||
id: "admin_car",
|
||||
label: "Admin Car",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
event: "ps-adminmenu:client:admincar",
|
||||
},
|
||||
{
|
||||
id: "ban_player",
|
||||
label: "Ban Player",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
dropdown: [
|
||||
{ label: "Player", option: "dropdown", data: "players" },
|
||||
{ label: "Reason", option: "text" },
|
||||
{ label: "Time", option: "dropdown",
|
||||
data: [
|
||||
{ label: "1 time", value: "1000" },
|
||||
{ label: "2 time", value: "2000" },
|
||||
{ label: "3 time", value: "3000" },
|
||||
],
|
||||
},
|
||||
{ label: "Ban", type: "server", option: "button", event: "ps-adminmenu:client:banplayer" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "noclip",
|
||||
label: "Noclip",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
event: "ps-adminmenu:client:noclip",
|
||||
},
|
||||
{
|
||||
id: "invisible",
|
||||
label: "Invisible",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
event: "ps-adminmenu:client:invisible",
|
||||
},
|
||||
{
|
||||
id: "kick_player",
|
||||
label: "Kick Player",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
event: "ps-adminmenu:client:kickplayer",
|
||||
dropdown: [
|
||||
{ label: "Player", option: "dropdown", data: "players" },
|
||||
{ label: "Reason", option: "text" },
|
||||
{ label: "Time", option: "dropdown",
|
||||
data: [
|
||||
{ label: "1 time", value: "1000" },
|
||||
{ label: "2 time", value: "2000" },
|
||||
{ label: "3 time", value: "3000" },
|
||||
],
|
||||
},
|
||||
{ label: "Ban", type: "server", option: "button", event: "ps-adminmenu:client:banplayer" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "spawn_vehicle",
|
||||
label: "Spawn Vehicle",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
dropdown: [
|
||||
{ label: "Player", option: "dropdown", data: "players" },
|
||||
{ label: "Reason", option: "text" },
|
||||
{ label: "Time", option: "dropdown",
|
||||
data: [
|
||||
{ label: "1 time", value: "1000" },
|
||||
{ label: "2 time", value: "2000" },
|
||||
{ label: "3 time", value: "3000" },
|
||||
],
|
||||
},
|
||||
{ label: "Ban", type: "server", option: "button", event: "ps-adminmenu:client:banplayer" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "spawn_vehicle",
|
||||
label: "Spawn Vehicle",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
dropdown: [
|
||||
{ label: "Player", option: "dropdown", data: "players" },
|
||||
{ label: "Reason", option: "text" },
|
||||
{ label: "Time", option: "dropdown",
|
||||
data: [
|
||||
{ label: "1 time", value: "1000" },
|
||||
{ label: "2 time", value: "2000" },
|
||||
{ label: "3 time", value: "3000" },
|
||||
],
|
||||
},
|
||||
{ label: "Ban", type: "server", option: "button", event: "ps-adminmenu:client:banplayer" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "spawn_vehicle",
|
||||
label: "Spawn Vehicle",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
dropdown: [
|
||||
{ label: "Player", option: "dropdown", data: "players" },
|
||||
{ label: "Reason", option: "text" },
|
||||
{ label: "Time", option: "dropdown",
|
||||
data: [
|
||||
{ label: "1 time", value: "1000" },
|
||||
{ label: "2 time", value: "2000" },
|
||||
{ label: "3 time", value: "3000" },
|
||||
],
|
||||
},
|
||||
{ label: "Ban", type: "server", option: "button", event: "ps-adminmenu:client:banplayer" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "spawn_vehicle",
|
||||
label: "Spawn Vehicle",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
dropdown: [
|
||||
{ label: "Player", option: "dropdown", data: "players" },
|
||||
{ label: "Reason", option: "text" },
|
||||
{ label: "Time", option: "dropdown",
|
||||
data: [
|
||||
{ label: "1 time", value: "1000" },
|
||||
{ label: "2 time", value: "2000" },
|
||||
{ label: "3 time", value: "3000" },
|
||||
],
|
||||
},
|
||||
{ label: "Ban", type: "server", option: "button", event: "ps-adminmenu:client:banplayer" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "spawn_vehicle",
|
||||
label: "Spawn Vehicle",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
dropdown: [
|
||||
{ label: "Player", option: "dropdown", data: "players" },
|
||||
{ label: "Reason", option: "text" },
|
||||
{ label: "Time", option: "dropdown",
|
||||
data: [
|
||||
{ label: "1 time", value: "1000" },
|
||||
{ label: "2 time", value: "2000" },
|
||||
{ label: "3 time", value: "3000" },
|
||||
],
|
||||
},
|
||||
{ label: "Ban", type: "server", option: "button", event: "ps-adminmenu:client:banplayer" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "spawn_vehicle",
|
||||
label: "Spawn Vehicle",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
dropdown: [
|
||||
{ label: "Player", option: "dropdown", data: "players" },
|
||||
{ label: "Reason", option: "text" },
|
||||
{ label: "Time", option: "dropdown",
|
||||
data: [
|
||||
{ label: "1 time", value: "1000" },
|
||||
{ label: "2 time", value: "2000" },
|
||||
{ label: "3 time", value: "3000" },
|
||||
],
|
||||
},
|
||||
{ label: "Ban", type: "server", option: "button", event: "ps-adminmenu:client:banplayer" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "spawn_vehicle",
|
||||
label: "Spawn Vehicle",
|
||||
type: "client",
|
||||
perms: "mod",
|
||||
dropdown: [
|
||||
{ label: "Player", option: "dropdown", data: "players" },
|
||||
{ label: "Reason", option: "text" },
|
||||
{ label: "Time", option: "dropdown",
|
||||
data: [
|
||||
{ label: "1 time", value: "1000" },
|
||||
{ label: "2 time", value: "2000" },
|
||||
{ label: "3 time", value: "3000" },
|
||||
],
|
||||
},
|
||||
{ label: "Ban", type: "server", option: "button", event: "ps-adminmenu:client:banplayer" },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const debugResources = [
|
||||
{
|
||||
name: "ps-adminmenu",
|
||||
resourceState: "started",
|
||||
},
|
||||
{
|
||||
name: "ps-mdt",
|
||||
version: "1.0.0",
|
||||
description: "A cool mdt",
|
||||
author: "Project Sloth",
|
||||
resourceState: "started",
|
||||
},
|
||||
{
|
||||
name: "ps-dispatch",
|
||||
version: "1.0.0",
|
||||
description: "A cool dispatch",
|
||||
author: "Project Sloth",
|
||||
resourceState: "started",
|
||||
},
|
||||
{
|
||||
name: "ps-hosuing",
|
||||
version: "1.0.0",
|
||||
description: "A cool house",
|
||||
author: "Project Sloth",
|
||||
resourceState: "started",
|
||||
},
|
||||
{
|
||||
name: "ps-camera",
|
||||
version: "1.0.0",
|
||||
description: "A cool camera",
|
||||
author: "Project Sloth and ok1ez ok1ez",
|
||||
resourceState: "started",
|
||||
},
|
||||
{
|
||||
name: "ps-hud",
|
||||
version: "1.0.0",
|
||||
description: "A cool hud",
|
||||
author: "Project Sloth",
|
||||
resourceState: "stopped",
|
||||
},
|
||||
{
|
||||
name: "ps-fuel",
|
||||
version: "1.0.0",
|
||||
description: "A cool gas pump",
|
||||
author: "Project Sloth",
|
||||
resourceState: "stopped",
|
||||
},
|
||||
{
|
||||
name: "ps-liveries",
|
||||
version: "1.0.0",
|
||||
description: "A cool liverie",
|
||||
author: "Project Sloth",
|
||||
resourceState: "stopped",
|
||||
},
|
||||
{
|
||||
name: "ps-ui",
|
||||
version: "1.0.0",
|
||||
description: "A cool ui",
|
||||
author: "Project Sloth",
|
||||
resourceState: "stopped",
|
||||
},
|
||||
]
|
||||
|
||||
const debugPlayers = [
|
||||
{
|
||||
id: "1",
|
||||
citizenid: "ERP95808",
|
||||
name: "John Doe",
|
||||
job: "Police Officer",
|
||||
phone: "555-555-5555",
|
||||
discord: "discord:917110675220865025",
|
||||
dateofbirth: "01/12/2001",
|
||||
bank: "10022",
|
||||
cash: "2022",
|
||||
license: "license:9e9df5e3b52641da00f5b2aba25edc45317689b2",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
citizenid: "ERP87521",
|
||||
name: "Jane Smith",
|
||||
job: "Paramedic",
|
||||
phone: "555-555-1234",
|
||||
discord: "discord:732198415678290144",
|
||||
dateofbirth: "05/18/1990",
|
||||
bank: "8000",
|
||||
cash: "150",
|
||||
license: "license:5a0f4e86c7d283b3cde6acba9821d4a5913076d8",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
citizenid: "ERP35267",
|
||||
name: "Michael Johnson",
|
||||
job: "Mechanic",
|
||||
phone: "555-555-9876",
|
||||
discord: "discord:609827518329704632",
|
||||
dateofbirth: "11/03/1985",
|
||||
bank: "500",
|
||||
cash: "3500",
|
||||
license: "license:c5f2b76a8e1e0d4c7892a3d1b74cf561b89e25e7",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
citizenid: "ERP70125",
|
||||
name: "Emily Davis",
|
||||
job: "Lawyer",
|
||||
phone: "555-555-2222",
|
||||
discord: "discord:815369027403189267",
|
||||
dateofbirth: "09/21/1988",
|
||||
bank: "22000",
|
||||
cash: "500",
|
||||
license: "license:3d4e6f7aa1b9e8c5d2fbc0439e1a865b470192f4",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
citizenid: "ERP48039",
|
||||
name: "Robert Wilson",
|
||||
job: "Taxi Driver",
|
||||
phone: "555-555-7777",
|
||||
discord: "discord:518942015678302479",
|
||||
dateofbirth: "07/08/1977",
|
||||
bank: "1200",
|
||||
cash: "780",
|
||||
license: "license:98e7c6d5a2b3f1e4d0c9876a5432109bfedc8a76",
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
citizenid: "ERP91726",
|
||||
name: "Amanda Lee",
|
||||
job: "Chef",
|
||||
phone: "555-555-3333",
|
||||
discord: "discord:725048390162871590",
|
||||
dateofbirth: "03/15/1995",
|
||||
bank: "4000",
|
||||
cash: "200",
|
||||
license: "license:4a5b6c7d8e9f01234567890abcdef1234567890",
|
||||
},
|
||||
{
|
||||
id: "7",
|
||||
citizenid: "ERP24680",
|
||||
name: "Christopher Martinez",
|
||||
job: "Firefighter",
|
||||
phone: "555-555-8888",
|
||||
discord: "discord:926371058274690831",
|
||||
dateofbirth: "12/30/1982",
|
||||
bank: "7500",
|
||||
cash: "1000",
|
||||
license: "license:7890123456abcdef0123456789a5b4c3d2e1f0",
|
||||
},
|
||||
]
|
||||
|
||||
debugData([
|
||||
{
|
||||
action: "setActionData",
|
||||
data: debugActions
|
||||
},
|
||||
])
|
||||
|
||||
debugData([
|
||||
{
|
||||
action: "setResourceData",
|
||||
data: debugResources
|
||||
},
|
||||
])
|
||||
|
||||
debugData([
|
||||
{
|
||||
action: "setPlayersData",
|
||||
data: debugPlayers
|
||||
},
|
||||
])
|
||||
|
||||
let options = [
|
||||
{
|
||||
component: 'Show',
|
||||
actions : [
|
||||
{
|
||||
name: "show",
|
||||
action: "setVisible",
|
||||
data: true,
|
||||
},
|
||||
{
|
||||
name: "hide",
|
||||
action: "setVisible",
|
||||
data: false,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
component: 'Actions Data',
|
||||
actions : [
|
||||
{
|
||||
name: "Set Data",
|
||||
action: "setActionData",
|
||||
data: debugActions,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
component: 'Resource Data',
|
||||
actions : [
|
||||
{
|
||||
name: "Set Data",
|
||||
action: "setResourceData",
|
||||
data: debugResources,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
component: 'Player Data',
|
||||
actions : [
|
||||
{
|
||||
name: "Set Data",
|
||||
action: "setPlayersData",
|
||||
data: debugPlayers,
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
|
||||
<div class="absolute top-0 z-[1000] font-medium uppercase m-4">
|
||||
<button class="bg-neutral-800 p-3
|
||||
3 font-medium uppercase"
|
||||
on:click={() => {
|
||||
show = !show;
|
||||
}}
|
||||
>
|
||||
Show
|
||||
</button>
|
||||
{#if show}
|
||||
<div class="w-fit h-fit bg-neutral-800 p-2 ">
|
||||
{#each options as option}
|
||||
<div class="flex flex-row gap-2 items-center m-1">
|
||||
<p class="h-full w-full mr-2">{option.component}</p>
|
||||
{#each option.actions as action}
|
||||
<button class="bg-neutral-600 p-2"
|
||||
on:click={() => {
|
||||
|
||||
if (action.custom == true) {
|
||||
action.customFunction();
|
||||
return
|
||||
}
|
||||
debugData([
|
||||
{
|
||||
action: action.action,
|
||||
data: action.data,
|
||||
},
|
||||
])
|
||||
}}
|
||||
>
|
||||
{action.name}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
@ -0,0 +1,69 @@
|
||||
<script lang="ts">
|
||||
import { ReceiveNUI } from '../utils/ReceiveNUI'
|
||||
import { SendNUI } from '../utils/SendNUI'
|
||||
import { onMount } from 'svelte'
|
||||
import { BROWSER_MODE, MENU_WIDE, VISIBILITY } from '../store/stores'
|
||||
import BackdropFix from './BackdropFix.svelte'
|
||||
|
||||
let isVisible: boolean
|
||||
let isBrowser: boolean
|
||||
|
||||
BROWSER_MODE.subscribe((browser: boolean) => {
|
||||
isBrowser = browser
|
||||
})
|
||||
|
||||
VISIBILITY.subscribe((visible: boolean) => {
|
||||
isVisible = visible
|
||||
})
|
||||
|
||||
ReceiveNUI<boolean>('setVisible', (visible: boolean) => {
|
||||
VISIBILITY.set(visible)
|
||||
})
|
||||
|
||||
ReceiveNUI('setBrowserMode', (data: boolean) => {
|
||||
BROWSER_MODE.set(data)
|
||||
// console.log('browser mode enabled')
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
const keyHandler = (e: KeyboardEvent) => {
|
||||
if (isVisible && ['Escape'].includes(e.code)) {
|
||||
SendNUI('hideUI')
|
||||
VISIBILITY.set(false)
|
||||
}
|
||||
if (
|
||||
!isVisible &&
|
||||
['Escape'].includes(e.code) &&
|
||||
isBrowser === true
|
||||
) {
|
||||
VISIBILITY.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', keyHandler)
|
||||
|
||||
return () => window.removeEventListener('keydown', keyHandler)
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if isVisible}
|
||||
<main class="w-screen h-screen flex justify-end items-center {!$MENU_WIDE ? " " : "justify-center"}">
|
||||
<slot />
|
||||
</main>
|
||||
<!-- <BackdropFix /> -->
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
main {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
</style>
|
48
resources/[ps]/ps-adminmenu/ui/src/styles.css
Normal file
48
resources/[ps]/ps-adminmenu/ui/src/styles.css
Normal file
@ -0,0 +1,48 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-smooth: auto;
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:root {
|
||||
font-size: 62.5%;
|
||||
font-smooth: auto;
|
||||
color: #c2c2c2;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
font-size: 1.6rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
height: 0px;
|
||||
width: 0vh;
|
||||
background-color: #24272b;
|
||||
}
|
||||
|
||||
.scroll-visble ::-webkit-scrollbar {
|
||||
width: 0.5vh;
|
||||
background-color: #141517;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #24272b;
|
||||
border-radius: 50px;
|
||||
}
|
2
resources/[ps]/ps-adminmenu/ui/src/vite-env.d.ts
vendored
Normal file
2
resources/[ps]/ps-adminmenu/ui/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
7
resources/[ps]/ps-adminmenu/ui/svelte.config.js
Normal file
7
resources/[ps]/ps-adminmenu/ui/svelte.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import sveltePreprocess from 'svelte-preprocess'
|
||||
|
||||
export default {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: sveltePreprocess()
|
||||
}
|
20
resources/[ps]/ps-adminmenu/ui/tailwind.config.cjs
Normal file
20
resources/[ps]/ps-adminmenu/ui/tailwind.config.cjs
Normal file
@ -0,0 +1,20 @@
|
||||
module.exports = {
|
||||
darkmode: true,
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{svelte,js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#141517',
|
||||
secondary: '#1a1b1e',
|
||||
tertiary: '#24272b',
|
||||
accent: '#2284d9',
|
||||
border_primary: '#373a40',
|
||||
hover_secondary: '#2c2e33',
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
29
resources/[ps]/ps-adminmenu/ui/tsconfig.json
Normal file
29
resources/[ps]/ps-adminmenu/ui/tsconfig.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@assets/*": ["src/assets/*"],
|
||||
"@components/*": ["src/components/*"],
|
||||
"@providers/*": ["src/providers/*"],
|
||||
"@store/*": ["src/store/*"],
|
||||
"@utils/*": ["src/utils/*"],
|
||||
"@typings/*": ["src/typings/*"],
|
||||
"@layout/*": ["src/layout/*"],
|
||||
"@pages/*": ["src/pages/*"],
|
||||
},
|
||||
/**
|
||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||
* Disable checkJs if you'd like to use dynamic types in JS.
|
||||
* Note that setting allowJs false does not prevent the use
|
||||
* of JS in `.svelte` files.
|
||||
*/
|
||||
"allowJs": true,
|
||||
"checkJs": true
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||
}
|
41
resources/[ps]/ps-adminmenu/ui/vite.config.js
Normal file
41
resources/[ps]/ps-adminmenu/ui/vite.config.js
Normal file
@ -0,0 +1,41 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
import postcss from './postcss.config.js';
|
||||
import { resolve } from "path";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
css: {
|
||||
postcss,
|
||||
},
|
||||
plugins: [svelte({
|
||||
/* plugin options */
|
||||
})],
|
||||
base: './', // fivem nui needs to have local dir reference
|
||||
resolve: {
|
||||
alias: {
|
||||
"@assets": resolve("./src/assets"),
|
||||
"@components": resolve("./src/components"),
|
||||
"@providers": resolve("./src/providers"),
|
||||
"@store": resolve("./src/store"),
|
||||
"@utils": resolve("./src/utils"),
|
||||
"@typings": resolve("./src/typings"),
|
||||
"@layout": resolve("./src/layout"),
|
||||
"@pages": resolve("./src/pages"),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
emptyOutDir: true,
|
||||
outDir: '../html',
|
||||
assetsDir: './',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// By not having hashes in the name, you don't have to update the manifest, yay!
|
||||
entryFileNames: `[name].js`,
|
||||
chunkFileNames: `[name].js`,
|
||||
assetFileNames: `[name].[ext]`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
Loading…
Reference in New Issue
Block a user