mirror of
https://github.com/jimathy/rpemotes.git
synced 2026-05-13 03:21:55 -04:00
* Major Update - Convert to RPEmotes. - Add support for colors on particles. - Removed an unusual check when using the pay emote function. - Added config option to change cancel key. - Changed all keys to lowercase to prevent the "key no worky" bug. - Changed the open key to f4. - Improved and cleaner version check. - Updated events as the usage was deprecated. - Added a check to remove any unsupported emotes such as ones unsupported from game builds and improved the adult animation removal. * Fix * Changes * Update README.md
679 lines
24 KiB
Lua
679 lines
24 KiB
Lua
-- You probably shouldnt touch these.
|
|
local AnimationDuration = -1
|
|
local ChosenAnimation = ""
|
|
local ChosenDict = ""
|
|
local IsInAnimation = false
|
|
local MostRecentChosenAnimation = ""
|
|
local MostRecentChosenDict = ""
|
|
local MovementType = 0
|
|
local PlayerGender = "male"
|
|
local PlayerHasProp = false
|
|
local PlayerProps = {}
|
|
local PlayerParticles = {}
|
|
local SecondPropEmote = false
|
|
local lang = Config.MenuLanguage
|
|
local PtfxNotif = false
|
|
local PtfxPrompt = false
|
|
local PtfxWait = 500
|
|
local PtfxCanHold = false
|
|
local PtfxNoProp = false
|
|
local AnimationThreadStatus = false
|
|
local CanCancel = true
|
|
local Pointing = false
|
|
|
|
local function RunAnimationThread()
|
|
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 IsPedShooting(PlayerPedId()) then
|
|
EmoteCancel()
|
|
end
|
|
end
|
|
|
|
if PtfxPrompt then
|
|
sleep = 0
|
|
if not PtfxNotif then
|
|
SimpleNotify(PtfxInfo)
|
|
PtfxNotif = true
|
|
end
|
|
if IsControlPressed(0, 47) then
|
|
PtfxStart()
|
|
Wait(PtfxWait)
|
|
if PtfxCanHold then
|
|
while IsControlPressed(0, 47) and IsInAnimation and AnimationThreadStatus do
|
|
Wait(5)
|
|
end
|
|
end
|
|
PtfxStop()
|
|
end
|
|
end
|
|
|
|
Wait(sleep)
|
|
end
|
|
end)
|
|
end
|
|
|
|
if Config.EnableXtoCancel then
|
|
RegisterKeyMapping("emotecancel", "Cancel current emote", "keyboard", Config.CancelEmoteKey)
|
|
end
|
|
|
|
if Config.MenuKeybindEnabled then
|
|
RegisterKeyMapping("emotemenu", "Open rpemotes menu", "keyboard", Config.MenuKeybind)
|
|
end
|
|
|
|
if Config.HandsupKeybindEnabled then
|
|
RegisterKeyMapping("handsup", "Put your arms up", "keyboard", Config.HandsupKeybind)
|
|
end
|
|
|
|
if Config.PointingKeybindEnabled then
|
|
RegisterKeyMapping("pointing", "Finger pointing", "keyboard", Config.PointingKeybind)
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------------------
|
|
-- Commands / Events --------------------------------------------------------------------------------
|
|
-----------------------------------------------------------------------------------------------------
|
|
|
|
Citizen.CreateThread(function()
|
|
TriggerEvent('chat:addSuggestion', '/e', 'Play an emote',
|
|
{ { name = "emotename", help = "dance, camera, sit or any valid emote." },
|
|
{ name = "texturevariation", help = "(Optional) 1, 2, 3 or any number. Will change the texture of some props used in emotes, for example the color of a phone. Enter -1 to see a list of variations." } })
|
|
TriggerEvent('chat:addSuggestion', '/emote', 'Play an emote',
|
|
{ { name = "emotename", help = "dance, camera, sit or any valid emote." },
|
|
{ name = "texturevariation", help = "(Optional) 1, 2, 3 or any number. Will change the texture of some props used in emotes, for example the color of a phone. Enter -1 to see a list of variations." } })
|
|
if Config.SqlKeybinding then
|
|
TriggerEvent('chat:addSuggestion', '/emotebind', 'Bind an emote',
|
|
{ { name = "key", help = "num4, num5, num6, num7. num8, num9. Numpad 4-9!" },
|
|
{ name = "emotename", help = "dance, camera, sit or any valid emote." } })
|
|
TriggerEvent('chat:addSuggestion', '/emotebinds', 'Check your currently bound emotes.')
|
|
end
|
|
TriggerEvent('chat:addSuggestion', '/emotemenu', 'Open rpemotes menu (F5) by default.')
|
|
TriggerEvent('chat:addSuggestion', '/emotes', 'List available emotes.')
|
|
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.')
|
|
TriggerEvent('chat:addSuggestion', '/emotecancel', 'Cancel currently playing emote.')
|
|
TriggerEvent('chat:addSuggestion', '/handsup', 'Put your arms up.')
|
|
TriggerEvent('chat:addSuggestion', '/pointing', 'Finger pointing.')
|
|
end)
|
|
|
|
RegisterCommand('e', function(source, args, raw) EmoteCommandStart(source, args, raw) end, false)
|
|
RegisterCommand('emote', function(source, args, raw) EmoteCommandStart(source, args, raw) end, false)
|
|
if Config.SqlKeybinding then
|
|
RegisterCommand('emotebind', function(source, args, raw) EmoteBindStart(source, args, raw) end, false)
|
|
RegisterCommand('emotebinds', function(source, args, raw) EmoteBindsStart(source, args, raw) end, false)
|
|
end
|
|
RegisterCommand('emotemenu', function(source, args, raw) OpenEmoteMenu() end, false)
|
|
RegisterCommand('emotes', function(source, args, raw) EmotesOnCommand() end, false)
|
|
RegisterCommand('walk', function(source, args, raw) WalkCommandStart(source, args, raw) end, false)
|
|
RegisterCommand('walks', function(source, args, raw) WalksOnCommand() end, false)
|
|
RegisterCommand('emotecancel', function(source, args, raw) EmoteCancel() end, false)
|
|
|
|
RegisterCommand('handsup', function(source, args, raw)
|
|
if Config.HandsupKeybindEnabled then
|
|
if IsEntityPlayingAnim(PlayerPedId(), "missminuteman_1ig_2", "handsup_base", 51) then
|
|
EmoteCancel()
|
|
else
|
|
EmoteCommandStart(nil, {"handsup"}, nil)
|
|
end
|
|
end
|
|
end, false)
|
|
|
|
RegisterCommand('pointing', function(source, args, raw)
|
|
if Config.PointingKeybindEnabled then
|
|
local ped = PlayerPedId()
|
|
Pointing = not Pointing
|
|
|
|
if Pointing then
|
|
if LoadAnim("anim@mp_point") then
|
|
SetPedConfigFlag(ped, 36, 1)
|
|
TaskMoveNetworkByName(ped, 'task_mp_pointing', 0.5, 0, 'anim@mp_point', 24)
|
|
end
|
|
|
|
Citizen.CreateThread(function()
|
|
local ped = PlayerPedId()
|
|
|
|
while Pointing and IsPedOnFoot(ped) do
|
|
Citizen.Wait(0)
|
|
|
|
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 = Cos(camHeading)
|
|
local sinCamHeading = 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(ped, (cosCamHeading * -0.2) - (sinCamHeading * (0.4 * camHeading + 0.3)), (sinCamHeading * -0.2) + (cosCamHeading * (0.4 * camHeading + 0.3)), 0.6)
|
|
local rayHandle, blocked = GetShapeTestResult(StartShapeTestCapsule(coords.x, coords.y, coords.z - 0.2, coords.x, coords.y, coords.z + 0.2, 0.4, 95, ped, 7))
|
|
|
|
SetTaskMoveNetworkSignalFloat(ped, 'Pitch', camPitch)
|
|
SetTaskMoveNetworkSignalFloat(ped, 'Heading', (camHeading * -1.0) + 1.0)
|
|
SetTaskMoveNetworkSignalBool(ped, 'isBlocked', blocked)
|
|
SetTaskMoveNetworkSignalBool(ped, 'isFirstPerson', GetCamViewModeForContext(GetCamActiveViewModeContext()) == 4)
|
|
end
|
|
|
|
ResetPedMovementClipset(ped, 0)
|
|
RequestTaskMoveNetworkStateTransition(ped, 'Stop')
|
|
|
|
if not IsPedInjured(ped) then ClearPedSecondaryTask(ped) end
|
|
|
|
SetPedConfigFlag(ped, 36, 0)
|
|
|
|
if Config.WalkingStylesEnabled and Config.PersistentWalk then
|
|
local kvp = GetResourceKvpString("walkstyle")
|
|
|
|
if kvp ~= nil then WalkMenuStart(kvp) end
|
|
end
|
|
end)
|
|
else
|
|
Pointing = false
|
|
end
|
|
end
|
|
end, false)
|
|
|
|
AddEventHandler('onResourceStop', function(resource)
|
|
if resource == GetCurrentResourceName() then
|
|
local ply = PlayerPedId()
|
|
DestroyAllProps()
|
|
ClearPedTasksImmediately(ply)
|
|
DetachEntity(ply, true, false)
|
|
ResetPedMovementClipset(ply)
|
|
AnimationThreadStatus = false
|
|
end
|
|
end)
|
|
|
|
-----------------------------------------------------------------------------------------------------
|
|
------ Functions and stuff --------------------------------------------------------------------------
|
|
-----------------------------------------------------------------------------------------------------
|
|
|
|
function EmoteCancel(force)
|
|
local ply = PlayerPedId()
|
|
if not CanCancel and force ~= true then return end
|
|
if ChosenDict == "MaleScenario" and IsInAnimation then
|
|
ClearPedTasksImmediately(ply)
|
|
IsInAnimation = false
|
|
DebugPrint("Forced scenario exit")
|
|
elseif ChosenDict == "Scenario" and IsInAnimation then
|
|
ClearPedTasksImmediately(ply)
|
|
IsInAnimation = false
|
|
DebugPrint("Forced scenario exit")
|
|
end
|
|
|
|
PtfxNotif = false
|
|
PtfxPrompt = false
|
|
Pointing = false
|
|
|
|
if IsInAnimation then
|
|
if LocalPlayer.state.ptfx then
|
|
PtfxStop()
|
|
end
|
|
ClearPedTasks(ply)
|
|
DetachEntity(ply, true, false)
|
|
CancelSharedEmote(ply)
|
|
DestroyAllProps()
|
|
IsInAnimation = false
|
|
end
|
|
AnimationThreadStatus = false
|
|
end
|
|
|
|
function EmoteChatMessage(msg, multiline)
|
|
if msg then
|
|
TriggerEvent("chat:addMessage", { multiline = multiline == true or false, color = { 255, 255, 255 }, args = { "^5Help^0", tostring(msg) } })
|
|
end
|
|
end
|
|
|
|
function DebugPrint(args)
|
|
if Config.DebugDisplay then
|
|
print(args)
|
|
end
|
|
end
|
|
|
|
--#region ptfx
|
|
function PtfxThis(asset)
|
|
while not HasNamedPtfxAssetLoaded(asset) do
|
|
RequestNamedPtfxAsset(asset)
|
|
Wait(10)
|
|
end
|
|
UseParticleFxAssetNextCall(asset)
|
|
end
|
|
|
|
function PtfxStart()
|
|
LocalPlayer.state:set('ptfx', true, true)
|
|
end
|
|
|
|
function PtfxStop()
|
|
LocalPlayer.state:set('ptfx', false, true)
|
|
end
|
|
|
|
AddStateBagChangeHandler('ptfx', nil, function(bagName, key, value, _unused, replicated)
|
|
local plyId = tonumber(bagName:gsub('player:', ''), 10)
|
|
|
|
-- We stop here if we don't need to go further
|
|
-- We don't need to start or stop the ptfx twice
|
|
if (PlayerParticles[plyId] and value) or (not PlayerParticles[plyId] and not value) then return end
|
|
|
|
-- Only allow ptfx change on players
|
|
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
|
|
-- Start ptfx
|
|
|
|
local asset = stateBag.ptfxAsset
|
|
local name = stateBag.ptfxName
|
|
local offset = stateBag.ptfxOffset
|
|
local rot = stateBag.ptfxRot
|
|
local scale = stateBag.ptfxScale or 1
|
|
local color = stateBag.ptfxColor
|
|
local propNet = stateBag.ptfxPropNet
|
|
local entityTarget = plyPed
|
|
-- Only do for valid obj
|
|
if propNet then
|
|
local propObj = NetToObj(propNet)
|
|
if DoesEntityExist(propObj) then
|
|
entityTarget = propObj
|
|
end
|
|
end
|
|
PtfxThis(asset)
|
|
PlayerParticles[plyId] = StartNetworkedParticleFxLoopedOnEntityBone(name, entityTarget, offset.x, offset.y, offset.z, rot.x, rot.y, rot.z, GetEntityBoneIndexByName(name, "VFX"), scale + 0.0, 0, 0, 0, 1065353216, 1065353216, 1065353216, 0)
|
|
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
|
|
-- Stop ptfx
|
|
DebugPrint("Stopped PTFX: " .. PlayerParticles[plyId])
|
|
StopParticleFxLooped(PlayerParticles[plyId], false)
|
|
RemoveNamedPtfxAsset(stateBag.ptfxAsset)
|
|
PlayerParticles[plyId] = nil
|
|
end
|
|
end)
|
|
--#endregion ptfx
|
|
|
|
function EmotesOnCommand(source, args, raw)
|
|
local EmotesCommand = ""
|
|
for a in pairsByKeys(RP.Emotes) do
|
|
EmotesCommand = EmotesCommand .. "" .. a .. ", "
|
|
end
|
|
EmoteChatMessage(EmotesCommand)
|
|
EmoteChatMessage(Config.Languages[lang]['emotemenucmd'])
|
|
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 EmoteMenuStart(args, hard, textureVariation)
|
|
local name = args
|
|
local etype = hard
|
|
|
|
if etype == "dances" then
|
|
if RP.Dances[name] ~= nil then
|
|
OnEmotePlay(RP.Dances[name])
|
|
end
|
|
elseif etype == "animals" then
|
|
if RP.AnimalEmotes[name] ~= nil then
|
|
OnEmotePlay(RP.AnimalEmotes[name])
|
|
end
|
|
elseif etype == "props" then
|
|
if RP.PropEmotes[name] ~= nil then
|
|
OnEmotePlay(RP.PropEmotes[name], textureVariation)
|
|
end
|
|
elseif etype == "emotes" then
|
|
if RP.Emotes[name] ~= nil then
|
|
OnEmotePlay(RP.Emotes[name])
|
|
else
|
|
if name ~= "🕺 Dance Emotes" then end
|
|
end
|
|
elseif etype == "expression" then
|
|
if RP.Expressions[name] ~= nil then
|
|
OnEmotePlay(RP.Expressions[name])
|
|
end
|
|
end
|
|
end
|
|
|
|
function EmoteCommandStart(source, args, raw)
|
|
if #args > 0 then
|
|
local name = string.lower(args[1])
|
|
if name == "c" then
|
|
if IsInAnimation then
|
|
EmoteCancel()
|
|
else
|
|
EmoteChatMessage(Config.Languages[lang]['nocancel'])
|
|
end
|
|
return
|
|
elseif name == "help" then
|
|
EmotesOnCommand()
|
|
return
|
|
end
|
|
|
|
if RP.Emotes[name] ~= nil then
|
|
OnEmotePlay(RP.Emotes[name])
|
|
return
|
|
elseif RP.Dances[name] ~= nil then
|
|
OnEmotePlay(RP.Dances[name])
|
|
return
|
|
elseif RP.AnimalEmotes[name] ~= nil then
|
|
OnEmotePlay(RP.AnimalEmotes[name])
|
|
return
|
|
elseif RP.PropEmotes[name] ~= nil then
|
|
if RP.PropEmotes[name].AnimationOptions.PropTextureVariations then
|
|
if #args > 1 then
|
|
local textureVariation = tonumber(args[2])
|
|
if (RP.PropEmotes[name].AnimationOptions.PropTextureVariations[textureVariation] ~= nil) then
|
|
OnEmotePlay(RP.PropEmotes[name], textureVariation - 1)
|
|
return
|
|
else
|
|
local str = ""
|
|
for k, v in ipairs(RP.PropEmotes[name].AnimationOptions.PropTextureVariations) do
|
|
str = str .. string.format("\n(%s) - %s", k, v.Name)
|
|
end
|
|
|
|
EmoteChatMessage(string.format(Config.Languages[lang]['invalidvariation'], str), true)
|
|
OnEmotePlay(RP.PropEmotes[name], 0)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
OnEmotePlay(RP.PropEmotes[name])
|
|
return
|
|
else
|
|
EmoteChatMessage("'" .. name .. "' " .. Config.Languages[lang]['notvalidemote'] .. "")
|
|
end
|
|
end
|
|
end
|
|
|
|
function LoadAnim(dict)
|
|
if not DoesAnimDictExist(dict) then
|
|
return false
|
|
end
|
|
|
|
while not HasAnimDictLoaded(dict) do
|
|
RequestAnimDict(dict)
|
|
Wait(10)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function LoadPropDict(model)
|
|
while not HasModelLoaded(joaat(model)) do
|
|
RequestModel(joaat(model))
|
|
Wait(10)
|
|
end
|
|
end
|
|
|
|
function DestroyAllProps()
|
|
for _, v in pairs(PlayerProps) do
|
|
DeleteEntity(v)
|
|
end
|
|
PlayerHasProp = false
|
|
DebugPrint("Destroyed Props")
|
|
end
|
|
|
|
function AddPropToPlayer(prop1, bone, off1, off2, off3, rot1, rot2, rot3, textureVariation)
|
|
local Player = PlayerPedId()
|
|
local x, y, z = table.unpack(GetEntityCoords(Player))
|
|
|
|
if not HasModelLoaded(prop1) then
|
|
LoadPropDict(prop1)
|
|
end
|
|
|
|
prop = CreateObject(joaat(prop1), x, y, z + 0.2, true, true, true)
|
|
if textureVariation ~= nil then
|
|
SetObjectTextureVariation(prop, textureVariation)
|
|
end
|
|
AttachEntityToEntity(prop, Player, GetPedBoneIndex(Player, bone), off1, off2, off3, rot1, rot2, rot3, true, true,
|
|
false, true, 1, true)
|
|
table.insert(PlayerProps, prop)
|
|
PlayerHasProp = true
|
|
SetModelAsNoLongerNeeded(prop1)
|
|
return true
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------------------
|
|
-- V -- This could be a whole lot better, i tried messing around with "IsPedMale(ped)"
|
|
-- V -- But i never really figured it out, if anyone has a better way of gender checking let me know.
|
|
-- V -- Since this way doesnt work for ped models.
|
|
-- V -- in most cases its better to replace the scenario with an animation bundled with prop instead.
|
|
-----------------------------------------------------------------------------------------------------
|
|
|
|
function CheckGender()
|
|
local hashSkinMale = joaat("mp_m_freemode_01")
|
|
local hashSkinFemale = joaat("mp_f_freemode_01")
|
|
|
|
if GetEntityModel(PlayerPedId()) == hashSkinMale then
|
|
PlayerGender = "male"
|
|
elseif GetEntityModel(PlayerPedId()) == hashSkinFemale then
|
|
PlayerGender = "female"
|
|
end
|
|
DebugPrint("Set gender as = (" .. PlayerGender .. ")")
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------------------
|
|
------ This is the major function for playing emotes! -----------------------------------------------
|
|
-----------------------------------------------------------------------------------------------------
|
|
|
|
function OnEmotePlay(EmoteName, textureVariation)
|
|
InVehicle = IsPedInAnyVehicle(PlayerPedId(), true)
|
|
Pointing = false
|
|
|
|
if not Config.AllowedInCars and InVehicle == 1 then
|
|
return
|
|
end
|
|
|
|
if not DoesEntityExist(PlayerPedId()) then
|
|
return false
|
|
end
|
|
|
|
ChosenDict, ChosenAnimation, ename = table.unpack(EmoteName)
|
|
AnimationDuration = -1
|
|
|
|
if ChosenDict == "Expression" then
|
|
SetFacialIdleAnimOverride(PlayerPedId(), ChosenAnimation, 0)
|
|
return
|
|
end
|
|
|
|
if Config.DisarmPlayer then
|
|
if IsPedArmed(PlayerPedId(), 7) then
|
|
SetCurrentPedWeapon(PlayerPedId(), joaat('WEAPON_UNARMED'), true)
|
|
end
|
|
end
|
|
|
|
if PlayerHasProp then
|
|
DestroyAllProps()
|
|
end
|
|
|
|
if ChosenDict == "MaleScenario" or "Scenario" then
|
|
CheckGender()
|
|
if ChosenDict == "MaleScenario" then if InVehicle then return end
|
|
if PlayerGender == "male" then
|
|
ClearPedTasks(PlayerPedId())
|
|
TaskStartScenarioInPlace(PlayerPedId(), ChosenAnimation, 0, true)
|
|
DebugPrint("Playing scenario = (" .. ChosenAnimation .. ")")
|
|
IsInAnimation = true
|
|
RunAnimationThread()
|
|
else
|
|
EmoteChatMessage(Config.Languages[lang]['maleonly'])
|
|
end
|
|
return
|
|
elseif ChosenDict == "ScenarioObject" then if InVehicle then return end
|
|
BehindPlayer = GetOffsetFromEntityInWorldCoords(PlayerPedId(), 0.0, 0 - 0.5, -0.5);
|
|
ClearPedTasks(PlayerPedId())
|
|
TaskStartScenarioAtPosition(PlayerPedId(), ChosenAnimation, BehindPlayer['x'], BehindPlayer['y'],
|
|
BehindPlayer['z'], GetEntityHeading(PlayerPedId()), 0, 1, false)
|
|
DebugPrint("Playing scenario = (" .. ChosenAnimation .. ")")
|
|
IsInAnimation = true
|
|
RunAnimationThread()
|
|
return
|
|
elseif ChosenDict == "Scenario" then if InVehicle then return end
|
|
ClearPedTasks(PlayerPedId())
|
|
TaskStartScenarioInPlace(PlayerPedId(), ChosenAnimation, 0, true)
|
|
DebugPrint("Playing scenario = (" .. ChosenAnimation .. ")")
|
|
IsInAnimation = true
|
|
RunAnimationThread()
|
|
return
|
|
end
|
|
end
|
|
|
|
-- Small delay at the start
|
|
if EmoteName.AnimationOptions and EmoteName.AnimationOptions.StartDelay then
|
|
Wait(EmoteName.AnimationOptions.StartDelay)
|
|
end
|
|
|
|
if not LoadAnim(ChosenDict) then
|
|
EmoteChatMessage("'" .. ename .. "' " .. Config.Languages[lang]['notvalidemote'] .. "")
|
|
return
|
|
end
|
|
|
|
if EmoteName.AnimationOptions then
|
|
if EmoteName.AnimationOptions.EmoteLoop then
|
|
MovementType = 1
|
|
if EmoteName.AnimationOptions.EmoteMoving then
|
|
MovementType = 51 -- 110011
|
|
end
|
|
|
|
elseif EmoteName.AnimationOptions.EmoteMoving then
|
|
MovementType = 51 -- 110011
|
|
elseif EmoteName.AnimationOptions.EmoteMoving == false then
|
|
MovementType = 0
|
|
elseif EmoteName.AnimationOptions.EmoteStuck then
|
|
MovementType = 50 -- 110010
|
|
end
|
|
|
|
else
|
|
MovementType = 0
|
|
end
|
|
|
|
if InVehicle == 1 then
|
|
MovementType = 51
|
|
end
|
|
|
|
if EmoteName.AnimationOptions then
|
|
if EmoteName.AnimationOptions.EmoteDuration == nil then
|
|
EmoteName.AnimationOptions.EmoteDuration = -1
|
|
AttachWait = 0
|
|
else
|
|
AnimationDuration = EmoteName.AnimationOptions.EmoteDuration
|
|
AttachWait = EmoteName.AnimationOptions.EmoteDuration
|
|
end
|
|
|
|
if EmoteName.AnimationOptions.PtfxAsset then
|
|
PtfxAsset = EmoteName.AnimationOptions.PtfxAsset
|
|
PtfxName = EmoteName.AnimationOptions.PtfxName
|
|
if EmoteName.AnimationOptions.PtfxNoProp then
|
|
PtfxNoProp = EmoteName.AnimationOptions.PtfxNoProp
|
|
else
|
|
PtfxNoProp = false
|
|
end
|
|
Ptfx1, Ptfx2, Ptfx3, Ptfx4, Ptfx5, Ptfx6, PtfxScale = table.unpack(EmoteName.AnimationOptions.PtfxPlacement)
|
|
PtfxColor = EmoteName.AnimationOptions.PtfxColor
|
|
PtfxInfo = EmoteName.AnimationOptions.PtfxInfo
|
|
PtfxWait = EmoteName.AnimationOptions.PtfxWait
|
|
PtfxCanHold = EmoteName.AnimationOptions.PtfxCanHold
|
|
PtfxNotif = false
|
|
PtfxPrompt = true
|
|
-- RunAnimationThread() -- ? This call should not be required, see if needed with tests
|
|
|
|
TriggerServerEvent("rpemotes:ptfx:sync", PtfxAsset, PtfxName, vector3(Ptfx1, Ptfx2, Ptfx3),
|
|
vector3(Ptfx4, Ptfx5, Ptfx6), PtfxScale, PtfxColor)
|
|
else
|
|
DebugPrint("Ptfx = none")
|
|
PtfxPrompt = false
|
|
end
|
|
end
|
|
|
|
TaskPlayAnim(PlayerPedId(), ChosenDict, ChosenAnimation, 2.0, 2.0, AnimationDuration, MovementType, 0, false, false,
|
|
false)
|
|
RemoveAnimDict(ChosenDict)
|
|
IsInAnimation = true
|
|
RunAnimationThread()
|
|
MostRecentDict = ChosenDict
|
|
MostRecentAnimation = ChosenAnimation
|
|
|
|
if EmoteName.AnimationOptions then
|
|
if EmoteName.AnimationOptions.Prop then
|
|
PropName = EmoteName.AnimationOptions.Prop
|
|
PropBone = EmoteName.AnimationOptions.PropBone
|
|
PropPl1, PropPl2, PropPl3, PropPl4, PropPl5, PropPl6 = table.unpack(EmoteName.AnimationOptions.PropPlacement)
|
|
if EmoteName.AnimationOptions.SecondProp then
|
|
SecondPropName = EmoteName.AnimationOptions.SecondProp
|
|
SecondPropBone = EmoteName.AnimationOptions.SecondPropBone
|
|
SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6 = table.unpack(EmoteName
|
|
.AnimationOptions.SecondPropPlacement)
|
|
SecondPropEmote = true
|
|
else
|
|
SecondPropEmote = false
|
|
end
|
|
Wait(AttachWait)
|
|
if not AddPropToPlayer(PropName, PropBone, PropPl1, PropPl2, PropPl3, PropPl4, PropPl5, PropPl6, textureVariation) then return end
|
|
if SecondPropEmote then
|
|
if not AddPropToPlayer(SecondPropName, SecondPropBone, SecondPropPl1, SecondPropPl2, SecondPropPl3,
|
|
SecondPropPl4, SecondPropPl5, SecondPropPl6, textureVariation) then
|
|
DestroyAllProps()
|
|
return
|
|
end
|
|
end
|
|
|
|
-- Ptfx is on the prop, then we need to sync it
|
|
if EmoteName.AnimationOptions.PtfxAsset and not PtfxNoProp then
|
|
TriggerServerEvent("rpemotes:ptfx:syncProp", ObjToNet(prop))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------------------
|
|
------ Some exports to make the script more standalone! (by Clem76) ---------------------------------
|
|
-----------------------------------------------------------------------------------------------------
|
|
|
|
exports("EmoteCommandStart", function(emoteName, textureVariation)
|
|
EmoteCommandStart(nil, {emoteName, textureVariation}, nil)
|
|
end)
|
|
exports("EmoteCancel", EmoteCancel)
|
|
exports("CanCancelEmote", function(State)
|
|
CanCancel = State == true
|
|
end)
|