fixes etc

This commit is contained in:
Nordi98 2025-06-12 03:36:12 +02:00
parent 4a0c8c6204
commit 453b281a4b
644 changed files with 1907 additions and 2456 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
-- Emotes you add in the file will automatically be added to AnimationList.lua
-- If you have multiple custom list files they MUST be added between AnimationList.lua and Emote.lua in fxmanifest.lua!
-- Don't change 'CustomDP' it is local to this file!
-- Remove the } from the = {} then enter your own animation code ---
-- Don't forget to close the tables.
local CustomDP = {}
CustomDP.Expressions = {}
CustomDP.Walks = {}
CustomDP.Shared = {}
CustomDP.Dances = {}
CustomDP.AnimalEmotes = {}
CustomDP.Exits = {}
CustomDP.Emotes = {}
CustomDP.PropEmotes = {}
-----------------------------------------------------------------------------------------
--| I don't think you should change the code below unless you know what you are doing |--
-----------------------------------------------------------------------------------------
function LoadAddonEmotes()
local prefixes = {
Shared = '🤼 ',
AnimalEmotes = '🐶 ',
PropEmotes = '📦 '
}
for arrayName, array in pairs(CustomDP) do
if RP[arrayName] then
local prefix = prefixes[arrayName]
for emoteName, emoteData in pairs(array) do
if prefix then
emoteData[3] = prefix .. emoteData[3]
end
RP[arrayName][emoteName] = emoteData
end
end
end
-- Free memory
CustomDP = nil
end

View file

@ -0,0 +1,168 @@
IsUsingBinoculars = false
if Config.BinocularsEnabled then
RegisterCommand("binoculars", function()
UseBinocular()
end, false)
TriggerEvent('chat:addSuggestion', '/binoculars', 'Use binoculars', {})
local fov = 40.0
local index = 0
local cam
local prop_binoc
local instructions = true
local scaleform_bin
local scaleform_instructions
local function CleanupBinoculars()
ClearPedTasks(PlayerPedId())
ClearTimecycleModifier()
RenderScriptCams(false, false, 0, true, false)
SetScaleformMovieAsNoLongerNeeded(scaleform_bin)
SetScaleformMovieAsNoLongerNeeded(scaleform_instructions)
DestroyCam(cam, false)
if prop_binoc then
DeleteEntity(prop_binoc)
end
SetNightvision(false)
SetSeethrough(false)
end
function UseBinocular()
if IsPedSittingInAnyVehicle(PlayerPedId()) then
return
end
if IsInActionWithErrorMessage({ ['IsUsingBinoculars'] = true }) then
return
end
IsUsingBinoculars = not IsUsingBinoculars
if IsUsingBinoculars then
CreateThread(function()
DestroyAllProps()
ClearPedTasks(PlayerPedId())
RequestAnimDict("amb@world_human_binoculars@male@idle_a")
while not HasAnimDictLoaded("amb@world_human_binoculars@male@idle_a") do
Wait(5)
end
-- attach the prop to the player
local boneIndex = GetPedBoneIndex(PlayerPedId(), 28422)
local x, y, z = table.unpack(GetEntityCoords(PlayerPedId(), true))
if not HasModelLoaded("prop_binoc_01") then
LoadPropDict("prop_binoc_01")
end
prop_binoc = CreateObject(`prop_binoc_01`, x, y, z + 0.2, true, true, true)
AttachEntityToEntity(prop_binoc, PlayerPedId(), boneIndex, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, true,
false, true, 1, true)
TaskPlayAnim(PlayerPedId(), "amb@world_human_binoculars@male@idle_a", "idle_c", 5.0, 5.0, -1, 51, 0,
false, false, false)
PlayAmbientSpeech1(PlayerPedId(), "GENERIC_CURSE_MED", "SPEECH_PARAMS_FORCE")
SetCurrentPedWeapon(PlayerPedId(), `WEAPON_UNARMED`, true)
RemoveAnimDict("amb@world_human_binoculars@male@idle_a")
SetModelAsNoLongerNeeded("prop_binoc_01")
end)
Wait(200)
SetTimecycleModifier("default")
SetTimecycleModifierStrength(0.3)
scaleform_bin = RequestScaleformMovie("BINOCULARS")
while not HasScaleformMovieLoaded(scaleform_bin) do
Wait(10)
end
cam = CreateCam("DEFAULT_SCRIPTED_FLY_CAMERA", true)
AttachCamToEntity(cam, PlayerPedId(), 0.0, 0.0, 1.2, true)
SetCamRot(cam, 0.0, 0.0, GetEntityHeading(PlayerPedId()))
SetCamFov(cam, fov)
RenderScriptCams(true, false, 0, true, false)
PushScaleformMovieFunction(scaleform_bin, "SET_CAM_LOGO")
PushScaleformMovieFunctionParameterInt(0) -- 0 for nothing, 1 for LSPD logo
PopScaleformMovieFunctionVoid()
local keyList
if Config.AllowVisionsToggling then
keyList = {
{ key = 177, text = 'exit_binoculars' },
{ key = 19, text = 'toggle_binoculars_vision' },
{ key = 47, text = 'toggle_instructions' }
}
else
keyList = {
{ key = 177, text = 'exit_binoculars' },
{ key = 47, text = 'toggle_instructions' }
}
end
scaleform_instructions = SetupButtons(keyList)
while IsUsingBinoculars and not IsEntityDead(PlayerPedId()) and not IsPedSittingInAnyVehicle(PlayerPedId()) do
if IsControlJustPressed(0, 177) then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
IsUsingBinoculars = false
end
fov = HandleZoomAndCheckRotation(cam, fov)
HideHUDThisFrame()
DisableControlAction(0, 25, true) -- disable aim
DisableControlAction(0, 44, true) -- INPUT_COVER
DisableControlAction(0, 37, true) -- INPUT_SELECT_WEAPON
DisableControlAction(0, 24, true) -- Attack
DisablePlayerFiring(PlayerPedId(), true) -- Disable weapon firing
if IsControlJustPressed(0, 19) and Config.AllowVisionsToggling then
-- if index = 0, toggle night vision, if index = 1, toggle thermal vision, if index = 2, toggle normal vision and reset index
if index == 0 then
SetNightvision(true)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
index = index + 1
elseif index == 1 then
SetSeethrough(true)
SetNightvision(false)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
index = index + 1
elseif index == 2 then
SetNightvision(false)
SetSeethrough(false)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
index = 0
end
end
if IsControlJustPressed(0, 47) then
instructions = not instructions
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
DrawScaleformMovieFullscreen(scaleform_bin, 255, 255, 255, 255)
if instructions then
DrawScaleformMovieFullscreen(scaleform_instructions, 255, 255, 255, 255)
end
Wait(1)
end
end
-- RESET EVERYTHING
IsUsingBinoculars = false
index = 0
CleanupBinoculars()
end
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() then
CleanupBinoculars()
end
end)
CreateExport('toggleBinoculars', function()
UseBinocular()
end)
end

View file

@ -0,0 +1,107 @@
Framework = 'standalone'
PlayerLoaded, PlayerData = nil, {}
local function InitializeFramework()
if GetResourceState('es_extended') == 'started' then
ESX = exports['es_extended']:getSharedObject()
Framework = 'esx'
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
PlayerData = xPlayer
PlayerLoaded = true
end)
RegisterNetEvent('esx:onPlayerLogout', function()
PlayerData = {}
PlayerLoaded = false
end)
AddEventHandler('esx:setPlayerData', function(key, value)
PlayerData[key] = value
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = ESX.GetPlayerData()
PlayerLoaded = true
end)
elseif GetResourceState('qb-core') == 'started' then
QBCore = exports['qb-core']:GetCoreObject()
Framework = 'qb'
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
PlayerData = QBCore.Functions.GetPlayerData()
end)
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
PlayerData = {}
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = QBCore.Functions.GetPlayerData()
end)
end
print('[RPEmotes-Reborn] Framework initialized: ' .. Framework)
end
function CanDoAction()
if Framework == 'esx' then
return PlayerLoaded and not PlayerData.dead
elseif Framework == 'qb' then
return LocalPlayer.state.isLoggedIn and not (PlayerData.metadata.inlaststand or PlayerData.metadata.isdead)
end
-- here you can implement your own standalone framework check
return true
end
InitializeFramework()
-- EVENTS
RegisterNetEvent('animations:client:PlayEmote', function(args)
if CanDoAction() then
EmoteCommandStart(args)
end
end)
if Config.Keybinding then
RegisterNetEvent('animations:client:BindEmote', function(args)
if CanDoAction() then
EmoteBindStart(nil, args)
end
end)
RegisterNetEvent('animations:client:EmoteBinds', function()
if CanDoAction() then
ListKeybinds()
end
end)
RegisterNetEvent('animations:client:EmoteDelete', function(args)
if CanDoAction() then
DeleteEmote(args)
end
end)
end
RegisterNetEvent('animations:client:EmoteMenu', function()
if CanDoAction() then
OpenEmoteMenu()
end
end)
RegisterNetEvent('animations:client:Walk', function(args)
if CanDoAction() then
WalkCommandStart(args)
end
end)
RegisterNetEvent('animations:client:ListWalks', function()
if CanDoAction() then
WalksOnCommand()
end
end)

View file

@ -0,0 +1,501 @@
IsProne = false
local isCrouched = false
local isCrawling = false
local inAction = false
local proneType = 'onfront'
local lastKeyPress = 0
local forceEndProne = false
-- Crouching --
local function ResetCrouch()
local playerPed = PlayerPedId()
ResetPedStrafeClipset(playerPed)
ResetPedWeaponMovementClipset(playerPed)
SetPedMaxMoveBlendRatio(playerPed, 1.0)
SetPedCanPlayAmbientAnims(playerPed, true)
local walkstyle = GetResourceKvpString("walkstyle")
if walkstyle then
local toApply = RP[walkstyle]
if not toApply or type(toApply) ~= "table" or toApply.category ~= "Walks" then
ResetPedMovementClipset(playerPed, 0.5)
DeleteResourceKvp("walkstyle")
DebugPrint('Invalid walkstyle found in KVP, resetting to default.')
return
end
RequestWalking(toApply[1])
SetPedMovementClipset(PlayerPedId(), toApply[1], 0.5)
RemoveClipSet(toApply[1])
else
ResetPedMovementClipset(playerPed, 0.5)
end
RemoveAnimSet('move_ped_crouched')
end
local function CrouchLoop()
local playerId = PlayerId()
while isCrouched do
local playerPed = PlayerPedId()
if not CanPlayerCrouchCrawl(playerPed) then
isCrouched = false
break
end
if IsPlayerAiming(playerId) then
SetPedMaxMoveBlendRatio(playerPed, 0.15)
end
SetPedCanPlayAmbientAnims(playerPed, false)
DisableControlAction(0, 36, true)
if IsPedUsingActionMode(playerPed) == 1 then
SetPedUsingActionMode(playerPed, false, -1, 'DEFAULT_ACTION')
end
DisableFirstPersonCamThisFrame()
if Config.FpsMode == true then
DisableControlAction(2, 25, true) -- disables the aim control action entirely while crouched
end
Wait(0)
end
TriggerEvent('crouch_crawl:onCrouch', false)
ResetCrouch()
end
local function StartCrouch()
isCrouched = true
RequestWalking('move_ped_crouched')
local playerPed = PlayerPedId()
if GetPedStealthMovement(playerPed) ~= 0 then
SetPedStealthMovement(playerPed, false, 'DEFAULT_ACTION')
Wait(100)
end
if GetFollowPedCamViewMode() == 4 then
SetFollowPedCamViewMode(0) -- THIRD_PERSON_NEAR
end
SetPedMovementClipset(playerPed, 'move_ped_crouched', 0.6)
SetPedStrafeClipset(playerPed, 'move_ped_crouched_strafing')
-- For other scripts to use
TriggerEvent('crouch_crawl:onCrouch', true)
CreateThread(CrouchLoop)
end
---@param playerPed number
---@return boolean success
local function AttemptCrouch(playerPed)
if CanPlayerCrouchCrawl(playerPed) and IsPedHuman(playerPed) then
StartCrouch()
return true
else
return false
end
end
---Disables a control until it's key has been released
---@param padIndex integer
---@param control integer
local function DisableControlUntilReleased(padIndex, control)
CreateThread(function()
while IsDisabledControlPressed(padIndex, control) do
DisableControlAction(padIndex, control, true)
Wait(0)
end
end)
end
local function CrouchKeyPressed()
if not LocalPlayer.state.canEmote then return end
if inAction then
return
end
if IsPauseMenuActive() or IsNuiFocused() then
return
end
if isCrouched then
isCrouched = false
local crouchKey = GetControlInstructionalButton(0, `+crouch` | 0x80000000, false)
local lookBehindKey = GetControlInstructionalButton(0, 26, false) -- INPUT_LOOK_BEHIND
if crouchKey == lookBehindKey then
DisableControlUntilReleased(0, 26) -- INPUT_LOOK_BEHIND
end
return
end
local playerPed = PlayerPedId()
if not CanPlayerCrouchCrawl(playerPed) or not IsPedHuman(playerPed) then
return
end
local crouchKey = GetControlInstructionalButton(0, `+crouch` | 0x80000000, false)
local lookBehindKey = GetControlInstructionalButton(0, 26, false) -- INPUT_LOOK_BEHIND
local duckKey = GetControlInstructionalButton(0, 36, false) -- INPUT_DUCK
if crouchKey == lookBehindKey then
DisableControlUntilReleased(0, 26) -- INPUT_LOOK_BEHIND
end
if crouchKey == duckKey then
if Config.CrouchOverrideStealthMode then
DisableControlAction(0, 36, true) -- Disable INPUT_DUCK this frame
elseif not IsProne then
local timer = GetGameTimer()
if GetPedStealthMovement(playerPed) ~= 0 and timer - lastKeyPress < 1000 then
DisableControlAction(0, 36, true) -- Disable INPUT_DUCK this frame
lastKeyPress = 0
else
lastKeyPress = timer
return
end
end
end
StartCrouch()
if IsProne then
inAction = true
IsProne = false
PlayAnimOnce(playerPed, 'get_up@directional@transition@prone_to_knees@crawl', 'front', nil, nil, 780)
Wait(780)
inAction = false
end
end
-- Crawling --
---@param playerPed number
---@return boolean
local function ShouldPlayerDiveToCrawl(playerPed)
if IsPedRunning(playerPed) or IsPedSprinting(playerPed) then
return true
end
return false
end
---Stops the player from being prone
---@param force boolean If forced then no exit anim is played
local function stopPlayerProne(force)
IsProne = false
forceEndProne = force
end
---@param playerPed number
---@param heading number|nil
---@param blendInSpeed number|nil
local function PlayIdleCrawlAnim(playerPed, heading, blendInSpeed)
local playerCoords = GetEntityCoords(playerPed)
TaskPlayAnimAdvanced(playerPed, 'move_crawl', proneType..'_fwd', playerCoords.x, playerCoords.y, playerCoords.z, 0.0, 0.0, heading or GetEntityHeading(playerPed), blendInSpeed or 2.0, 2.0, -1, 2, 1.0, false, false)
end
---@param forceEnd boolean
local function PlayExitCrawlAnims(forceEnd)
if not forceEnd then
inAction = true
local playerPed = PlayerPedId()
local animDict, animName, waitTime
if proneType == 'onfront' then
animDict, animName, waitTime = 'get_up@directional@transition@prone_to_knees@crawl', 'front', 780
else
animDict, animName, waitTime = 'get_up@directional@transition@prone_to_seated@crawl', 'back', 950
end
PlayAnimOnce(playerPed, animDict, animName, nil, nil, waitTime)
if not isCrouched then
Wait(waitTime)
PlayAnimOnce(playerPed, 'get_up@directional@movement@from_'..(proneType == 'onfront' and 'knees' or 'seated')..'@standard', 'getup_l_0', nil, nil, 1300)
end
end
end
-- Crawls one "step" forward/backward
---@param playerPed number
---@param type string
---@param direction string
local function Crawl(playerPed, type, direction)
isCrawling = true
TaskPlayAnim(playerPed, 'move_crawl', type..'_'..direction, 8.0, -8.0, -1, 2, 0.0, false, false, false)
local time = {
['onfront'] = {
['fwd'] = 820,
['bwd'] = 990
},
['onback'] = {
['fwd'] = 1200,
['bwd'] = 1200
}
}
SetTimeout(time[type][direction], function()
isCrawling = false
end)
end
-- Flips the player when crawling
---@param playerPed number
local function CrawlFlip(playerPed)
inAction = true
local heading = GetEntityHeading(playerPed)
proneType = proneType == 'onfront' and 'onback' or 'onfront'
if proneType == 'onback' then
PlayAnimOnce(playerPed, 'get_up@directional_sweep@combat@pistol@front', 'front_to_prone', 2.0)
ChangeHeadingSmooth(playerPed, -18.0, 3600)
else
PlayAnimOnce(playerPed, 'move_crawlprone2crawlfront', 'back', 2.0, nil, -1)
ChangeHeadingSmooth(playerPed, 12.0, 1700)
end
PlayIdleCrawlAnim(playerPed, heading + 180.0)
Wait(400)
inAction = false
end
local function CrawlLoop()
Wait(400)
while IsProne do
local playerPed = PlayerPedId()
if not CanPlayerCrouchCrawl(playerPed) or IsEntityInWater(playerPed) then
ClearPedTasks(playerPed)
stopPlayerProne(true)
break
end
local forward, backwards = IsControlPressed(0, 32), IsControlPressed(0, 33) -- INPUT_MOVE_UP_ONLY, INPUT_MOVE_DOWN_ONLY
if not isCrawling then
if forward then -- Forward
Crawl(playerPed, proneType, 'fwd')
elseif backwards then -- Back
Crawl(playerPed, proneType, 'bwd')
end
end
-- Moving left/right
if IsControlPressed(0, 34) then -- INPUT_MOVE_LEFT_ONLY
if isCrawling then
local headingDiff = forward and 1.0 or -1.0
SetEntityHeading(playerPed, GetEntityHeading(playerPed) + headingDiff)
else
inAction = true
if proneType == 'onfront' then
local playerCoords = GetEntityCoords(playerPed)
TaskPlayAnimAdvanced(playerPed, 'move_crawlprone2crawlfront', 'left', playerCoords.x, playerCoords.y, playerCoords.z, 0.0, 0.0, GetEntityHeading(playerPed), 2.0, 2.0, -1, 2, 0.1, false, false)
ChangeHeadingSmooth(playerPed, -10.0, 300)
Wait(700)
else
PlayAnimOnce(playerPed, 'get_up@directional_sweep@combat@pistol@left', 'left_to_prone')
ChangeHeadingSmooth(playerPed, 25.0, 400)
PlayIdleCrawlAnim(playerPed)
Wait(600)
end
inAction = false
end
elseif IsControlPressed(0, 35) then -- INPUT_MOVE_RIGHT_ONLY
if isCrawling then
local headingDiff = backwards and 1.0 or -1.0
SetEntityHeading(playerPed, GetEntityHeading(playerPed) + headingDiff)
else
inAction = true
if proneType == 'onfront' then
local playerCoords = GetEntityCoords(playerPed)
TaskPlayAnimAdvanced(playerPed, 'move_crawlprone2crawlfront', 'right', playerCoords.x, playerCoords.y, playerCoords.z, 0.0, 0.0, GetEntityHeading(playerPed), 2.0, 2.0, -1, 2, 0.1, false, false)
ChangeHeadingSmooth(playerPed, 10.0, 300)
Wait(700)
else
PlayAnimOnce(playerPed, 'get_up@directional_sweep@combat@pistol@right', 'right_to_prone')
ChangeHeadingSmooth(playerPed, -25.0, 400)
PlayIdleCrawlAnim(playerPed)
Wait(600)
end
inAction = false
end
end
if not isCrawling then
if IsControlPressed(0, 22) then -- INPUT_JUMP
CrawlFlip(playerPed)
end
end
Wait(0)
end
TriggerEvent('crouch_crawl:onCrawl', false)
PlayExitCrawlAnims(forceEndProne)
isCrawling = false
inAction = false
forceEndProne = false
proneType = 'onfront'
SetPedConfigFlag(PlayerPedId(), 48, false) -- CPED_CONFIG_FLAG_BlockWeaponSwitching
RemoveAnimDict('move_crawl')
RemoveAnimDict('move_crawlprone2crawlfront')
end
local function CrawlKeyPressed()
if not LocalPlayer.state.canEmote then return end
if inAction then
return
end
if IsPauseMenuActive() or IsNuiFocused() then
return
end
if IsProne then
IsProne = false
return
end
if IsInAnimation then
EmoteCancel()
end
local wasCrouched = false
if isCrouched then
isCrouched = false
wasCrouched = true
end
local playerPed = PlayerPedId()
if not CanPlayerCrouchCrawl(playerPed) or IsEntityInWater(playerPed) or not IsPedHuman(playerPed) then
return
end
inAction = true
if Pointing then
Pointing = false
end
if InHandsup then
return
end
if IsInActionWithErrorMessage({['IsProne'] = true}) then
return
end
IsProne = true
SetPedConfigFlag(playerPed, 48, true) -- CPED_CONFIG_FLAG_BlockWeaponSwitching
if GetPedStealthMovement(playerPed) ~= 0 then
SetPedStealthMovement(playerPed, false, 'DEFAULT_ACTION')
Wait(100)
end
LoadAnim('move_crawl')
LoadAnim('move_crawlprone2crawlfront')
if ShouldPlayerDiveToCrawl(playerPed) then
PlayAnimOnce(playerPed, 'explosions', 'react_blown_forwards', nil, 3.0)
Wait(1100)
elseif wasCrouched then
PlayAnimOnce(playerPed, 'amb@world_human_sunbathe@male@front@enter', 'enter', nil, nil, -1, 0.3)
Wait(1500)
else
PlayAnimOnce(playerPed, 'amb@world_human_sunbathe@male@front@enter', 'enter')
Wait(3000)
end
if CanPlayerCrouchCrawl(playerPed) and not IsEntityInWater(playerPed) then
PlayIdleCrawlAnim(playerPed, nil, 3.0)
end
TriggerEvent('crouch_crawl:onCrawl', true)
inAction = false
CreateThread(CrawlLoop)
end
-- Commands & KeyMapping --
if Config.CrouchEnabled then
if Config.CrouchKeybindEnabled then
RegisterKeyMapping('+crouch', Translate('register_crouch'), 'keyboard', Config.CrouchKeybind)
RegisterCommand('+crouch', function() CrouchKeyPressed() end, false)
RegisterCommand('-crouch', function() end, false) -- This needs to be here to prevent warnings in chat
end
RegisterCommand('crouch', function()
if not LocalPlayer.state.canEmote then return end
if isCrouched then
isCrouched = false
return
end
AttemptCrouch(PlayerPedId())
end, false)
TriggerEvent('chat:addSuggestion', '/crouch', Translate('crouch'))
end
if Config.CrawlEnabled then
if Config.CrawlKeybindEnabled then
RegisterKeyMapping('+crawl', Translate('register_crawl'), 'keyboard', Config.CrawlKeybind)
RegisterCommand('+crawl', function() CrawlKeyPressed() end, false)
RegisterCommand('-crawl', function() end, false) -- This needs to be here to prevent warnings in chat
end
RegisterCommand('crawl', function() CrawlKeyPressed() end, false)
TriggerEvent('chat:addSuggestion', '/crawl', Translate('crawl'))
end
---Returns if the player is crouched
---@return boolean
local function IsPlayerCrouched()
return isCrouched
end
CreateExport('IsPlayerCrouched', IsPlayerCrouched)
---Returns if the player is prone (both when laying still and when moving)
---@return boolean
local function IsPlayerProne()
return IsProne
end
CreateExport('IsPlayerProne', IsPlayerProne)
---Returns if the player is crawling (only when moving forward/backward)
---@return boolean
local function IsPlayerCrawling()
return isCrawling
end
CreateExport('IsPlayerCrawling', IsPlayerCrawling)
---Returns either "onfront" or "onback", this can be used to check if the player is on his back or on his stomach. NOTE: This will still return a string even if the player is not prone. Use IsPlayerProne() to check if the player is prone.
---@return string
local function GetPlayerProneType()
return proneType
end
CreateExport('GetPlayerProneType', GetPlayerProneType)
-- Useful to call if the player gets handcuffed etc.
CreateExport('StopPlayerProne', stopPlayerProne)

View file

@ -0,0 +1,895 @@
-- You probably shouldn't touch these.
IsInAnimation = false
CurrentAnimationName = nil
CurrentTextureVariation = nil
InHandsup = false
CONVERTED = false
local ChosenDict = ""
local CurrentAnimOptions = false
local PlayerGender = "male"
local PlayerProps = {}
local PreviewPedProps = {}
local PtfxNotif = false
local PtfxPrompt = false
local AnimationThreadStatus = false
local CheckStatus = false
local CanCancel = true
local InExitEmote = false
local ExitAndPlay = false
local EmoteCancelPlaying = false
local currentEmote = {}
local attachedProp
local scenarioObjects = {
`p_amb_coffeecup_01`,
`p_amb_joint_01`,
`p_cs_ciggy_01`,
`p_cs_ciggy_01b_s`,
`p_cs_clipboard`,
`prop_curl_bar_01`,
`p_cs_joint_01`,
`p_cs_joint_02`,
`prop_acc_guitar_01`,
`prop_amb_ciggy_01`,
`prop_amb_phone`,
`prop_beggers_sign_01`,
`prop_beggers_sign_02`,
`prop_beggers_sign_03`,
`prop_beggers_sign_04`,
`prop_bongos_01`,
`prop_cigar_01`,
`prop_cigar_02`,
`prop_cigar_03`,
`prop_cs_beer_bot_40oz_02`,
`prop_cs_paper_cup`,
`prop_cs_trowel`,
`prop_fib_clipboard`,
`prop_fish_slice_01`,
`prop_fishing_rod_01`,
`prop_fishing_rod_02`,
`prop_notepad_02`,
`prop_parking_wand_01`,
`prop_rag_01`,
`prop_scn_police_torch`,
`prop_sh_cigar_01`,
`prop_sh_joint_01`,
`prop_tool_broom`,
`prop_tool_hammer`,
`prop_tool_jackham`,
`prop_tennis_rack_01`,
`prop_weld_torch`,
`w_me_gclub`,
`p_amb_clipboard_01`
}
if not Config.AnimalEmotesEnabled then
RP.AnimalEmotes = {}
end
CreateThread(function()
LocalPlayer.state:set('canEmote', true, true)
end)
local function RunAnimationThread()
local pPed = PlayerPedId()
if AnimationThreadStatus then return end
AnimationThreadStatus = true
CreateThread(function()
local sleep
while AnimationThreadStatus and (IsInAnimation or PtfxPrompt) do
sleep = 500
if IsInAnimation then
sleep = 0
if IsPlayerAiming(pPed) then
EmoteCancel()
end
if not Config.AllowPunchingDuringEmote then
DisableControlAction(2, 140, true)
DisableControlAction(2, 141, true)
DisableControlAction(2, 142, true)
end
end
if PtfxPrompt and CurrentAnimOptions then
sleep = 0
if not PtfxNotif then
SimpleNotify(CurrentAnimOptions.PtfxInfo or Translate('ptfxinfo'))
PtfxNotif = true
end
if IsControlPressed(0, 47) then
PtfxStart()
Wait(CurrentAnimOptions.PtfxWait)
if CurrentAnimOptions.PtfxCanHold then
while IsControlPressed(0, 47) and IsInAnimation and AnimationThreadStatus do
Wait(5)
end
end
PtfxStop()
end
end
Wait(sleep)
end
end)
end
local function CheckStatusThread(dict, anim)
CreateThread(function()
if CheckStatus then
CheckStatus = false
Wait(10)
end
CheckStatus = true
while not IsEntityPlayingAnim(PlayerPedId(), dict, anim, 3) do
Wait(5)
end
while CheckStatus and IsInAnimation do
if not IsEntityPlayingAnim(PlayerPedId(), dict, anim, 3) then
DebugPrint("Animation ended")
DestroyAllProps()
EmoteCancel()
break
end
Wait(0)
end
end)
end
local function cleanScenarioObjects(isClone)
local ped = isClone and ClonedPed or PlayerPedId()
local playerCoords = GetEntityCoords(ped)
for i = 1, #scenarioObjects do
local deleteScenarioObject = GetClosestObjectOfType(playerCoords.x, playerCoords.y, playerCoords.z, 1.0,
scenarioObjects[i], false, true, true)
if DoesEntityExist(deleteScenarioObject) then
SetEntityAsMissionEntity(deleteScenarioObject, false, false)
DeleteObject(deleteScenarioObject)
end
end
end
function EmoteCancel(force)
LocalPlayer.state:set('currentEmote', nil, true)
EmoteCancelPlaying = true
if InExitEmote then
return
end
if not CanCancel and not force then return end
if ChosenDict == "MaleScenario" and IsInAnimation then
ClearPedTasksImmediately(PlayerPedId())
IsInAnimation = false
DebugPrint("Forced scenario exit")
elseif ChosenDict == "Scenario" and IsInAnimation then
ClearPedTasksImmediately(PlayerPedId())
IsInAnimation = false
DebugPrint("Forced scenario exit")
end
PtfxNotif = false
PtfxPrompt = false
Pointing = false
if IsInAnimation then
local ped = PlayerPedId()
if LocalPlayer.state.ptfx then
PtfxStop()
end
DetachEntity(ped, true, false)
CancelSharedEmote()
if CurrentAnimOptions and CurrentAnimOptions.ExitEmote then
local options = CurrentAnimOptions
local ExitEmoteType = options.ExitEmoteType or "Emotes"
if not RP[options.ExitEmote] then
DebugPrint("Exit emote was invalid")
IsInAnimation = false
ClearPedTasks(ped)
return
end
OnEmotePlay(options.ExitEmote)
DebugPrint("Playing exit animation")
local animationOptions = RP[options.ExitEmote].AnimationOptions
if animationOptions and animationOptions.EmoteDuration then
InExitEmote = true
SetTimeout(animationOptions.EmoteDuration, function()
InExitEmote = false
DestroyAllProps()
ClearPedTasks(ped)
EmoteCancelPlaying = false
end)
return
end
else
IsInAnimation = false
ClearPedTasks(ped)
EmoteCancelPlaying = false
end
DestroyAllProps()
end
cleanScenarioObjects(false)
AnimationThreadStatus = false
CheckStatus = false
end
function EmoteMenuStart(name, category, textureVariation)
local emote = RP[name]
if not emote then
return
end
if emote.category ~= category then
DebugPrint("Emote category mismatch : " .. emote.category .. " vs " .. category)
return
end
if category == "Expressions" then
SetPlayerPedExpression(name, true)
return
end
if emote.category == "AnimalEmotes" then
CheckAnimalAndOnEmotePlay(name)
return
end
OnEmotePlay(name, textureVariation)
end
function EmoteMenuStartClone(name, category)
if not Config.PreviewPed then return end
if not DoesEntityExist(ClonedPed) then return end
local emote = RP[name]
if not emote then
return
end
if emote.category ~= category then
DebugPrint("Emote category mismatch : " .. emote.category .. " vs " .. category)
return
end
if category == "Expressions" then
SetFacialIdleAnimOverride(ClonedPed, emote[1], true)
return
end
OnEmotePlayClone(name)
end
function EmoteCommandStart(args)
if #args > 0 then
if IsEntityDead(PlayerPedId()) or IsPedRagdoll(PlayerPedId()) or IsPedGettingUp(PlayerPedId()) or IsPedInMeleeCombat(PlayerPedId()) then
TriggerEvent('chat:addMessage', {
color = { 255, 0, 0 },
multiline = true,
args = { "RPEmotes", Translate('dead') }
})
return
end
if (IsPedSwimming(PlayerPedId()) or IsPedSwimmingUnderWater(PlayerPedId())) and not Config.AllowInWater then
TriggerEvent('chat:addMessage', {
color = { 255, 0, 0 },
multiline = true,
args = { "RPEmotes", Translate('swimming') }
})
return
end
local name = string.lower(args[1])
if name == "c" then
if IsInAnimation then
EmoteCancel()
else
EmoteChatMessage(Translate('nocancel'))
end
return
end
local emote = RP[name]
if emote then
if emote.category == "AnimalEmotes" then
if Config.AnimalEmotesEnabled then
CheckAnimalAndOnEmotePlay(name)
else
EmoteChatMessage(Translate('animaldisabled'))
end
return
end
if emote.category == "PropEmotes" and emote.AnimationOptions.PropTextureVariations then
if #args > 1 then
local textureVariation = tonumber(args[2])
if emote.AnimationOptions.PropTextureVariations[textureVariation] then
OnEmotePlay(name, textureVariation - 1)
return
else
local str = ""
for k, v in ipairs(emote.AnimationOptions.PropTextureVariations) do
str = str .. string.format("\n(%s) - %s", k, v.Name)
end
EmoteChatMessage(string.format(Translate('invalidvariation'), str), true)
OnEmotePlay(name, 0)
return
end
end
end
OnEmotePlay(name)
else
EmoteChatMessage("'" .. name .. "' " .. Translate('notvalidemote') .. "")
end
end
end
function CheckAnimalAndOnEmotePlay(name)
local playerPed = PlayerPedId()
local isValidPet = false
if string.sub(name, 1, 4) == "bdog" then
for _, model in ipairs(BigDogs) do
if IsPedModel(playerPed, GetHashKey(model)) then
isValidPet = true
break
end
end
elseif string.sub(name, 1, 4) == "sdog" then
for _, model in ipairs(SmallDogs) do
if IsPedModel(playerPed, GetHashKey(model)) then
isValidPet = true
break
end
end
end
if isValidPet then
OnEmotePlay(name)
else
EmoteChatMessage(Translate('notvalidpet'))
end
end
---@param isClone? boolean
function DestroyAllProps(isClone)
if isClone then
for _, v in pairs(PreviewPedProps) do
DeleteEntity(v)
end
PreviewPedProps = {}
else
for _, v in pairs(PlayerProps) do
DeleteEntity(v)
end
PlayerProps = {}
end
DebugPrint("Destroyed Props for " .. (isClone and "clone" or "player"))
end
---@param data table
---@return boolean
function AddProp(data)
assert(data.prop1, 'no prop1 passed')
assert(data.bone, 'no bone passed')
data.off1 = data.off1 or 0.0
data.off2 = data.off2 or 0.0
data.off3 = data.off3 or 0.0
data.rot1 = data.rot1 or 0.0
data.rot2 = data.rot2 or 0.0
data.rot3 = data.rot3 or 0.0
assert(data.noCollision == nil or type(data.noCollision) == "boolean", 'noCollision must be a boolean')
local target = data.isClone and ClonedPed or PlayerPedId()
local x, y, z = table.unpack(GetEntityCoords(target))
if not IsModelValid(data.prop1) then
DebugPrint(tostring(data.prop1) .. " is not a valid model!")
return false
end
LoadPropDict(data.prop1)
attachedProp = CreateObject(GetHashKey(data.prop1), x, y, z + 0.2, not data.isClone, true, true)
if data.textureVariation ~= nil then
SetObjectTextureVariation(attachedProp, data.textureVariation)
end
if data.noCollision then
SetEntityCollision(attachedProp, false, false)
end
AttachEntityToEntity(attachedProp, target, GetPedBoneIndex(target, data.bone), data.off1, data.off2, data.off3, data.rot1, data.rot2, data.rot3,
true, true, false, true, 1, true)
if data.isClone then
table.insert(PreviewPedProps, attachedProp)
else
table.insert(PlayerProps, attachedProp)
end
SetModelAsNoLongerNeeded(data.prop1)
DebugPrint("Added prop to " .. (data.isClone and "clone" or "player"))
return true
end
function CheckGender()
PlayerGender = "male"
if GetEntityModel(PlayerPedId()) == GetHashKey("mp_f_freemode_01") then
PlayerGender = "female"
end
DebugPrint("Set gender to " .. PlayerGender)
end
RegisterNetEvent('animations:ToggleCanDoAnims', function(value)
LocalPlayer.state:set('canEmote', value, true)
end)
function OnEmotePlay(name, textureVariation)
local emoteData = RP[name]
if not emoteData then
EmoteChatMessage("'" .. name .. "' " .. Translate('notvalidemote') .. "")
return
end
if not LocalPlayer.state.canEmote then return end
if not DoesEntityExist(PlayerPedId()) then
return false
end
cleanScenarioObjects(false)
InVehicle = IsPedInAnyVehicle(PlayerPedId(), true)
Pointing = false
if not Config.AllowEmoteInVehicle and InVehicle then
return
end
if Config.AdultEmotesDisabled and emoteData.AdultAnimation then
return EmoteChatMessage(Translate('adultemotedisabled'))
end
if InExitEmote then
return false
end
if Config.CancelPreviousEmote and IsInAnimation and not ExitAndPlay and not EmoteCancelPlaying then
ExitAndPlay = true
DebugPrint("Canceling previous emote and playing next emote")
PlayExitAndEnterEmote(name, textureVariation)
return
end
local animOption = emoteData.AnimationOptions
if InVehicle then
if animOption and animOption.NotInVehicle then
return EmoteChatMessage(Translate('not_in_a_vehicle'))
end
elseif animOption and animOption.onlyInVehicle then
return EmoteChatMessage(Translate('in_a_vehicle'))
end
if CurrentAnimOptions and CurrentAnimOptions.ExitEmote and animOption and animOption.ExitEmote then
if not (animOption and CurrentAnimOptions.ExitEmote == animOption.ExitEmote) and RP[CurrentAnimOptions.ExitEmote][2] ~= emoteData[2] then
return
end
end
if IsInActionWithErrorMessage() then
return false
end
ChosenDict = emoteData[1]
local anim = emoteData[2]
CurrentAnimationName = name
LocalPlayer.state:set('currentEmote', name, true)
CurrentTextureVariation = textureVariation
CurrentAnimOptions = animOption
if Config.DisarmPlayerOnEmote then
if IsPedArmed(PlayerPedId(), 7) then
SetCurrentPedWeapon(PlayerPedId(), GetHashKey('WEAPON_UNARMED'), true)
end
end
if animOption and animOption.Prop then
DestroyAllProps()
end
if ChosenDict == "MaleScenario" or ChosenDict == "Scenario" or ChosenDict == "ScenarioObject" then
if InVehicle then return end
CheckGender()
ClearPedTasks(PlayerPedId())
DestroyAllProps()
if ChosenDict == "MaleScenario" then
if PlayerGender == "male" then
TaskStartScenarioInPlace(PlayerPedId(), anim, 0, true)
DebugPrint("Playing scenario = (" .. anim .. ")")
else
EmoteCancel()
EmoteChatMessage(Translate('maleonly'))
return
end
elseif ChosenDict == "ScenarioObject" then
local BehindPlayer = GetOffsetFromEntityInWorldCoords(PlayerPedId(), 0.0, -0.5, -0.5)
TaskStartScenarioAtPosition(PlayerPedId(), anim, BehindPlayer.x, BehindPlayer.y, BehindPlayer.z, GetEntityHeading(PlayerPedId()), 0, true, false)
DebugPrint("Playing scenario = (" .. anim .. ")")
else
TaskStartScenarioInPlace(PlayerPedId(), anim, 0, true)
DebugPrint("Playing scenario = (" .. anim .. ")")
end
IsInAnimation = true
RunAnimationThread()
return
end
-- Small delay at the start
if animOption and animOption.StartDelay then
Wait(animOption.StartDelay)
end
if not LoadAnim(ChosenDict) then
EmoteChatMessage("'" .. name .. "' " .. Translate('notvalidemote') .. "")
return
end
local movementType = 0
if InVehicle then
if animOption and animOption.FullBody then
movementType = 35
else
movementType = 51
end
elseif animOption then
if animOption.EmoteMoving then
movementType = 51
elseif animOption.EmoteLoop then
movementType = 1
elseif animOption.EmoteStuck then
movementType = 50
end
end
DebugPrint("Animation flag = (" .. movementType .. ")")
if animOption then
if animOption.PtfxAsset then
Ptfx1, Ptfx2, Ptfx3, Ptfx4, Ptfx5, Ptfx6, PtfxScale = table.unpack(animOption.PtfxPlacement)
PtfxNotif = false
PtfxPrompt = true
RunAnimationThread()
TriggerServerEvent("rpemotes:ptfx:sync", animOption.PtfxAsset, animOption.PtfxName, vector3(Ptfx1, Ptfx2, Ptfx3),
vector3(Ptfx4, Ptfx5, Ptfx6), animOption.PtfxBone, PtfxScale, animOption.PtfxColor)
else
PtfxPrompt = false
end
end
if IsPedUsingAnyScenario(PlayerPedId()) or IsPedActiveInScenario(PlayerPedId()) then
ClearPedTasksImmediately(PlayerPedId())
end
TaskPlayAnim(PlayerPedId(), ChosenDict, anim, animOption?.BlendInSpeed or 5.0, animOption?.BlendOutSpeed or 5.0, animOption?.EmoteDuration or -1, animOption?.Flag or movementType, 0, false, false,
false)
RemoveAnimDict(ChosenDict)
IsInAnimation = true
RunAnimationThread()
if not (animOption and animOption.Prop) then
CheckStatusThread(ChosenDict, anim)
end
local currentEmoteTable = emoteData
for _, tabledata in pairs(RP) do
for command, emotedata in pairs(tabledata) do
if emotedata == emoteData then
table.insert(currentEmoteTable, command)
break
end
end
end
currentEmote = currentEmoteTable
if animOption and animOption.Prop then
PropPl1, PropPl2, PropPl3, PropPl4, PropPl5, PropPl6 = table.unpack(animOption.PropPlacement)
Wait(animOption and animOption.EmoteDuration or 0)
if not AddProp({
prop1 = animOption.Prop,
bone = animOption.PropBone,
off1 = PropPl1, off2 = PropPl2, off3 = PropPl3,
rot1 = PropPl4, rot2 = PropPl5, rot3 = PropPl6,
textureVariation = textureVariation,
isClone = false,
noCollision = animOption.PropNoCollision
}) then return end
if animOption.SecondProp then
SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6 = table.unpack(animOption.SecondPropPlacement)
if not AddProp({
prop1 = animOption.SecondProp,
bone = animOption.SecondPropBone,
off1 = SecondPropPl1, off2 = SecondPropPl2, off3 = SecondPropPl3,
rot1 = SecondPropPl4, rot2 = SecondPropPl5, rot3 = SecondPropPl6,
textureVariation = textureVariation,
isClone = false,
noCollision = animOption.SecondPropNoCollision
}) then
DestroyAllProps()
return
end
end
-- Ptfx is on the prop, then we need to sync it
if not animOption then return end
if animOption.PtfxAsset and not animOption.PtfxNoProp then
TriggerServerEvent("rpemotes:ptfx:syncProp", ObjToNet(attachedProp))
end
end
end
function OnEmotePlayClone(name)
if not Config.PreviewPed then return end
cleanScenarioObjects(true)
if not DoesEntityExist(ClonedPed) then
return false
end
if InExitEmote then
return false
end
if Config.CancelPreviousEmote and not ExitAndPlay and not EmoteCancelPlaying then
ExitAndPlay = true
DebugPrint("Canceling previous emote and playing next emote")
return
end
local emoteData = RP[name]
local animOption = emoteData.AnimationOptions
local dict, anim = table.unpack(emoteData)
if animOption and animOption.Prop then
DestroyAllProps(true)
end
if dict == "MaleScenario" or dict == "Scenario" or dict == "ScenarioObject" then
CheckGender()
ClearPedTasks(ClonedPed)
DestroyAllProps(true)
if dict == "MaleScenario" then
if PlayerGender == "male" then
TaskStartScenarioInPlace(ClonedPed, anim, 0, true)
end
elseif dict == "ScenarioObject" then
local BehindPlayer = GetOffsetFromEntityInWorldCoords(ClonedPed, 0.0, -0.5, -0.5)
TaskStartScenarioAtPosition(ClonedPed, anim, BehindPlayer.x, BehindPlayer.y, BehindPlayer.z, GetEntityHeading(ClonedPed), 0, true, false)
elseif dict == "Scenario" then
TaskStartScenarioInPlace(ClonedPed, anim, 0, true)
end
return
end
if not LoadAnim(dict) then
EmoteChatMessage("'" .. name .. "' " .. Translate('notvalidemote') .. "")
return
end
local movementType = 0
if animOption then
if animOption.EmoteMoving then
movementType = 51
elseif animOption.EmoteLoop then
movementType = 1
elseif animOption.EmoteStuck then
movementType = 50
end
end
if IsPedUsingAnyScenario(ClonedPed) or IsPedActiveInScenario(ClonedPed) then
ClearPedTasksImmediately(ClonedPed)
end
TaskPlayAnim(ClonedPed, dict, anim, 5.0, 5.0, animOption and animOption.EmoteDuration or -1, animOption?.Flag or movementType, 0, false, false, false)
RemoveAnimDict(dict)
if animOption and animOption.Prop then
local PropPl1, PropPl2, PropPl3, PropPl4, PropPl5, PropPl6 = table.unpack(animOption.PropPlacement)
Wait(animOption and animOption.EmoteDuration or 0)
if not AddProp({
prop1 = animOption.Prop,
bone = animOption.PropBone,
off1 = PropPl1, off2 = PropPl2, off3 = PropPl3,
rot1 = PropPl4, rot2 = PropPl5, rot3 = PropPl6,
isClone = true,
noCollision = animOption.PropNoCollision
}) then return end
if animOption.SecondProp then
local SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6 = table.unpack(animOption.SecondPropPlacement)
if not AddProp({
prop1 = animOption.SecondProp,
bone = animOption.SecondPropBone,
off1 = SecondPropPl1, off2 = SecondPropPl2, off3 = SecondPropPl3,
rot1 = SecondPropPl4, rot2 = SecondPropPl5, rot3 = SecondPropPl6,
isClone = true,
noCollision = animOption.SecondPropNoCollision
}) then
DestroyAllProps(true)
return
end
end
end
end
function PlayExitAndEnterEmote(name, textureVariation)
local ped = PlayerPedId()
if not CanCancel then return end
if ChosenDict == "MaleScenario" and IsInAnimation then
ClearPedTasksImmediately(ped)
IsInAnimation = false
DebugPrint("Forced scenario exit")
elseif ChosenDict == "Scenario" and IsInAnimation then
ClearPedTasksImmediately(ped)
IsInAnimation = false
DebugPrint("Forced scenario exit")
end
PtfxNotif = false
PtfxPrompt = false
Pointing = false
if LocalPlayer.state.ptfx then
PtfxStop()
end
DetachEntity(ped, true, false)
CancelSharedEmote()
if CurrentAnimOptions?.ExitEmote then
local options = CurrentAnimOptions or {}
if not RP[options.ExitEmote] then
DebugPrint("Exit emote was invalid")
ClearPedTasks(ped)
IsInAnimation = false
return
end
OnEmotePlay(options.ExitEmote)
DebugPrint("Playing exit animation")
local animationOptions = RP[options.ExitEmote].AnimationOptions
if animationOptions and animationOptions.EmoteDuration then
InExitEmote = true
SetTimeout(animationOptions.EmoteDuration, function()
InExitEmote = false
DestroyAllProps(true)
ClearPedTasks(ped)
OnEmotePlay(name, textureVariation)
ExitAndPlay = false
end)
return
end
else
ClearPedTasks(ped)
IsInAnimation = false
ExitAndPlay = false
DestroyAllProps(true)
OnEmotePlay(name, CurrentTextureVariation)
end
end
RegisterNetEvent('animations:client:EmoteCommandStart', function(args)
EmoteCommandStart(args)
end)
CreateExport("EmoteCommandStart", function(emoteName, textureVariation)
EmoteCommandStart({ emoteName, textureVariation })
end)
CreateExport("EmoteCancel", EmoteCancel)
CreateExport("CanCancelEmote", function(State)
CanCancel = State == true
end)
CreateExport('IsPlayerInAnim', function()
return LocalPlayer.state.currentEmote
end)
CreateExport('getCurrentEmote', function()
return currentEmote
end)
-- Door stuff
local openingDoor = false
AddEventHandler('CEventOpenDoor', function(unk1)
if unk1[1] ~= PlayerPedId() then return end
if ShowPed then
return
end
if not IsInAnimation then
return
end
if openingDoor then
return
end
openingDoor = true
while IsPedOpeningADoor(PlayerPedId()) do
Wait(100)
end
openingDoor = false
Wait(200)
ClearPedTasks(PlayerPedId())
DestroyAllProps()
OnEmotePlay(CurrentAnimationName, CurrentTextureVariation)
end)
local isBumpingPed = false
local timeout = 500
AddEventHandler("CEventPlayerCollisionWithPed", function(unk1)
if unk1[1] ~= PlayerPedId() then return end
if not IsInAnimation then
return
end
if isBumpingPed then
timeout = 500
return
end
isBumpingPed = true
timeout = 500
-- We wait a bit to avoid collision with the ped resetting the animation again
while timeout > 0 do
Wait(100)
timeout = timeout - 100
end
if not IsInAnimation then
return
end
isBumpingPed = false
ClearPedTasks(PlayerPedId())
Wait(125)
DestroyAllProps()
OnEmotePlay(CurrentAnimationName, CurrentTextureVariation)
end)
AddEventHandler('onResourceStop', function(resource)
if resource ~= GetCurrentResourceName() then return end
local ped = PlayerPedId()
ClosePedMenu()
DestroyAllProps()
ClearPedTasksImmediately(ped)
DetachEntity(ped, true, false)
ResetPedMovementClipset(ped, 0.8)
end)

View file

@ -0,0 +1,521 @@
local isSearching = false
local rightPosition = { x = 1430, y = 200 }
local leftPosition = { x = 0, y = 200 }
local menuPosition = { x = 0, y = 200 }
local menuHeader = "shopui_title_sm_hangar"
if GetAspectRatio() > 2.0 then
rightPosition = { x = 1200, y = 100 }
leftPosition = { x = -250, y = 100 }
end
if Config.MenuPosition then
if Config.MenuPosition == "left" then
menuPosition = leftPosition
elseif Config.MenuPosition == "right" then
menuPosition = rightPosition
end
end
if Config.CustomMenuEnabled then
local txd = CreateRuntimeTxd('Custom_Menu_Head')
CreateRuntimeTextureFromImage(txd, 'Custom_Menu_Head', 'header.png')
menuHeader = "Custom_Menu_Head"
end
local _menuPool = NativeUI.CreatePool()
local mainMenu = NativeUI.CreateMenu(Config.MenuTitle or "", "", menuPosition["x"], menuPosition["y"], menuHeader, menuHeader)
_menuPool:Add(mainMenu)
local sharemenu, shareddancemenu, infomenu
local EmoteTable = {}
local DanceTable = {}
local AnimalTable = {}
local PropTable = {}
local WalkTable = {}
local FaceTable = {}
local ShareTable = {}
function AddEmoteMenu(menu)
local submenu = _menuPool:AddSubMenu(menu, Translate('emotes'), "", true, true)
if Config.Search then
submenu:AddItem(NativeUI.CreateItem(Translate('searchemotes'), ""))
table.insert(EmoteTable, Translate('searchemotes'))
end
local dancemenu = _menuPool:AddSubMenu(submenu, Translate('danceemotes'), "", true, true)
local animalmenu
if Config.AnimalEmotesEnabled then
animalmenu = _menuPool:AddSubMenu(submenu, Translate('animalemotes'), "", true, true)
table.insert(EmoteTable, Translate('animalemotes'))
end
local propmenu = _menuPool:AddSubMenu(submenu, Translate('propemotes'), "", true, true)
table.insert(EmoteTable, Translate('danceemotes'))
table.insert(EmoteTable, Translate('danceemotes'))
if Config.SharedEmotesEnabled then
sharemenu = _menuPool:AddSubMenu(submenu, Translate('shareemotes'),
Translate('shareemotesinfo'), true, true)
shareddancemenu = _menuPool:AddSubMenu(sharemenu, Translate('sharedanceemotes'), "", true, true)
table.insert(ShareTable, 'none')
table.insert(EmoteTable, Translate('shareemotes'))
end
if Config.Keybinding then
table.insert(EmoteTable, "keybinds")
submenu:AddItem(NativeUI.CreateItem(Translate('keybinds'), Translate('keybindsinfo') .. " /emotebind [~y~num4-9~w~] [~g~emotename~w~]"))
end
for a, b in PairsByKeys(RP.Emotes) do
local x, y, z = table.unpack(b)
submenu:AddItem(NativeUI.CreateItem(z, "/e (" .. a .. ")"))
table.insert(EmoteTable, a)
end
for a, b in PairsByKeys(RP.Dances) do
local name = '🤼 ' .. b[3]
dancemenu:AddItem(NativeUI.CreateItem(name, "/e (" .. a .. ")"))
if Config.SharedEmotesEnabled then
shareddancemenu:AddItem(NativeUI.CreateItem(name, "/nearby (" .. a .. ")"))
end
table.insert(DanceTable, a)
end
if Config.AnimalEmotesEnabled then
for a, b in PairsByKeys(RP.AnimalEmotes) do
local name = '🐶 ' .. b[3]
animalmenu:AddItem(NativeUI.CreateItem(name, "/e (" .. a .. ")"))
table.insert(AnimalTable, a)
end
end
if Config.SharedEmotesEnabled then
for a, b in PairsByKeys(RP.Shared) do
local name = b[3]
local shareitem = NativeUI.CreateItem(name, "/nearby (~g~" .. a .. "~w~)" .. (otheremotename and " " .. Translate('makenearby') .. " (~y~" .. otheremotename .. "~w~)" or ""))
sharemenu:AddItem(shareitem)
table.insert(ShareTable, a)
end
end
for a, b in PairsByKeys(RP.PropEmotes) do
local name = '📦 ' .. b[3]
local propitem = b.AnimationOptions.PropTextureVariations and
NativeUI.CreateListItem(name, b.AnimationOptions.PropTextureVariations, 1, "/e (" .. a .. ")") or
NativeUI.CreateItem(name, "/e (" .. a .. ")")
propmenu:AddItem(propitem)
table.insert(PropTable, a)
end
-- Ped Emote on Change Index
dancemenu.OnIndexChange = function(_, newindex)
ClearPedTaskPreview()
EmoteMenuStartClone(DanceTable[newindex], "Dances")
end
propmenu.OnIndexChange = function(_, newindex)
ClearPedTaskPreview()
EmoteMenuStartClone(PropTable[newindex], "PropEmotes")
end
submenu.OnIndexChange = function(_, newindex)
if newindex > 5 then
ClearPedTaskPreview()
EmoteMenuStartClone(EmoteTable[newindex], "Emotes")
end
end
dancemenu.OnMenuClosed = function()
ClearPedTaskPreview()
end
dancemenu.OnItemSelect = function(_, _, index)
EmoteMenuStart(DanceTable[index], "Dances")
end
if Config.AnimalEmotesEnabled then
animalmenu.OnItemSelect = function(_, _, index)
EmoteMenuStart(AnimalTable[index], "AnimalEmotes")
end
end
if Config.SharedEmotesEnabled then
sharemenu.OnItemSelect = function(_, _, index)
if ShareTable[index] ~= 'none' then
local target, distance = GetClosestPlayer()
if (distance ~= -1 and distance < 3) then
TriggerServerEvent("rpemotes:server:requestEmote", GetPlayerServerId(target), ShareTable[index])
SimpleNotify(Translate('sentrequestto') .. GetPlayerName(target))
else
SimpleNotify(Translate('nobodyclose'))
end
end
end
shareddancemenu.OnItemSelect = function(_, _, index)
local target, distance = GetClosestPlayer()
if (distance ~= -1 and distance < 3) then
TriggerServerEvent("rpemotes:server:requestEmote", GetPlayerServerId(target), DanceTable[index], 'Dances')
SimpleNotify(Translate('sentrequestto') .. GetPlayerName(target))
else
SimpleNotify(Translate('nobodyclose'))
end
end
end
propmenu.OnItemSelect = function(_, _, index)
EmoteMenuStart(PropTable[index], "PropEmotes")
end
propmenu.OnListSelect = function(_, item, itemIndex, listIndex)
EmoteMenuStart(PropTable[itemIndex], "PropEmotes", item:IndexToItem(listIndex).Value)
end
submenu.OnItemSelect = function(_, _, index)
if Config.Search and EmoteTable[index] == Translate('searchemotes') then
EmoteMenuSearch(submenu)
else
EmoteMenuStart(EmoteTable[index], "Emotes")
end
end
submenu.OnMenuClosed = function()
if not isSearching then
ClosePedMenu()
end
end
end
if Config.Search then
local ignoredCategories = {
["Walks"] = true,
["Expressions"] = true,
["Shared"] = not Config.SharedEmotesEnabled
}
function EmoteMenuSearch(lastMenu)
ClosePedMenu()
AddTextEntry("PM_NAME_CHALL", Translate('searchinputtitle'))
DisplayOnscreenKeyboard(1, "PM_NAME_CHALL", "", "", "", "", "", 30)
while UpdateOnscreenKeyboard() == 0 do
DisableAllControlActions(0)
Wait(100)
end
local input = GetOnscreenKeyboardResult()
if input ~= nil then
local results = {}
for a, b in pairs(RP) do
if not ignoredCategories[b.category] then
if string.find(string.lower(a), string.lower(input)) or (b[3] ~= nil and string.find(string.lower(b[3]), string.lower(input))) then
table.insert(results, { table = b.category, name = a, data = b })
end
end
end
if #results > 0 then
isSearching = true
local searchMenu = _menuPool:AddSubMenu(lastMenu, string.format('%s '..Translate('searchmenudesc')..' ~r~%s~w~', #results, input), "", true, true)
local sharedDanceMenu
if Config.SharedEmotesEnabled then
sharedDanceMenu = _menuPool:AddSubMenu(searchMenu, Translate('sharedanceemotes'), "", true, true)
end
table.sort(results, function(a, b) return a.name < b.name end)
for k, v in pairs(results) do
local desc = ""
if v.table == "Shared" then
local otheremotename = v.data[4]
if otheremotename == nil then
desc = "/nearby (~g~" .. v.name .. "~w~)"
else
desc = "/nearby (~g~" .. v.name .. "~w~) " .. Translate('makenearby') .. " (~y~" .. otheremotename .. "~w~)"
end
else
desc = "/e (" .. v.name .. ")"
end
if v.data.AnimationOptions and v.data.AnimationOptions.PropTextureVariations then
searchMenu:AddItem(NativeUI.CreateListItem(v.data[3], v.data.AnimationOptions.PropTextureVariations, 1, desc))
else
searchMenu:AddItem(NativeUI.CreateItem(v.data[3], desc))
end
if v.table == "Dances" and Config.SharedEmotesEnabled then
sharedDanceMenu:AddItem(NativeUI.CreateItem(v.data[3], ""))
end
end
searchMenu.OnMenuChanged = function()
isSearching = false
ShowPedMenu()
end
searchMenu.OnIndexChange = function(_, newindex)
local data = results[newindex]
ClearPedTaskPreview()
EmoteMenuStartClone(data.name, data.data.category)
end
searchMenu.OnItemSelect = function(_, _, index)
local data = results[index]
if data == Translate('sharedanceemotes') then return end
if data.table == "Shared" then
local target, distance = GetClosestPlayer()
if (distance ~= -1 and distance < 3) then
TriggerServerEvent("rpemotes:server:requestEmote", GetPlayerServerId(target), data.name)
SimpleNotify(Translate('sentrequestto') .. GetPlayerName(target))
else
SimpleNotify(Translate('nobodyclose'))
end
else
EmoteMenuStart(data.name, data.data.category)
end
end
searchMenu.OnListSelect = function(_, item, itemIndex, listIndex)
EmoteMenuStart(results[itemIndex].name, "PropEmotes", item:IndexToItem(listIndex).Value)
end
if Config.SharedEmotesEnabled then
if #sharedDanceMenu.Items > 0 then
table.insert(results, 1, Translate('sharedanceemotes'))
sharedDanceMenu.OnItemSelect = function(_, _, index)
if not LocalPlayer.state.canEmote then return end
local data = results[index]
local target, distance = GetClosestPlayer()
if (distance ~= -1 and distance < 3) then
TriggerServerEvent("rpemotes:server:requestEmote", GetPlayerServerId(target), data.name, 'Dances')
SimpleNotify(Translate('sentrequestto') .. GetPlayerName(target))
else
SimpleNotify(Translate('nobodyclose'))
end
end
else
sharedDanceMenu:Clear()
searchMenu:RemoveItemAt(1)
end
end
searchMenu.OnMenuClosed = function()
searchMenu:Clear()
lastMenu:RemoveItemAt(#lastMenu.Items)
_menuPool:RefreshIndex()
results = {}
end
_menuPool:RefreshIndex()
_menuPool:CloseAllMenus()
searchMenu:Visible(true)
ShowPedMenu()
else
SimpleNotify(string.format(Translate('searchnoresult')..' ~r~%s~w~', input))
end
end
end
end
function AddCancelEmote(menu)
local newitem = NativeUI.CreateItem(Translate('cancelemote'), Translate('cancelemoteinfo'))
menu:AddItem(newitem)
newitem.Activated = function()
EmoteCancel()
DestroyAllProps()
end
end
ShowPedPreview = function(menu)
menu.OnItemSelect = function(_, _, index)
if index == 1 then
isSearching = false
ShowPedMenu()
elseif index == 4 then
ShowPedMenu(true)
end
end
end
function AddWalkMenu(menu)
local submenu = _menuPool:AddSubMenu(menu, Translate('walkingstyles'), "", true, true)
local walkreset = NativeUI.CreateItem(Translate('normalreset'), Translate('resetdef'))
submenu:AddItem(walkreset)
table.insert(WalkTable, Translate('resetdef'))
local sortedWalks = {}
for a, b in PairsByKeys(RP.Walks) do
if b[1] == "move_m@injured" then
table.insert(sortedWalks, 1, {label = a, anim = b[1]})
else
table.insert(sortedWalks, {label = a, anim = b[1]})
end
end
for _, walk in ipairs(sortedWalks) do
submenu:AddItem(NativeUI.CreateItem(walk.label, "/walk (" .. string.lower(walk.label) .. ")"))
table.insert(WalkTable, walk.label)
end
submenu.OnItemSelect = function(_, item, index)
if item == walkreset then
ResetWalk()
DeleteResourceKvp("walkstyle")
else
WalkMenuStart(WalkTable[index])
end
end
end
function AddFaceMenu(menu)
local submenu = _menuPool:AddSubMenu(menu, Translate('moods'), "", true, true)
local facereset = NativeUI.CreateItem(Translate('normalreset'), Translate('resetdef'))
submenu:AddItem(facereset)
table.insert(FaceTable, "")
for name, data in PairsByKeys(RP.Expressions) do
local faceitem = NativeUI.CreateItem(data[2] or name, "")
submenu:AddItem(faceitem)
table.insert(FaceTable, name)
end
submenu.OnIndexChange = function(_, newindex)
EmoteMenuStartClone(FaceTable[newindex], "Expressions")
end
submenu.OnItemSelect = function(_, item, index)
if item == facereset then
DeleteResourceKvp("Expressions")
ClearFacialIdleAnimOverride(PlayerPedId())
else
EmoteMenuStart(FaceTable[index], "Expressions")
end
end
submenu.OnMenuClosed = function()
ClosePedMenu()
end
end
function AddInfoMenu(menu)
infomenu = _menuPool:AddSubMenu(menu, Translate('infoupdate'), "~h~~y~The RPEmotes Developers~h~~y~", true, true)
for _,v in ipairs(Config.Credits) do
local item = NativeUI.CreateItem(v.title,v.subtitle or "")
infomenu:AddItem(item)
end
end
function OpenEmoteMenu()
if IsEntityDead(PlayerPedId()) then
-- show in chat
TriggerEvent('chat:addMessage', {
color = {255, 0, 0},
multiline = true,
args = {"RPEmotes", Translate('dead')}
})
return
end
if (IsPedSwimming(PlayerPedId()) or IsPedSwimmingUnderWater(PlayerPedId())) and not Config.AllowInWater then
-- show in chat
TriggerEvent('chat:addMessage', {
color = {255, 0, 0},
multiline = true,
args = {"RPEmotes", Translate('swimming')}
})
return
end
if _menuPool:IsAnyMenuOpen() then
_menuPool:CloseAllMenus()
else
mainMenu:Visible(true)
ProcessMenu()
end
end
CreateThread(function()
LoadAddonEmotes()
AddEmoteMenu(mainMenu)
AddCancelEmote(mainMenu)
if Config.PreviewPed then
ShowPedPreview(mainMenu)
end
if Config.WalkingStylesEnabled then
AddWalkMenu(mainMenu)
end
if Config.ExpressionsEnabled then
AddFaceMenu(mainMenu)
end
AddInfoMenu(mainMenu)
_menuPool:RefreshIndex()
local newRP = {}
for emoteType, content in pairs(RP) do
for emoteName, emoteData in pairs(content) do
local shouldRemove = false
if Config.AdultEmotesDisabled and emoteData.AdultAnimation then
shouldRemove = true
end
if newRP[emoteName] then
print('WARNING - Duplicate emote name found: ' .. emoteName .. ' in ' .. emoteType .. ' and ' .. newRP[emoteName].category)
end
if shouldRemove then
elseif type(emoteData) == "table" then
newRP[emoteName] = {}
for k, v in pairs(emoteData) do
newRP[emoteName][k] = v
end
newRP[emoteName].category = emoteType
else
newRP[emoteName] = { emoteData }
newRP[emoteName].category = emoteType
end
end
newRP[emoteType] = nil
end
RP = newRP
CONVERTED = true
end)
local isMenuProcessing = false
function ProcessMenu()
if isMenuProcessing then return end
isMenuProcessing = true
while _menuPool:IsAnyMenuOpen() do
_menuPool:ProcessMenus()
Wait(0)
end
isMenuProcessing = false
end
-- While ped is dead, don't show menus
CreateThread(function()
while true do
Wait(500)
if IsEntityDead(PlayerPedId()) then
_menuPool:CloseAllMenus()
end
if (IsPedSwimming(PlayerPedId()) or IsPedSwimmingUnderWater(PlayerPedId())) and not Config.AllowInWater then
-- cancel emote, destroy props and close menu
if IsInAnimation then
EmoteCancel()
end
_menuPool:CloseAllMenus()
end
end
end)

View file

@ -0,0 +1,50 @@
function SetPlayerPedExpression(expression, saveToKvp)
local emote = RP[expression]
if emote and emote.category == "Expressions" then
SetFacialIdleAnimOverride(PlayerPedId(), emote[1], 0)
if Config.PersistentExpression and saveToKvp then SetResourceKvp("expression", emote[1]) end
else
ClearFacialIdleAnimOverride(PlayerPedId())
DeleteResourceKvp("expression")
end
end
if Config.ExpressionsEnabled then
RegisterCommand('mood', function(_source, args, _raw)
local expression = FirstToUpper(string.lower(args[1]))
local emote = RP[expression]
if emote and emote.category == "Expressions" then
SetPlayerPedExpression(RP[expression][1], true)
elseif expression == "Reset" then
ClearFacialIdleAnimOverride(PlayerPedId())
DeleteResourceKvp("expression")
else
EmoteChatMessage("'" .. expression .. "' is not a valid mood")
end
end, false)
TriggerEvent('chat:addSuggestion', '/mood', 'Set your current mood/expression.',
{ { name = "expression", help = "/moods for a list of valid moods" } })
TriggerEvent('chat:addSuggestion', '/moods', 'List available walking moods/expressions.')
local function LoadPersistentExpression()
local expression = GetResourceKvpString("expression")
if expression then
Wait(2500)
SetPlayerPedExpression(expression, false)
end
end
if Config.PersistentExpression then
AddEventHandler('playerSpawned', LoadPersistentExpression)
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', LoadPersistentExpression)
RegisterNetEvent('esx:playerLoaded', LoadPersistentExpression)
end
AddEventHandler('onResourceStart', function(resource)
if resource == GetCurrentResourceName() then
LoadPersistentExpression()
end
end)
end

View file

@ -0,0 +1,86 @@
local function HandsUpLoop()
CreateThread(function()
while InHandsup do
if Config.DisabledHandsupControls then
for control, state in pairs(Config.DisabledHandsupControls) do
DisableControlAction(0, control, state)
end
end
if IsPlayerAiming(PlayerId()) then
ClearPedSecondaryTask(PlayerPedId())
CreateThread(function()
Wait(350)
InHandsup = false
end)
end
Wait(0)
end
end)
end
if Config.HandsupEnabled then
local function ToggleHandsUp(commandType)
RegisterCommand(commandType, function()
if IsPedInAnyVehicle(PlayerPedId(), false) and not Config.HandsupInCar and not InHandsup then
return
end
Handsup()
end, false)
end
if Config.HoldToHandsUp then
ToggleHandsUp('+handsup')
ToggleHandsUp('-handsup')
else
ToggleHandsUp('handsup')
end
function Handsup()
local playerPed = PlayerPedId()
if not IsPedHuman(playerPed) then
return
end
if IsInActionWithErrorMessage() then
return
end
InHandsup = not InHandsup
if InHandsup then
LocalPlayer.state:set('currentEmote', 'handsup', true)
DestroyAllProps()
local dict = "random@mugging3"
RequestAnimDict(dict)
while not HasAnimDictLoaded(dict) do
Wait(0)
end
TaskPlayAnim(PlayerPedId(), dict, "handsup_standing_base", 3.0, 3.0, -1, 49, 0, false,
IsThisModelABike(GetEntityModel(GetVehiclePedIsIn(PlayerPedId(), false))) and 4127 or false, false)
HandsUpLoop()
else
LocalPlayer.state:set('currentEmote', nil, true)
ClearPedSecondaryTask(PlayerPedId())
if Config.ReplayEmoteAfterHandsup and IsInAnimation then
local emote = RP[CurrentAnimationName]
if not emote then
return
end
Wait(400)
DestroyAllProps()
OnEmotePlay(CurrentAnimationName, CurrentTextureVariation)
end
end
end
TriggerEvent('chat:addSuggestion', '/handsup', Translate('handsup'))
if Config.HandsupKeybindEnabled then
RegisterKeyMapping("handsup", Translate('register_handsup'), "keyboard", Config.HandsupKeybind)
end
CreateExport('IsPlayerInHandsUp', function()
return InHandsup
end)
end

View file

@ -0,0 +1,96 @@
CreateThread(function()
TriggerEvent('chat:addSuggestion', '/e', Translate('play_emote'),
{ { name = "emotename", help = Translate('help_command') },
{ name = "texturevariation", help = Translate('help_variation') } })
TriggerEvent('chat:addSuggestion', '/emote', Translate('play_emote'),
{ { name = "emotename", help = Translate('help_command') },
{ name = "texturevariation", help = Translate('help_variation') } })
if Config.Keybinding then
TriggerEvent('chat:addSuggestion', '/emotebind', Translate('link_emote_keybind'),
{ { name = "key", help = "num4, num5, num6, num7. num8, num9. Numpad 4-9!" },
{ name = "emotename", help = Translate('help_command') } })
TriggerEvent('chat:addSuggestion', '/emotebinds', Translate('show_emote_keybind'))
TriggerEvent('chat:addSuggestion', '/emotedelete', Translate('remove_emote_keybind'),
{ { name = "key", help = "num4, num5, num6, num7. num8, num9. Numpad 4-9!" } })
end
TriggerEvent('chat:addSuggestion', '/emotemenu', Translate('open_menu_emote'))
TriggerEvent('chat:addSuggestion', '/emotes', Translate('show_list_emote'))
TriggerEvent('chat:addSuggestion', '/emotecancel', Translate('cancel_emote'))
end)
RegisterCommand('e', function(source, args, raw) EmoteCommandStart(args) end, false)
RegisterCommand('emote', function(source, args, raw) EmoteCommandStart(args) end, false)
RegisterCommand('emotecancel', function() EmoteCancel() end, false)
if Config.MenuKeybindEnabled then
RegisterCommand('emoteui', function() OpenEmoteMenu() end, false)
RegisterKeyMapping("emoteui", Translate('register_open_menu'), "keyboard", Config.MenuKeybind)
else
RegisterCommand('emotemenu', function() OpenEmoteMenu() end, false)
end
if Config.EnableCancelKeybind then
RegisterKeyMapping("emotecancel", Translate('register_cancel_emote'), "keyboard", Config.CancelEmoteKey)
end
-- BINDING EMOTES TO KEYS
if Config.Keybinding then
RegisterCommand('emotebind', function(source, args, raw) EmoteBindStart(source, args, raw) end, false)
RegisterCommand('emotebinds', function(source, args, raw) ListKeybinds() end, false)
RegisterCommand('emotedelete', function(source, args) DeleteEmote(args) end, false)
for i = 1, #Config.KeybindKeys do
local cmd = string.format('emoteSelect%s', i)
RegisterCommand(cmd, function()
local emote = GetResourceKvpString(string.format('%s_emob%s', Config.keybindKVP, i))
if emote and emote ~= "" then
EmoteCommandStart({ emote, 0 })
end
end, false)
RegisterKeyMapping(cmd, string.format('Emote bind %s', i), 'keyboard', Config.KeybindKeys[i])
end
function EmoteBindStart(source, args, raw)
if #args > 0 then
local numkey = tonumber(args[1])
local emote = string.lower(args[2])
if not (numkey and emote) then
DebugPrint('Invalid arguments to EmoteBindStart')
return
end
if type(numkey) == "number" then
if RP[emote] then
SetResourceKvp(string.format('%s_emob%s', Config.keybindKVP, numkey), emote)
else
EmoteChatMessage("'" .. emote .. "' " .. Translate('notvalidemote') .. "")
end
else
EmoteChatMessage("'" .. numkey .. "' " .. Translate('notvalidkey'))
end
else
DebugPrint('Invalid number of arguments to EmoteBindStart')
end
end
function ListKeybinds()
for i = 1, #Config.KeybindKeys do
local emote = GetResourceKvpString(string.format('%s_emob%s', Config.keybindKVP, i))
if emote then
EmoteChatMessage(string.format('Emote %s : %s',i, emote))
end
end
end
function DeleteEmote(args)
if #args > 0 then
local numkey = tonumber(args[1])
if type(numkey) == "number" then
DeleteResourceKvp(string.format('%s_emob%s', Config.keybindKVP, numkey))
else
EmoteChatMessage("'" .. numkey .. "' " .. Translate('notvalidkey'))
end
else
DebugPrint("invalid")
end
end
end

View file

@ -0,0 +1,245 @@
IsUsingNewscam = false
if Config.NewscamEnabled then
RegisterCommand("newscam", function()
UseNewscam()
end, false)
TriggerEvent('chat:addSuggestion', '/newscam', 'Use newscam', {})
local fov = 40.0
local index = 0
local scaleform_instructions
local scaleform_news
local prop_newscam
local msg = "YOUR TEXT HERE"
local bottom = "YOUR TEXT HERE"
local title = "YOUR TEXT HERE"
local instructions = true
local cam
local function CleanupNewscam()
ClearPedTasks(PlayerPedId())
ClearTimecycleModifier()
RenderScriptCams(false, false, 0, true, false)
SetScaleformMovieAsNoLongerNeeded(breaking_news)
SetScaleformMovieAsNoLongerNeeded(scaleform_instructions)
DestroyCam(cam, false)
if prop_newscam then
DeleteEntity(prop_newscam)
end
SetNightvision(false)
SetSeethrough(false)
end
function UseNewscam()
if IsPedSittingInAnyVehicle(PlayerPedId()) then
return
end
if IsInActionWithErrorMessage({ ['IsUsingNewscam'] = true }) then
return
end
IsUsingNewscam = not IsUsingNewscam
if IsUsingNewscam then
CreateThread(function()
DestroyAllProps()
ClearPedTasks(PlayerPedId())
RequestAnimDict("missfinale_c2mcs_1")
while not HasAnimDictLoaded("missfinale_c2mcs_1") do
Wait(5)
end
-- attach the prop to the player
local boneIndex = GetPedBoneIndex(PlayerPedId(), 28422)
local x, y, z = table.unpack(GetEntityCoords(PlayerPedId(), true))
if not HasModelLoaded("prop_v_cam_01") then
LoadPropDict("prop_v_cam_01")
end
prop_newscam = CreateObject(`prop_v_cam_01`, x, y, z + 0.2, true, true, true)
AttachEntityToEntity(prop_newscam, PlayerPedId(), boneIndex, 0.0, 0.03, 0.01, 0.0, 0.0, 0.0, true, true, false, true, 1, true)
TaskPlayAnim(PlayerPedId(), "missfinale_c2mcs_1", "fin_c2_mcs_1_camman", 5.0, 5.0, -1, 51, 0, false, false, false)
PlayAmbientSpeech1(PlayerPedId(), "GENERIC_CURSE_MED", "SPEECH_PARAMS_FORCE")
SetCurrentPedWeapon(PlayerPedId(), `WEAPON_UNARMED`, true)
RemoveAnimDict("missfinale_c2mcs_1")
SetModelAsNoLongerNeeded("prop_v_cam_01")
end)
Wait(200)
SetTimecycleModifier("default")
SetTimecycleModifierStrength(0.3)
local breaking_news = RequestScaleformMovie("breaking_news")
while not HasScaleformMovieLoaded(breaking_news) do
Wait(10)
end
PushScaleformMovieFunction(breaking_news, "breaking_news")
PopScaleformMovieFunctionVoid()
BeginScaleformMovieMethod(breaking_news, 'SET_TEXT')
PushScaleformMovieMethodParameterString(msg)
PushScaleformMovieMethodParameterString(bottom)
EndScaleformMovieMethod()
BeginScaleformMovieMethod(breaking_news, 'SET_SCROLL_TEXT')
PushScaleformMovieMethodParameterInt(0) -- top ticker
PushScaleformMovieMethodParameterInt(0) -- Since this is the first string, start at 0
PushScaleformMovieMethodParameterString(title)
EndScaleformMovieMethod()
BeginScaleformMovieMethod(breaking_news, 'DISPLAY_SCROLL_TEXT')
PushScaleformMovieMethodParameterInt(0) -- Top ticker
PushScaleformMovieMethodParameterInt(0) -- Index of string
EndScaleformMovieMethod()
scaleform_news = breaking_news
cam = CreateCam("DEFAULT_SCRIPTED_FLY_CAMERA", true)
AttachCamToEntity(cam, PlayerPedId(), 0.0, 0.0, 1.2, true)
SetCamRot(cam, 0.0, 0.0, GetEntityHeading(PlayerPedId()))
SetCamFov(cam, fov)
RenderScriptCams(true, false, 0, true, false)
scaleform_instructions = SetupButtons({
{ key = 177, text = 'exit_news' },
{ key = 19, text = 'toggle_news_vision' },
{ key = 74, text = "edit_values_newscam" },
{ key = 47, text = 'toggle_instructions' }
})
while IsUsingNewscam and not IsEntityDead(PlayerPedId()) and not IsPedSittingInAnyVehicle(PlayerPedId()) do
if IsControlJustPressed(0, 177) then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
IsUsingNewscam = false
end
fov = HandleZoomAndCheckRotation(cam, fov)
HideHUDThisFrame()
DisableControlAction(0, 25, true) -- disable aim
DisableControlAction(0, 44, true) -- INPUT_COVER
DisableControlAction(0, 37, true) -- INPUT_SELECT_WEAPON
DisableControlAction(0, 24, true) -- Attack
DisablePlayerFiring(PlayerPedId(), true) -- Disable weapon firing
if IsControlJustPressed(0, 19) then
-- if index = 0, show the "security_camera" scaleform, if index = 1, show the "breaking_news" scaleform and reset the index to 0
if index == 0 then
index = 1
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
scaleform_news = nil
CreateThread(function()
while index == 1 do
DrawRect(0.0, 0.0, 2.0, 0.2, 0, 0, 0, 255)
DrawRect(0.0, 1.0, 2.0, 0.2, 0, 0, 0, 255)
Wait(1)
end
end)
else
index = 0
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
scaleform_news = breaking_news
end
end
if IsControlJustPressed(0, 74) then
SetMsgBottomTitle()
end
if IsControlJustPressed(0, 47) then
instructions = not instructions
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
DrawScaleformMovieFullscreen(scaleform_news, 255, 255, 255, 255)
if instructions then
DrawScaleformMovieFullscreen(scaleform_instructions, 255, 255, 255, 255)
end
Wait(1)
end
end
-- RESET EVERYTHING
IsUsingNewscam = false
index = 0
CleanupNewscam()
end
function SetMsgBottomTitle()
-- keyboard input to set the message and bottom title
AddTextEntry("top", "Enter the top message of the news")
DisplayOnscreenKeyboard(1, "top", "", "", "", "", "", 200)
while (UpdateOnscreenKeyboard() == 0) do
DisableAllControlActions(0);
Wait(0);
end
if (GetOnscreenKeyboardResult()) then
title = tostring(GetOnscreenKeyboardResult())
end
AddTextEntry("bottom", "Enter the bottom title of the news")
DisplayOnscreenKeyboard(1, "bottom", "", "", "", "", "", 200)
while (UpdateOnscreenKeyboard() == 0) do
DisableAllControlActions(0);
Wait(0);
end
if (GetOnscreenKeyboardResult()) then
bottom = tostring(GetOnscreenKeyboardResult())
end
AddTextEntry("title", "Enter the title of the news")
DisplayOnscreenKeyboard(1, "title", "", "", "", "", "", 200)
while (UpdateOnscreenKeyboard() == 0) do
DisableAllControlActions(0);
Wait(0);
end
if (GetOnscreenKeyboardResult()) then
msg = tostring(GetOnscreenKeyboardResult())
end
-- reset the scaleform and set the new values
SetScaleformMovieAsNoLongerNeeded(breaking_news)
breaking_news = RequestScaleformMovie("breaking_news")
while not HasScaleformMovieLoaded(breaking_news) do
Wait(10)
end
PushScaleformMovieFunction(breaking_news, "breaking_news")
PopScaleformMovieFunctionVoid()
BeginScaleformMovieMethod(breaking_news, 'SET_TEXT')
PushScaleformMovieMethodParameterString(msg)
PushScaleformMovieMethodParameterString(bottom)
EndScaleformMovieMethod()
BeginScaleformMovieMethod(breaking_news, 'SET_SCROLL_TEXT')
PushScaleformMovieMethodParameterInt(0) -- top ticker
PushScaleformMovieMethodParameterInt(0) -- Since this is the first string, start at 0
PushScaleformMovieMethodParameterString(title)
EndScaleformMovieMethod()
BeginScaleformMovieMethod(breaking_news, 'DISPLAY_SCROLL_TEXT')
PushScaleformMovieMethodParameterInt(0) -- Top ticker
PushScaleformMovieMethodParameterInt(0) -- Index of string
EndScaleformMovieMethod()
end
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() then
CleanupNewscam()
end
end)
CreateExport('toggleNewscam', function()
UseNewscam()
end)
end

View file

@ -0,0 +1,35 @@
if not Config.DisableIdleCam then return end
RegisterCommand('idlecamoff', function() -- help2 31, 167, 9
TriggerEvent('chat:addMessage', {
color = {227,8,0},
multiline = true,
args = {'[RPEmotes]', 'Idle Cam Is Now Off'}
})
DisableIdleCamera(true)
SetPedCanPlayAmbientAnims(PlayerPedId(), false)
SetResourceKvpInt("idleCamToggle", 1)
end, false)
RegisterCommand('idlecamon', function() -- help2 31, 167, 9
TriggerEvent('chat:addMessage', {
color = {31,167,9},
multiline = true,
args = {'[RPEmotes]', 'Idle Cam Is Now On'}
})
DisableIdleCamera(false)
SetPedCanPlayAmbientAnims(PlayerPedId(), true)
SetResourceKvpInt("idleCamToggle", 2)
end, false)
CreateThread(function()
TriggerEvent("chat:addSuggestion", "/idlecamon", "Re-enables the idle cam")
TriggerEvent("chat:addSuggestion", "/idlecamoff", "Disables the idle cam")
local idleCamKvp = GetResourceKvpInt("idleCamToggle")
if idleCamKvp == 0 then
return
end
DisableIdleCamera(idleCamKvp == 1)
end)

View file

@ -0,0 +1,65 @@
local PlayerParticles = {}
function PtfxThis(asset)
while not HasNamedPtfxAssetLoaded(asset) do
RequestNamedPtfxAsset(asset)
Wait(10)
end
UseParticleFxAsset(asset)
end
function PtfxStart()
LocalPlayer.state:set('ptfx', true, true)
end
function PtfxStop()
LocalPlayer.state:set('ptfx', nil, true)
end
AddStateBagChangeHandler('ptfx', '', function(bagName, key, value, _unused, replicated)
local plyId = tonumber(bagName:gsub('player:', ''), 10)
if (PlayerParticles[plyId] and value) or (not PlayerParticles[plyId] and not value) then return end
local ply = GetPlayerFromServerId(plyId)
if ply <= 0 then return end
local plyPed = GetPlayerPed(ply)
if not DoesEntityExist(plyPed) then return end
local stateBag = Player(plyId).state
if value then
local boneIndex = stateBag.ptfxBone and GetPedBoneIndex(plyPed, stateBag.ptfxBone) or GetEntityBoneIndexByName(stateBag.ptfxName, "VFX")
local entityTarget = plyPed
if stateBag.ptfxPropNet then
local propObj = NetToObj(stateBag.ptfxPropNet)
if DoesEntityExist(propObj) then
entityTarget = propObj
end
end
PtfxThis(stateBag.ptfxAsset)
local offset = stateBag.ptfxOffset
local rot = stateBag.ptfxRot
PlayerParticles[plyId] = StartNetworkedParticleFxLoopedOnEntityBone(stateBag.ptfxName, entityTarget, offset.x, offset.y, offset.z, rot.x, rot.y, rot.z, boneIndex, (stateBag.ptfxScale or 1) + 0.0, false, false, false)
local color = stateBag.ptfxColor
if color then
if color[1] and type(color[1]) == 'table' then
local randomIndex = math.random(1, #color)
color = color[randomIndex]
end
SetParticleFxLoopedAlpha(PlayerParticles[plyId], color.A)
SetParticleFxLoopedColour(PlayerParticles[plyId], color.R / 255, color.G / 255, color.B / 255, false)
end
DebugPrint("Started PTFX: " .. PlayerParticles[plyId])
else
DebugPrint("Stopped PTFX: " .. PlayerParticles[plyId])
StopParticleFxLooped(PlayerParticles[plyId], false)
RemoveNamedPtfxAsset(stateBag.ptfxAsset)
PlayerParticles[plyId] = nil
end
end)

View file

@ -0,0 +1,124 @@
Pointing = false
local function IsPlayerAiming(player)
return IsPlayerFreeAiming(player) or IsAimCamActive() or IsAimCamThirdPersonActive()
end
local function CanPlayerPoint()
local playerPed = PlayerPedId()
local playerId = PlayerId()
if not DoesEntityExist(playerPed) or IsPedOnAnyBike(playerPed) or IsPlayerAiming(playerId) or IsPedFalling(playerPed) or IsPedInjured(playerPed) or IsPedInMeleeCombat(playerPed) or IsPedRagdoll(playerPed) or not IsPedHuman(playerPed) then
return false
end
return true
end
local function PointingStopped()
local playerPed = PlayerPedId()
RequestTaskMoveNetworkStateTransition(playerPed, 'Stop')
SetPedConfigFlag(playerPed, 36, false)
if not IsPedInjured(playerPed) then
ClearPedSecondaryTask(playerPed)
end
RemoveAnimDict("anim@mp_point")
if Config.ReplayEmoteAfterPointing and IsInAnimation then
local emote = RP[CurrentAnimationName]
if not emote then
return
end
Wait(400)
DestroyAllProps()
OnEmotePlay(CurrentAnimationName, CurrentTextureVariation)
end
end
local function PointingThread()
CreateThread(function()
local playerPed = PlayerPedId()
while Pointing do
Wait(0)
if not CanPlayerPoint() then
Pointing = false
break
end
local camPitch = GetGameplayCamRelativePitch()
if camPitch < -70.0 then
camPitch = -70.0
elseif camPitch > 42.0 then
camPitch = 42.0
end
camPitch = (camPitch + 70.0) / 112.0
local camHeading = GetGameplayCamRelativeHeading()
local cosCamHeading = math.cos(camHeading)
local sinCamHeading = math.sin(camHeading)
if camHeading < -180.0 then
camHeading = -180.0
elseif camHeading > 180.0 then
camHeading = 180.0
end
camHeading = (camHeading + 180.0) / 360.0
local coords = GetOffsetFromEntityInWorldCoords(playerPed, (cosCamHeading * -0.2) - (sinCamHeading * (0.4 * camHeading + 0.3)), (sinCamHeading * -0.2) + (cosCamHeading * (0.4 * camHeading + 0.3)), 0.6)
local _, blocked = GetShapeTestResult(StartShapeTestCapsule(coords.x, coords.y, coords.z - 0.2, coords.x, coords.y, coords.z + 0.2, 0.4, 95, playerPed, 7))
SetTaskMoveNetworkSignalFloat(playerPed, 'Pitch', camPitch)
SetTaskMoveNetworkSignalFloat(playerPed, 'Heading', (camHeading * -1.0) + 1.0)
SetTaskMoveNetworkSignalBool(playerPed, 'isBlocked', blocked)
SetTaskMoveNetworkSignalBool(playerPed, 'isFirstPerson', GetCamViewModeForContext(GetCamActiveViewModeContext()) == 4)
end
PointingStopped()
end)
end
local function StartPointing()
if IsInActionWithErrorMessage() then
return
end
if not CanPlayerPoint() then
return
end
Pointing = not Pointing
if Pointing and LoadAnim("anim@mp_point") then
SetPedConfigFlag(PlayerPedId(), 36, true)
TaskMoveNetworkByName(PlayerPedId(), 'task_mp_pointing', 0.5, false, 'anim@mp_point', 24)
DestroyAllProps()
PointingThread()
end
end
-- Commands & KeyMapping --
if Config.PointingEnabled then
RegisterCommand('pointing', function()
if IsPedInAnyVehicle(PlayerPedId(), false) and not Config.PointingInCar then
return
end
StartPointing()
end, false)
if Config.PointingKeybindEnabled then
RegisterKeyMapping("pointing", Translate('register_pointing'), "keyboard", Config.PointingKeybind)
end
TriggerEvent('chat:addSuggestion', '/pointing', Translate('pointing'))
end
---@return boolean
local function IsPlayerPointing()
return Pointing
end
CreateExport('IsPlayerPointing', IsPlayerPointing)

View file

@ -0,0 +1,33 @@
if Config.RagdollEnabled then
RegisterCommand('+ragdoll', function() Ragdoll() end, false)
RegisterCommand('-ragdoll', function() StopRagdoll() end, false)
RegisterKeyMapping('+ragdoll', Translate('register_ragdoll'), 'keyboard', Config.RagdollKeybind)
local isRagdolling = true
function Ragdoll()
if IsInAnimation then return end
local ped = PlayerPedId()
if not IsPedOnFoot(ped) then return end
if Config.RagdollAsToggle then
isRagdolling = not isRagdolling
else
isRagdolling = true
end
while isRagdolling do
ped = PlayerPedId()
SetPedRagdollForceFall(ped)
ResetPedRagdollTimer(ped)
SetPedToRagdoll(ped, 1000, 1000, 3, false, false, false)
ResetPedRagdollTimer(ped)
Wait(0)
end
end
function StopRagdoll()
if Config.RagdollAsToggle then return end
isRagdolling = false
end
end

View file

@ -0,0 +1,164 @@
local isRequestAnim = false
local targetPlayerId
if Config.SharedEmotesEnabled then
RegisterCommand('nearby', function(source, args, raw)
if not LocalPlayer.state.canEmote then return end
if IsPedInAnyVehicle(PlayerPedId(), true) then
return EmoteChatMessage(Translate('not_in_a_vehicle'))
end
if #args > 0 then
local emotename = string.lower(args[1])
local target, distance = GetClosestPlayer()
if (distance ~= -1 and distance < 3) then
if RP[emotename] ~= nil and RP[emotename].category == "Shared" then
local _, _, ename = table.unpack(RP[emotename])
TriggerServerEvent("rpemotes:server:requestEmote", GetPlayerServerId(target), emotename)
SimpleNotify(Translate('sentrequestto') ..
GetPlayerName(target) .. " ~w~(~g~" .. ename .. "~w~)")
else
EmoteChatMessage("'" .. emotename .. "' " .. Translate('notvalidsharedemote') .. "")
end
else
SimpleNotify(Translate('nobodyclose'))
end
else
NearbysOnCommand()
end
end, false)
end
RegisterNetEvent("rpemotes:client:syncEmote", function(emote, player)
EmoteCancel()
Wait(300)
targetPlayerId = player
local plyServerId = GetPlayerFromServerId(player)
if IsPedInAnyVehicle(GetPlayerPed(plyServerId ~= 0 and plyServerId or GetClosestPlayer()), true) then
return EmoteChatMessage(Translate('not_in_a_vehicle'))
end
if RP[emote] then
local options = RP[emote].AnimationOptions
if options and options.Attachto then
local targetEmote = RP[emote][4]
if not targetEmote or not RP[targetEmote] or not RP[targetEmote].AnimationOptions or not RP[targetEmote].AnimationOptions.Attachto then
local ped = PlayerPedId()
local pedInFront = GetPlayerPed(plyServerId ~= 0 and plyServerId or GetClosestPlayer())
AttachEntityToEntity(
ped,
pedInFront,
GetPedBoneIndex(pedInFront, options.bone or -1),
options.xPos or 0.0,
options.yPos or 0.0,
options.zPos or 0.0,
options.xRot or 0.0,
options.yRot or 0.0,
options.zRot or 0.0,
false,
false,
false,
true,
1,
true
)
end
end
OnEmotePlay(emote)
return
else
DebugPrint("rpemotes:client:syncEmote : Emote not found")
end
end)
RegisterNetEvent("rpemotes:client:syncEmoteSource", function(emote, player)
local ped = PlayerPedId()
local plyServerId = GetPlayerFromServerId(player)
local pedInFront = GetPlayerPed(plyServerId ~= 0 and plyServerId or GetClosestPlayer())
if IsPedInAnyVehicle(ped, true) or IsPedInAnyVehicle(pedInFront, true) then
return EmoteChatMessage(Translate('not_in_a_vehicle'))
end
local options = RP[emote] and RP[emote].AnimationOptions
if options then
if (options.Attachto) then
AttachEntityToEntity(
ped,
pedInFront,
GetPedBoneIndex(pedInFront, options.bone or -1),
options.xPos or 0.0,
options.yPos or 0.0,
options.zPos or 0.0,
options.xRot or 0.0,
options.yRot or 0.0,
options.zRot or 0.0,
false,
false,
false,
true,
1,
true
)
end
end
local coords = GetOffsetFromEntityInWorldCoords(pedInFront, (options and options.SyncOffsetSide or 0) + 0.0, (options and options.SyncOffsetFront or 1) + 0.0, (options and options.SyncOffsetHeight or 0) + 0.0)
local heading = GetEntityHeading(pedInFront)
SetEntityHeading(ped, heading - (options and options.SyncOffsetHeading or 180) + 0.0)
SetEntityCoordsNoOffset(ped, coords.x, coords.y, coords.z)
EmoteCancel()
Wait(300)
targetPlayerId = player
if RP[emote] ~= nil then
OnEmotePlay(emote)
return
end
end)
RegisterNetEvent("rpemotes:client:cancelEmote", function(player)
if targetPlayerId and targetPlayerId == player then
targetPlayerId = nil
EmoteCancel()
end
end)
function CancelSharedEmote()
if targetPlayerId then
TriggerServerEvent("rpemotes:server:cancelEmote", targetPlayerId)
targetPlayerId = nil
end
end
RegisterNetEvent("rpemotes:client:requestEmote", function(emotename, etype, target)
isRequestAnim = true
local displayed = RP[emotename] and select(3, table.unpack(RP[emotename]))
PlaySound(-1, "NAV", "HUD_AMMO_SHOP_SOUNDSET", false, 0, true)
SimpleNotify(Translate('doyouwanna') .. displayed .. "~w~)")
-- The player has now 10 seconds to accept the request
local timer = 10 * 1000
while isRequestAnim do
Wait(5)
timer = timer - 5
if timer <= 0 then
isRequestAnim = false
SimpleNotify(Translate('refuseemote'))
end
if IsControlJustPressed(1, 246) then
isRequestAnim = false
local otheremote = RP[emotename] and RP[emotename][4] or emotename
TriggerServerEvent("rpemotes:server:confirmEmote", target, emotename, otheremote)
elseif IsControlJustPressed(1, 182) then
isRequestAnim = false
SimpleNotify(Translate('refuseemote'))
end
end
end)

View file

@ -0,0 +1,395 @@
-- You can edit this function to add support for your favorite notification system
function SimpleNotify(message)
if Config.NotificationsAsChatMessage then
TriggerEvent("chat:addMessage", { color = { 255, 255, 255 }, args = { tostring(message) } })
else
BeginTextCommandThefeedPost("STRING")
AddTextComponentSubstringPlayerName(message)
EndTextCommandThefeedPostTicker(true, true)
end
end
-- Don't touch after this line if you don't know what you're doing
CreateExport = function(name, func)
AddEventHandler('__cfx_export_rpemotes_'..name, function(setCb)
setCb(function(...)
return func(...)
end)
end)
exports(name, func)
end
function DebugPrint(...)
if Config.DebugDisplay then
print(...)
end
end
function FirstToUpper(str)
return (str:gsub("^%l", string.upper))
end
function IsPlayerAiming(player)
return (IsPlayerFreeAiming(player) or IsAimCamActive() or IsAimCamThirdPersonActive()) and
tonumber(GetSelectedPedWeapon(player)) ~= tonumber(GetHashKey("WEAPON_UNARMED"))
end
function CanPlayerCrouchCrawl(playerPed)
if not IsPedOnFoot(playerPed) or IsPedJumping(playerPed) or IsPedFalling(playerPed) or IsPedInjured(playerPed) or IsPedInMeleeCombat(playerPed) or IsPedRagdoll(playerPed) then
return false
end
return true
end
function PlayAnimOnce(playerPed, animDict, animName, blendInSpeed, blendOutSpeed, duration, startTime)
LoadAnim(animDict)
TaskPlayAnim(playerPed, animDict, animName, blendInSpeed or 2.0, blendOutSpeed or 2.0, duration or -1, 0,
startTime or 0.0, false, false, false)
RemoveAnimDict(animDict)
end
function ChangeHeadingSmooth(playerPed, amount, time)
local times = math.abs(amount)
local test = amount / times
local wait = time / times
for _i = 1, times do
Wait(wait)
SetEntityHeading(playerPed, GetEntityHeading(playerPed) + test)
end
end
function EmoteChatMessage(msg, multiline)
if msg then
TriggerEvent("chat:addMessage", {
multiline = multiline == true or false,
color = { 255, 255, 255 },
args = { "^1Help^0", tostring(msg) }
})
end
end
function PairsByKeys(t, f)
local a = {}
for n in pairs(t) do
table.insert(a, n)
end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function() -- iterator function
i = i + 1
if a[i] == nil then
return nil
else
return a[i], t[a[i]]
end
end
return iter
end
function LoadAnim(dict)
if not DoesAnimDictExist(dict) then
return false
end
local timeout = 2000
while not HasAnimDictLoaded(dict) and timeout > 0 do
RequestAnimDict(dict)
Wait(5)
timeout = timeout - 5
end
if timeout == 0 then
DebugPrint("Loading anim dict " .. dict .. " timed out")
return false
else
return true
end
end
function LoadPropDict(model)
if not HasModelLoaded(GetHashKey(model)) then
RequestModel(GetHashKey(model))
local timeout = 2000
while not HasModelLoaded(GetHashKey(model)) and timeout > 0 do
Wait(5)
timeout = timeout - 5
end
if timeout == 0 then
DebugPrint("Loading model " .. model .. " timed out")
return
end
end
end
function TableHasKey(table, key)
return table[key] ~= nil
end
function RequestWalking(set)
local timeout = GetGameTimer() + 5000
while not HasAnimSetLoaded(set) and GetGameTimer() < timeout do
RequestAnimSet(set)
Wait(5)
end
end
function GetPedInFront()
local player = PlayerId()
local plyPed = GetPlayerPed(player)
local plyPos = GetEntityCoords(plyPed, false)
local plyOffset = GetOffsetFromEntityInWorldCoords(plyPed, 0.0, 1.3, 0.0)
local rayHandle = StartShapeTestCapsule(plyPos.x, plyPos.y, plyPos.z, plyOffset.x, plyOffset.y, plyOffset.z, 10.0, 12
, plyPed, 7)
local _, _, _, _, ped2 = GetShapeTestResult(rayHandle)
return ped2
end
function NearbysOnCommand(source, args, raw)
local NearbysCommand = ""
for a, b in PairsByKeys(RP) do
if type(b) == "table" and b.category == "Shared" then
NearbysCommand = NearbysCommand .. a .. ", "
end
end
EmoteChatMessage(NearbysCommand)
EmoteChatMessage(Translate('emotemenucmd'))
end
function GetClosestPlayer()
local players = GetPlayers()
local closestDistance = -1
local closestPlayer
local ped = PlayerPedId()
local pedCoords = GetEntityCoords(ped, false)
for index, value in ipairs(players) do
local target = GetPlayerPed(value)
if (target ~= ped) then
local targetCoords = GetEntityCoords(GetPlayerPed(value), false)
local distance = GetDistanceBetweenCoords(targetCoords["x"], targetCoords["y"], targetCoords["z"],
pedCoords["x"], pedCoords["y"], pedCoords["z"], true)
if (closestDistance == -1 or closestDistance > distance) then
closestPlayer = value
closestDistance = distance
end
end
end
return closestPlayer, closestDistance
end
function GetPlayers()
local players = {}
for i = 0, 255 do
if NetworkIsPlayerActive(i) then
table.insert(players, i)
end
end
return players
end
-- Function that'll check if player is already proning, using news cam or else
---@param ignores? table | nil key string is the ignored value
function IsInActionWithErrorMessage(ignores)
if ignores then DebugPrint(ignores) end
DebugPrint('IsProne', IsProne)
DebugPrint('IsUsingNewscam', IsUsingNewscam)
DebugPrint('IsUsingBinoculars', IsUsingBinoculars)
if (ignores == nil) then ignores = {} end
if not ignores['IsProne'] and IsProne then
EmoteChatMessage(Translate('no_anim_crawling'))
return true
end
if not ignores['IsUsingNewscam'] and IsUsingNewscam then
-- TODO: use specific error message
EmoteChatMessage(Translate('no_anim_right_now'))
return true
end
if not ignores['IsUsingBinoculars'] and IsUsingBinoculars then
-- TODO: use specific error message
EmoteChatMessage(Translate('no_anim_right_now'))
return true
end
return false
end
function HideHUDThisFrame()
HideHelpTextThisFrame()
HideHudAndRadarThisFrame()
HideHudComponentThisFrame(19) -- weapon wheel
HideHudComponentThisFrame(1) -- Wanted Stars
HideHudComponentThisFrame(2) -- Weapon icon
HideHudComponentThisFrame(3) -- Cash
HideHudComponentThisFrame(4) -- MP CASH
HideHudComponentThisFrame(13) -- Cash Change
HideHudComponentThisFrame(11) -- Floating Help Text
HideHudComponentThisFrame(12) -- more floating help text
HideHudComponentThisFrame(15) -- Subtitle Text
HideHudComponentThisFrame(18) -- Game Stream
end
function SetupButtons(button)
local scaleform = RequestScaleformMovie("instructional_buttons")
while not HasScaleformMovieLoaded(scaleform) do
Wait(10)
end
PushScaleformMovieFunction(scaleform, "CLEAR_ALL")
PopScaleformMovieFunctionVoid()
PushScaleformMovieFunction(scaleform, "SET_CLEAR_SPACE")
PushScaleformMovieFunctionParameterInt(200)
PopScaleformMovieFunctionVoid()
for i, btn in pairs(button) do
PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
PushScaleformMovieFunctionParameterInt(i - 1)
ScaleformMovieMethodAddParamPlayerNameString(GetControlInstructionalButton(0, btn.key, true))
BeginTextCommandScaleformString("STRING")
AddTextComponentScaleform(Translate(btn.text))
EndTextCommandScaleformString()
PopScaleformMovieFunctionVoid()
end
PushScaleformMovieFunction(scaleform, "DRAW_INSTRUCTIONAL_BUTTONS")
PopScaleformMovieFunctionVoid()
return scaleform
end
function HandleZoomAndCheckRotation(cam, fov)
local zoomspeed = 10.0 -- camera zoom speed
local lPed = PlayerPedId()
local fov_max = 70.0
local fov_min = 10.0 -- max zoom level (smaller fov is more zoom)
local speed_lr = 8.0 -- speed by which the camera pans left-right
local speed_ud = 8.0 -- speed by which the camera pans up-down
local zoomvalue = (1.0 / (fov_max - fov_min)) * (fov - fov_min)
local rightAxisX = GetDisabledControlNormal(0, 220)
local rightAxisY = GetDisabledControlNormal(0, 221)
local rotation = GetCamRot(cam, 2)
if rightAxisX ~= 0.0 or rightAxisY ~= 0.0 then
local new_z = rotation.z + rightAxisX * -1.0 * (speed_ud) * (zoomvalue + 0.1)
local new_x = math.max(math.min(20.0, rotation.x + rightAxisY * -1.0 * (speed_lr) * (zoomvalue + 0.1)), -29.5)
SetCamRot(cam, new_x, 0.0, new_z, 2)
end
if not (IsPedSittingInAnyVehicle(lPed)) then
if IsControlJustPressed(0, 241) then -- Scrollup
fov = math.max(fov - zoomspeed, fov_min)
end
if IsControlJustPressed(0, 242) then
fov = math.min(fov + zoomspeed, fov_max) -- ScrollDown
end
local current_fov = GetCamFov(cam)
if math.abs(fov - current_fov) < 0.1 then
fov = current_fov
end
SetCamFov(cam, current_fov + (fov - current_fov) * 0.05)
else
if IsControlJustPressed(0, 17) then -- Scrollup
fov = math.max(fov - zoomspeed, fov_min)
end
if IsControlJustPressed(0, 16) then
fov = math.min(fov + zoomspeed, fov_max) -- ScrollDown
end
local current_fov = GetCamFov(cam)
if math.abs(fov - current_fov) < 0.1 then -- the difference is too small, just set the value directly to avoid unneeded updates to FOV of order 10^-5
fov = current_fov
end
SetCamFov(cam, current_fov + (fov - current_fov) * 0.05) -- Smoothing of camera zoom
end
return fov
end
----------------------------------------------------------------------
ShowPed = false
function ShowPedMenu(zoom)
if not Config.PreviewPed then return end
if not ShowPed then
CreateThread(function()
local playerPed = PlayerPedId()
local coords = GetEntityCoords(playerPed) - vector3(0.0, 0.0, 10.0)
ClonedPed = CreatePed(26, GetEntityModel(playerPed), coords.x, coords.y, coords.z, 0, false, false)
ClonePedToTarget(playerPed, ClonedPed)
SetEntityInvincible(ClonedPed, true)
SetEntityLocallyVisible(ClonedPed)
NetworkSetEntityInvisibleToNetwork(ClonedPed, true)
SetEntityCanBeDamaged(ClonedPed, false)
SetBlockingOfNonTemporaryEvents(ClonedPed, true)
SetEntityAlpha(ClonedPed, 254, false)
SetEntityCollision(ClonedPed, false, false)
ShowPed = true
local positionBuffer = {}
local bufferSize = 5
while ShowPed do
local screencoordsX = zoom and 0.6 or 0.65135417461395
local screencoordsY = zoom and 1.9 or 0.77
if Config.MenuPosition == "left" then
screencoordsX = 1.0 - screencoordsX
end
local world, normal = GetWorldCoordFromScreenCoord(screencoordsX, screencoordsY)
local depth = zoom and 2.0 or 3.5
local target = world + normal * depth
local camRot = GetGameplayCamRot(2)
table.insert(positionBuffer, target)
if #positionBuffer > bufferSize then
table.remove(positionBuffer, 1)
end
local averagedTarget = vector3(0, 0, 0)
for _, position in ipairs(positionBuffer) do
averagedTarget = averagedTarget + position
end
averagedTarget = averagedTarget / #positionBuffer
SetEntityCoords(ClonedPed, averagedTarget.x, averagedTarget.y, averagedTarget.z, false, false, false, true)
local heading_offset = Config.MenuPosition == "left" and 170.0 or 190.0
SetEntityHeading(ClonedPed, camRot.z + heading_offset)
SetEntityRotation(ClonedPed, camRot.x * (-1), 0.0, camRot.z + 170.0, 2, false)
Wait(4)
end
DeleteEntity(ClonedPed)
ClonedPed = nil
end)
end
end
function ClosePedMenu()
if not Config.PreviewPed then return end
if ClonedPed then
ShowPed = false
ClearPedTaskPreview()
DeleteEntity(ClonedPed)
end
end
function ClearPedTaskPreview()
if not Config.PreviewPed then return end
if ClonedPed then
DestroyAllProps(true)
ClearPedTasksImmediately(ClonedPed)
end
end

View file

@ -0,0 +1,124 @@
local canChange = true
local unable_message = "You are unable to change your walking style right now."
function WalkMenuStart(name, force)
if not canChange and not force then
EmoteChatMessage(unable_message)
return
end
if not name or name == "" then
ResetWalk()
return
end
if not RP[name] or type(RP[name]) ~= "table" or RP[name].category ~= "Walks" then
EmoteChatMessage("'" .. tostring(name) .. "' is not a valid walk")
return
end
local walk = RP[name][1]
RequestWalking(walk)
SetPedMovementClipset(PlayerPedId(), walk, 0.2)
RemoveAnimSet(walk)
if Config.PersistentWalk then SetResourceKvp("walkstyle", name) end
end
function ResetWalk()
if not canChange then
EmoteChatMessage(unable_message)
return
end
ResetPedMovementClipset(PlayerPedId(), 0.0)
end
function WalksOnCommand()
local WalksCommand = ""
for name, data in PairsByKeys(RP) do
if type(data) == "table" and data.category == "Walks" then
WalksCommand = WalksCommand .. string.lower(name) .. ", "
end
end
EmoteChatMessage(WalksCommand)
EmoteChatMessage("To reset do /walk reset")
end
function WalkCommandStart(name)
if not canChange then
EmoteChatMessage(unable_message)
return
end
name = FirstToUpper(string.lower(name))
if name == "Reset" then
ResetPedMovementClipset(PlayerPedId(), 0.0)
DeleteResourceKvp("walkstyle")
return
end
WalkMenuStart(name, true)
end
if Config.WalkingStylesEnabled and Config.PersistentWalk then
local function walkstyleExists(kvp)
while not CONVERTED do
Wait(0)
end
if not kvp or kvp == "" then
return false
end
local walkstyle = RP[kvp]
if walkstyle and type(walkstyle) == "table" and walkstyle.category == "Walks" then
return true
end
return false
end
local function handleWalkstyle()
local kvp = GetResourceKvpString("walkstyle")
if kvp then
if walkstyleExists(kvp) then
WalkMenuStart(kvp, true)
else
ResetPedMovementClipset(PlayerPedId(), 0.0)
DeleteResourceKvp("walkstyle")
end
end
end
AddEventHandler('playerSpawned', function()
Wait(3000)
handleWalkstyle()
end)
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', handleWalkstyle)
RegisterNetEvent('esx:playerLoaded', handleWalkstyle)
AddEventHandler('onResourceStart', function(resource)
if resource == GetCurrentResourceName() then
handleWalkstyle()
end
end)
end
if Config.WalkingStylesEnabled then
RegisterCommand('walks', function() WalksOnCommand() end, false)
RegisterCommand('walk', function(_, args, _) WalkCommandStart(tostring(args[1])) end, false)
TriggerEvent('chat:addSuggestion', '/walk', 'Set your walkingstyle.', { { name = "style", help = "/walks for a list of valid styles" } })
TriggerEvent('chat:addSuggestion', '/walks', 'List available walking styles.')
end
CreateExport('toggleWalkstyle', function(bool, message)
canChange = bool
if message then
unable_message = message
end
end)
CreateExport('getWalkstyle', function()
return GetResourceKvpString("walkstyle")
end)
CreateExport('setWalkstyle', WalkMenuStart)