forked from Simnation/Main
fixes etc
This commit is contained in:
parent
4a0c8c6204
commit
453b281a4b
644 changed files with 1907 additions and 2456 deletions
25356
resources/[standalone]/rpemotes-reborn/client/AnimationList.lua
Normal file
25356
resources/[standalone]/rpemotes-reborn/client/AnimationList.lua
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
168
resources/[standalone]/rpemotes-reborn/client/Binoculars.lua
Normal file
168
resources/[standalone]/rpemotes-reborn/client/Binoculars.lua
Normal 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
|
107
resources/[standalone]/rpemotes-reborn/client/Bridge.lua
Normal file
107
resources/[standalone]/rpemotes-reborn/client/Bridge.lua
Normal 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)
|
501
resources/[standalone]/rpemotes-reborn/client/Crouch.lua
Normal file
501
resources/[standalone]/rpemotes-reborn/client/Crouch.lua
Normal 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)
|
895
resources/[standalone]/rpemotes-reborn/client/Emote.lua
Normal file
895
resources/[standalone]/rpemotes-reborn/client/Emote.lua
Normal 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)
|
521
resources/[standalone]/rpemotes-reborn/client/EmoteMenu.lua
Normal file
521
resources/[standalone]/rpemotes-reborn/client/EmoteMenu.lua
Normal 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)
|
|
@ -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
|
86
resources/[standalone]/rpemotes-reborn/client/Handsup.lua
Normal file
86
resources/[standalone]/rpemotes-reborn/client/Handsup.lua
Normal 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
|
96
resources/[standalone]/rpemotes-reborn/client/Keybinds.lua
Normal file
96
resources/[standalone]/rpemotes-reborn/client/Keybinds.lua
Normal 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
|
245
resources/[standalone]/rpemotes-reborn/client/NewsCam.lua
Normal file
245
resources/[standalone]/rpemotes-reborn/client/NewsCam.lua
Normal 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
|
35
resources/[standalone]/rpemotes-reborn/client/NoIdleCam.lua
Normal file
35
resources/[standalone]/rpemotes-reborn/client/NoIdleCam.lua
Normal 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)
|
65
resources/[standalone]/rpemotes-reborn/client/PTFX.lua
Normal file
65
resources/[standalone]/rpemotes-reborn/client/PTFX.lua
Normal 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)
|
124
resources/[standalone]/rpemotes-reborn/client/Pointing.lua
Normal file
124
resources/[standalone]/rpemotes-reborn/client/Pointing.lua
Normal 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)
|
33
resources/[standalone]/rpemotes-reborn/client/Ragdoll.lua
Normal file
33
resources/[standalone]/rpemotes-reborn/client/Ragdoll.lua
Normal 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
|
164
resources/[standalone]/rpemotes-reborn/client/Syncing.lua
Normal file
164
resources/[standalone]/rpemotes-reborn/client/Syncing.lua
Normal 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)
|
395
resources/[standalone]/rpemotes-reborn/client/Utils.lua
Normal file
395
resources/[standalone]/rpemotes-reborn/client/Utils.lua
Normal 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
|
124
resources/[standalone]/rpemotes-reborn/client/Walk.lua
Normal file
124
resources/[standalone]/rpemotes-reborn/client/Walk.lua
Normal 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)
|
Loading…
Add table
Add a link
Reference in a new issue