-- StarterPlayer/StarterPlayerScripts/RootMotionClient.lua
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local AbilityEvent = ReplicatedStorage:WaitForChild("RootMotionAbility")
local player = Players.LocalPlayer
local AbilityConfig = {
DashSlash = {
Duration = 0.4,
Range = 18,
},
}
local Animations = {
DashSlash = "",
}
local CurrentAbility = nil
local function easeInOutQuad(t)
if t <= 0 then
return 0
end
if t >= 1 then
return 1
end
if t < 0.5 then
return 2 * t * t
else
local u = -2 * t + 2
return 1 - (u * u) / 2
end
end
local function getCharacterParts()
local character = player.Character or player.CharacterAdded:Wait()
local root = character:FindFirstChild("HumanoidRootPart")
local humanoid = character:FindFirstChildOfClass("Humanoid")
local animator = humanoid and humanoid:FindFirstChildOfClass("Animator")
return character, root, humanoid, animator
end
local function playAbilityAnimation(abilityId)
local animId = Animations[abilityId]
if not animId or animId == "" then
print("Client no animation id for", abilityId, "movement will still play")
return
end
local _, _, humanoid, animator = getCharacterParts()
if not humanoid then
print("Client no humanoid for animation")
return
end
local targetAnimator = animator or humanoid:FindFirstChildOfClass("Animator")
if not targetAnimator then
print("Client no animator for animation")
return
end
local animation = Instance.new("Animation")
animation.AnimationId = animId
local track = targetAnimator:LoadAnimation(animation)
track:Play()
end
local function onServerStart(abilityId, startTime, startCFrame)
local config = AbilityConfig[abilityId]
if not config then
print("Client unknown ability", abilityId)
return
end
local _, root = getCharacterParts()
if not root then
print("Client no root to start ability")
return
end
if CurrentAbility then
print("Client ability already active, ignoring", abilityId)
return
end
CurrentAbility = {
AbilityId = abilityId,
StartTime = startTime,
StartCFrame = startCFrame,
Facing = startCFrame.LookVector,
LastAlpha = 0,
}
print("Client ability start", abilityId, "at", startTime)
playAbilityAnimation(abilityId)
end
AbilityEvent.OnClientEvent:Connect(function(action, abilityId, startTime, startCFrame)
if action == "Start" then
onServerStart(abilityId, startTime, startCFrame)
end
end)
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then
return
end
if input.UserInputType == Enum.UserInputType.Keyboard and input.KeyCode == Enum.KeyCode.Q then
if CurrentAbility then
print("Client ability already active, cannot request")
return
end
print("Client requesting DashSlash")
AbilityEvent:FireServer("Request", "DashSlash")
end
end)
RunService.RenderStepped:Connect(function()
local state = CurrentAbility
if not state then
return
end
local config = AbilityConfig[state.AbilityId]
if not config then
print("Client missing config during ability, cancelling")
CurrentAbility = nil
return
end
local _, root = getCharacterParts()
if not root then
print("Client lost root during ability, cancelling")
CurrentAbility = nil
return
end
local now = os.clock()
local alpha = (now - state.StartTime) / config.Duration
if alpha >= 1 then
print("Client ability finished", state.AbilityId)
CurrentAbility = nil
return
end
local distNow = config.Range * easeInOutQuad(alpha)
local distLast = config.Range * easeInOutQuad(state.LastAlpha)
state.LastAlpha = alpha
local target = state.StartCFrame + state.Facing * distNow
root.CFrame = root.CFrame:Lerp(target, 0.3)
end)