mirror of
https://github.com/jimathy/rpemotes.git
synced 2026-06-06 07:11:41 -04:00
452 lines
16 KiB
Lua
452 lines
16 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 PtfxNoProp = false
|
|
|
|
Citizen.CreateThread(function()
|
|
while true do
|
|
if IsPedShooting(PlayerPedId()) and IsInAnimation then
|
|
EmoteCancel()
|
|
end
|
|
|
|
if PtfxPrompt then
|
|
if not PtfxNotif then
|
|
SimpleNotify(PtfxInfo)
|
|
PtfxNotif = true
|
|
end
|
|
if IsControlPressed(0, 47) then
|
|
PtfxStart()
|
|
Wait(PtfxWait)
|
|
PtfxStop()
|
|
end
|
|
end
|
|
|
|
if Config.EnableXtoCancel then if IsControlPressed(0, 73) then EmoteCancel() end end
|
|
Citizen.Wait(1)
|
|
end
|
|
end)
|
|
|
|
if Config.MenuKeybindEnabled then
|
|
RegisterKeyMapping("emotemenu", "Open dpemotes menu", "keyboard", Config.MenuKeybind)
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------------------
|
|
-- Commands / Events --------------------------------------------------------------------------------
|
|
-----------------------------------------------------------------------------------------------------
|
|
|
|
Citizen.CreateThread(function()
|
|
TriggerEvent('chat:addSuggestion', '/e', 'Play an emote', { { name = "emotename", help = "dance, camera, sit or any valid emote." } })
|
|
TriggerEvent('chat:addSuggestion', '/emote', 'Play an emote', { { name = "emotename", help = "dance, camera, sit or any valid emote." } })
|
|
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 dpemotes 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.')
|
|
end)
|
|
|
|
RegisterCommand('e', function(source, args, raw) EmoteCommandStart(source, args, raw) end)
|
|
RegisterCommand('emote', function(source, args, raw) EmoteCommandStart(source, args, raw) end)
|
|
if Config.SqlKeybinding then
|
|
RegisterCommand('emotebind', function(source, args, raw) EmoteBindStart(source, args, raw) end)
|
|
RegisterCommand('emotebinds', function(source, args, raw) EmoteBindsStart(source, args, raw) end)
|
|
end
|
|
RegisterCommand('emotemenu', function(source, args, raw) OpenEmoteMenu() end)
|
|
RegisterCommand('emotes', function(source, args, raw) EmotesOnCommand() end)
|
|
RegisterCommand('walk', function(source, args, raw) WalkCommandStart(source, args, raw) end)
|
|
RegisterCommand('walks', function(source, args, raw) WalksOnCommand() end)
|
|
|
|
AddEventHandler('onResourceStop', function(resource)
|
|
if resource == GetCurrentResourceName() then
|
|
local ply = PlayerPedId()
|
|
DestroyAllProps()
|
|
ClearPedTasksImmediately(ply)
|
|
DetachEntity(ply, true, false)
|
|
ResetPedMovementClipset(ply)
|
|
end
|
|
end)
|
|
|
|
-----------------------------------------------------------------------------------------------------
|
|
------ Functions and stuff --------------------------------------------------------------------------
|
|
-----------------------------------------------------------------------------------------------------
|
|
|
|
function EmoteCancel()
|
|
local ply = PlayerPedId()
|
|
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
|
|
|
|
if IsInAnimation then
|
|
PtfxStop()
|
|
ClearPedTasks(ply)
|
|
DetachEntity(ply, true, false)
|
|
CancelSharedEmote(ply)
|
|
DestroyAllProps()
|
|
IsInAnimation = false
|
|
end
|
|
end
|
|
|
|
function EmoteChatMessage(args)
|
|
if args == display then
|
|
TriggerEvent("chatMessage", "^5Help^0", { 0, 0, 0 }, string.format(""))
|
|
else
|
|
TriggerEvent("chatMessage", "^5Help^0", { 0, 0, 0 }, string.format("" .. args .. ""))
|
|
end
|
|
end
|
|
|
|
function DebugPrint(args)
|
|
if Config.DebugDisplay then
|
|
print(args)
|
|
end
|
|
end
|
|
|
|
function PtfxStart()
|
|
if PtfxNoProp then
|
|
PtfxAt = PlayerPedId()
|
|
else
|
|
PtfxAt = prop
|
|
end
|
|
UseParticleFxAssetNextCall(PtfxAsset)
|
|
Ptfx = StartNetworkedParticleFxLoopedOnEntityBone(PtfxName, PtfxAt, Ptfx1, Ptfx2, Ptfx3, Ptfx4, Ptfx5, Ptfx6, GetEntityBoneIndexByName(PtfxName, "VFX"), 1065353216, 0, 0, 0, 1065353216, 1065353216, 1065353216, 0)
|
|
SetParticleFxLoopedColour(Ptfx, 1.0, 1.0, 1.0)
|
|
table.insert(PlayerParticles, Ptfx)
|
|
end
|
|
|
|
function PtfxStop()
|
|
for a, b in pairs(PlayerParticles) do
|
|
DebugPrint("Stopped PTFX: " .. b)
|
|
StopParticleFxLooped(b, false)
|
|
table.remove(PlayerParticles, a)
|
|
end
|
|
end
|
|
|
|
function EmotesOnCommand(source, args, raw)
|
|
local EmotesCommand = ""
|
|
for a in pairsByKeys(DP.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)
|
|
local name = args
|
|
local etype = hard
|
|
|
|
if etype == "dances" then
|
|
if DP.Dances[name] ~= nil then
|
|
if OnEmotePlay(DP.Dances[name]) then end
|
|
end
|
|
elseif etype == "animals" then
|
|
if DP.AnimalEmotes[name] ~= nil then
|
|
if OnEmotePlay(DP.AnimalEmotes[name]) then end
|
|
end
|
|
elseif etype == "props" then
|
|
if DP.PropEmotes[name] ~= nil then
|
|
if OnEmotePlay(DP.PropEmotes[name]) then end
|
|
end
|
|
elseif etype == "emotes" then
|
|
if DP.Emotes[name] ~= nil then
|
|
if OnEmotePlay(DP.Emotes[name]) then end
|
|
else
|
|
if name ~= "🕺 Dance Emotes" then end
|
|
end
|
|
elseif etype == "expression" then
|
|
if DP.Expressions[name] ~= nil then
|
|
if OnEmotePlay(DP.Expressions[name]) then end
|
|
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 DP.Emotes[name] ~= nil then
|
|
if OnEmotePlay(DP.Emotes[name]) then end
|
|
return
|
|
elseif DP.Dances[name] ~= nil then
|
|
if OnEmotePlay(DP.Dances[name]) then end
|
|
return
|
|
elseif DP.AnimalEmotes[name] ~= nil then
|
|
if OnEmotePlay(DP.AnimalEmotes[name]) then end
|
|
return
|
|
elseif DP.PropEmotes[name] ~= nil then
|
|
if OnEmotePlay(DP.PropEmotes[name]) then end
|
|
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(GetHashKey(model)) do
|
|
RequestModel(GetHashKey(model))
|
|
Wait(10)
|
|
end
|
|
end
|
|
|
|
function PtfxThis(asset)
|
|
while not HasNamedPtfxAssetLoaded(asset) do
|
|
RequestNamedPtfxAsset(asset)
|
|
Wait(10)
|
|
end
|
|
UseParticleFxAssetNextCall(asset)
|
|
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)
|
|
local Player = PlayerPedId()
|
|
local x, y, z = table.unpack(GetEntityCoords(Player))
|
|
|
|
if not HasModelLoaded(prop1) then
|
|
LoadPropDict(prop1)
|
|
end
|
|
|
|
prop = CreateObject(GetHashKey(prop1), x, y, z + 0.2, true, true, true)
|
|
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)
|
|
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 = GetHashKey("mp_m_freemode_01")
|
|
local hashSkinFemale = GetHashKey("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)
|
|
InVehicle = IsPedInAnyVehicle(PlayerPedId(), true)
|
|
if not Config.AllowedInCars and InVehicle == 1 then
|
|
return
|
|
end
|
|
|
|
if not DoesEntityExist(PlayerPedId()) then
|
|
return false
|
|
end
|
|
|
|
if Config.DisarmPlayer then
|
|
if IsPedArmed(PlayerPedId(), 7) then
|
|
SetCurrentPedWeapon(PlayerPedId(), GetHashKey('WEAPON_UNARMED'), true)
|
|
end
|
|
end
|
|
|
|
ChosenDict, ChosenAnimation, ename = table.unpack(EmoteName)
|
|
AnimationDuration = -1
|
|
|
|
if PlayerHasProp then
|
|
DestroyAllProps()
|
|
end
|
|
|
|
if ChosenDict == "Expression" then
|
|
SetFacialIdleAnimOverride(PlayerPedId(), ChosenAnimation, 0)
|
|
return
|
|
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
|
|
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
|
|
return
|
|
elseif ChosenDict == "Scenario" then if InVehicle then return end
|
|
ClearPedTasks(PlayerPedId())
|
|
TaskStartScenarioInPlace(PlayerPedId(), ChosenAnimation, 0, true)
|
|
DebugPrint("Playing scenario = (" .. ChosenAnimation .. ")")
|
|
IsInAnimation = true
|
|
return
|
|
end
|
|
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
|
|
end
|
|
|
|
elseif EmoteName.AnimationOptions.EmoteMoving then
|
|
MovementType = 51
|
|
elseif EmoteName.AnimationOptions.EmoteMoving == false then
|
|
MovementType = 0
|
|
elseif EmoteName.AnimationOptions.EmoteStuck then
|
|
MovementType = 50
|
|
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)
|
|
PtfxInfo = EmoteName.AnimationOptions.PtfxInfo
|
|
PtfxWait = EmoteName.AnimationOptions.PtfxWait
|
|
PtfxNotif = false
|
|
PtfxPrompt = true
|
|
PtfxThis(PtfxAsset)
|
|
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
|
|
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)
|
|
AddPropToPlayer(PropName, PropBone, PropPl1, PropPl2, PropPl3, PropPl4, PropPl5, PropPl6)
|
|
if SecondPropEmote then
|
|
AddPropToPlayer(SecondPropName, SecondPropBone, SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6)
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|