├── LICENSE ├── README.md ├── client.lua ├── config.lua ├── fxmanifest.lua └── index.html /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 PixelPrecision 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 🚀 Revolutionize your FiveM server with PixelPrecision's adjust animation script! 🚀 2 | 3 | Take control of your FiveM server's character animations like never before! PixelPrecision's free animation adjust script allows you to precisely adjust the position of your character animation playback effortlessly. Thanks to this tool, adapting the animation to any scenario or action is intuitive and precise. 4 | 5 | 🎮 **Character Customization at Your Fingertips**: Easily customize animations using a clone of your character. Change positions with precise camera control or relaxed manual control, ensuring characters sit, stand and move exactly as they should, without disruption. 6 | 7 | ⚙️ **Easy to install**: You can easily install this script on your server. Plug and play, without any major difficulties. 8 | 9 | 🆓 **Free**: The script is completely free and provides high-quality animation control without any additional costs. 10 | 11 | 12 | [Download](https://github.com/pixelprecisiondev/pp-adjustanim) 13 | 14 | [YouTube trailer](https://youtu.be/dQ3mEsm3YdI) 15 | 16 | [Documentation](https://docs.pixelprecision.dev/adjust-animation) 17 | 18 | [Discord](https://discord.gg/pixelprecision) 19 | 20 | ~~[Forum CFX Post]~~ not yet 21 | 22 | 23 | **Dependecies:** 24 | - [ox_lib](https://github.com/overextended/ox_lib) 25 | 26 | Transform your FiveM server with script from PixelPrecision, giving you unparalleled control over character positioning. Enhance player interactions and provide smooth, realistic movements that make every session unique. Grab this essential tool today and take your server to the next level! 27 | -------------------------------------------------------------------------------- /client.lua: -------------------------------------------------------------------------------- 1 | Citizen.CreateThread(function() 2 | SendNUIMessage({ 3 | action = 'updateTranslations', 4 | data = Config.Translations 5 | }) 6 | end) 7 | 8 | function adjustAnim() 9 | local animdata = Config.getCurrentAnimation() 10 | if not animdata then 11 | Config.Notify(Config.Translations['no_anim']) 12 | return 13 | end 14 | local clonePed = ClonePed(cache.ped, false, true, true) 15 | SetEntityAlpha(clonePed, Config.cloneAlpha) 16 | SetEntityNoCollisionEntity(cache.ped, clonePed, false) 17 | TaskPlayAnim(clonePed, animdata.dict, animdata.anim, 2.0, 2.0, -1, animdata.flag, 0, false, false, false) 18 | FreezeEntityPosition(cache.ped, true) 19 | local currentHeading = GetEntityHeading(cache.ped) 20 | local startPos = GetEntityCoords(cache.ped) 21 | Config.setupPed(clonePed) 22 | local nuiData = {} 23 | for _, key in pairs(Config.keys) do 24 | table.insert(nuiData, {key = key[2], text = Config.Translations.keys[key[2]]}) 25 | end 26 | SendNUIMessage({ 27 | action = 'open', 28 | data = nuiData 29 | }) 30 | Citizen.CreateThread(function() 31 | local enterPressed = false 32 | local xPressed = false 33 | local heightoffset = 0 34 | local moveX = 0 35 | local moveY = 0 36 | while not xPressed do 37 | Citizen.Wait(0) 38 | local hit, _, endCoords = lib.raycast.cam(1 | 2 | 16, 4, Config.maxDistance) 39 | if hit then 40 | local groundZ, isOnGround = GetGroundZFor_3dCoord(endCoords.x, endCoords.y, endCoords.z, 0) 41 | if isOnGround then 42 | SetEntityCoords(clonePed, endCoords.x + moveX, endCoords.y + moveY, endCoords.z + heightoffset) 43 | SetEntityHeading(clonePed, currentHeading) 44 | end 45 | end 46 | if not IsEntityPlayingAnim(clonePed, animdata.dict, animdata.anim, 3) then 47 | TaskPlayAnim(clonePed, animdata.dict, animdata.anim, 2.0, 2.0, -1, animdata.flag, 0, false, false, false) 48 | end 49 | 50 | -- enter 51 | if IsControlJustReleased(0, Config.keys.confirm[1]) and not enterPressed then 52 | if #(GetEntityCoords(cache.ped) - GetEntityCoords(clonePed)) <= Config.maxDistance then 53 | enterPressed = true 54 | keyPressed = true 55 | FreezeEntityPosition(cache.ped, false) 56 | local coords = GetEntityCoords(clonePed) 57 | SendNUIMessage({ 58 | action = 'close' 59 | }) 60 | if Config.walkToPosition then 61 | ClearPedTasksImmediately(cache.ped) 62 | Citizen.Wait(400) 63 | TaskGoStraightToCoord(cache.ped, coords, 1.0, -1, currentHeading, 0.0) 64 | 65 | while GetScriptTaskStatus(cache.ped, "SCRIPT_TASK_GO_STRAIGHT_TO_COORD") ~= 7 do 66 | Citizen.Wait(100) 67 | end 68 | end 69 | SetEntityCoordsNoOffset(cache.ped, coords.x, coords.y, coords.z) 70 | SetEntityHeading(cache.ped, currentHeading) 71 | DeletePed(clonePed) 72 | ExecuteCommand(animdata.command) 73 | FreezeEntityPosition(cache.ped, true) 74 | Citizen.Wait(0) 75 | end 76 | end 77 | 78 | -- X 79 | if IsControlPressed(0, Config.keys.cancel[1]) then 80 | DeletePed(clonePed) 81 | SendNUIMessage({ 82 | action = 'close' 83 | }) 84 | FreezeEntityPosition(cache.ped, false) 85 | if Config.returnToStart then 86 | SetEntityCoords(cache.ped, startPos.x, startPos.y, startPos.z) 87 | end 88 | xPressed = true 89 | break 90 | end 91 | 92 | -- Q 93 | if IsControlPressed(0, Config.keys.rleft[1]) then 94 | currentHeading = currentHeading - Config.rotateSpeed 95 | end 96 | 97 | -- E 98 | if IsControlPressed(0, Config.keys.rright[1]) then 99 | currentHeading = currentHeading + Config.rotateSpeed 100 | end 101 | 102 | if IsControlPressed(0, Config.keys.up[1]) then 103 | heightoffset = heightoffset + Config.movementSpeed 104 | end 105 | 106 | if IsControlPressed(0, Config.keys.down[1]) then 107 | heightoffset = heightoffset - Config.movementSpeed 108 | end 109 | 110 | if IsControlPressed(0, Config.keys.left[1]) then 111 | moveX = moveX - math.cos(math.rad(currentHeading)) * Config.movementSpeed 112 | moveY = moveY - math.sin(math.rad(currentHeading)) * Config.movementSpeed 113 | elseif IsControlPressed(0, Config.keys.right[1]) then 114 | moveX = moveX + math.cos(math.rad(currentHeading)) * Config.movementSpeed 115 | moveY = moveY + math.sin(math.rad(currentHeading)) * Config.movementSpeed 116 | end 117 | 118 | if IsControlPressed(0, Config.keys.forward[1]) then 119 | moveX = moveX + math.cos(math.rad(currentHeading + 90)) * Config.movementSpeed 120 | moveY = moveY + math.sin(math.rad(currentHeading + 90)) * Config.movementSpeed 121 | elseif IsControlPressed(0, Config.keys.backward[1]) then 122 | moveX = moveX + math.cos(math.rad(currentHeading - 90)) * Config.movementSpeed 123 | moveY = moveY + math.sin(math.rad(currentHeading - 90)) * Config.movementSpeed 124 | end 125 | end 126 | end) 127 | end 128 | 129 | RegisterCommand(Config.command, adjustAnim) -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | Config = {} 2 | 3 | Config.keys = { 4 | confirm = {191, 'Enter'}, 5 | cancel = {73, 'X'}, 6 | rleft = {44, 'Q'}, 7 | rright = {38, 'E'}, 8 | up = {45, 'R'}, 9 | down = {23, 'F'}, 10 | left = {34, 'A'}, 11 | right = {35, 'D'}, 12 | forward = {32, 'W'}, 13 | backward = {33, 'S'} 14 | } 15 | 16 | Config.Translations = { 17 | ['title'] = 'Adjust animation', 18 | ['no_anim'] = 'Use animation first!', 19 | keys = { 20 | ['X'] = 'Cancel', 21 | ['Enter'] = 'Confirm', 22 | ['Q'] = 'Rot. left', 23 | ['E'] = 'Rot. right', 24 | ['R'] = 'Up', 25 | ['F'] = 'Down', 26 | ['A'] = 'Left', 27 | ['D'] = 'Right', 28 | ['W'] = 'Forward', 29 | ['S'] = 'Backward' 30 | } 31 | } 32 | 33 | Config.command = 'adjust' 34 | 35 | Config.setupPed = function(ped) 36 | SetEntityInvincible(ped, true) 37 | SetBlockingOfNonTemporaryEvents(ped, true) 38 | TaskSetBlockingOfNonTemporaryEvents(ped, true) 39 | FreezeEntityPosition(ped, true) 40 | end 41 | 42 | Config.maxDistance = 30 43 | 44 | Config.rotateSpeed = 5 45 | 46 | Config.movementSpeed = 0.05 47 | 48 | Config.cloneAlpha = 204 49 | 50 | Config.returnToStart = true 51 | 52 | Config.walkToPosition = true 53 | 54 | Config.getCurrentAnimation = function() 55 | if GetResourceState('scully_emotemenu') == 'started' then 56 | local data, lastVariant = exports.scully_emotemenu:getLastEmote() 57 | if not data then return end 58 | local movementFlag = cache.vehicle and 51 or 0 59 | if not cache.vehicle and data.Options.Flags then 60 | if data.Options.Flags.Loop then movementFlag = 1 end 61 | if data.Options.Flags.Move then movementFlag = 51 end 62 | if data.Options.Flags.Stuck then movementFlag = 50 end 63 | end 64 | if not IsEntityPlayingAnim(cache.ped, data.Dictionary, data.Animation, 3) then return end 65 | return { 66 | dict = data.Dictionary, 67 | anim = data.Animation, 68 | flag = movementFlag, 69 | command = 'e ' .. data.Command 70 | } 71 | elseif GetResourceState('rpemotes') == 'started' then 72 | --[[ 73 | ================================================================================================= 74 | Make sure you are using the modified version that is required for the script to function properly 75 | https://docs.pixelprecision.dev 76 | ================================================================================================= 77 | ]] 78 | local data = exports.rpemotes:getCurrentEmote() 79 | if not data then return end 80 | local MovementType = cache.vehicle and 51 or 0 81 | if data.AnimationOptions then 82 | if data.AnimationOptions.EmoteLoop then 83 | MovementType = 1 84 | if data.AnimationOptions.EmoteMoving then 85 | MovementType = 51 86 | end 87 | 88 | elseif data.AnimationOptions.EmoteMoving then 89 | MovementType = 51 90 | elseif data.AnimationOptions.EmoteMoving == false then 91 | MovementType = 0 92 | elseif data.AnimationOptions.EmoteStuck then 93 | MovementType = 50 94 | end 95 | 96 | else 97 | MovementType = 0 98 | end 99 | 100 | if InVehicle == 1 then 101 | MovementType = 51 102 | end 103 | if not IsEntityPlayingAnim(cache.ped, data[1], data[2], 3) then return end 104 | return { 105 | dict = data[1], 106 | anim = data[2], 107 | flag = MovementType, 108 | command = 'e ' .. data[4] 109 | } 110 | end 111 | end 112 | 113 | Config.Notify = function(message) 114 | lib.notify({ 115 | title = 'Adjust animation', 116 | description = message, 117 | type = 'info' 118 | }) 119 | end -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'bodacious' 2 | game 'gta5' 3 | lua54 'yes' 4 | author 'PixelPrecision' 5 | 6 | version '1.0.0' 7 | 8 | description 'Adjust Animation' 9 | 10 | client_scripts { 11 | 'config.lua', 12 | 'client.lua' 13 | } 14 | 15 | shared_scripts { 16 | '@ox_lib/init.lua' 17 | } 18 | 19 | ui_page 'index.html' 20 | 21 | files { 22 | 'index.html' 23 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | PP-Adjust 8 | 115 | 116 | 117 | 123 | 187 | 188 | 189 | --------------------------------------------------------------------------------