Untitled

mail@pastecode.io avatar
unknown
lua
a month ago
19 kB
2
Indexable
Never
---------------------------
-- author: maksgame19    --
-- version: 1.0          --
---------------------------
local Players = game.Players or game:GetService("Players")

local playersData = {}

local legacyChat = (game.TextChatService or game:GetService("TextChatService")).ChatVersion == Enum.ChatVersion.LegacyChatService
local allowCustomAnims = (game.StarterPlayer or game:GetService("StarterPlayer")).AllowCustomAnimations
local localPlrID = Players.localPlayer.UserId


local freefall, idle1, idle2, jumping, running, seated, walk, swimmingIdle, swimming, defaultTool, climbing, emotes--animations

local customState, localPlayerTable, oldAnim

local function startSound(playerTable, sound)
	local oldSound = playerTable.oldSound
	if oldSound ~= sound then
		if oldSound then
			oldSound:Pause()--can't do "oldSound.Playing = false" because it will privent from fireing Paused event
		end
		if sound then
			sound.Playing = true
		end
		playerTable.oldSound = sound
	end
end

local function startSoundAndAnim(sound, anim, transitionTime, state)
	startSound(localPlayerTable, sound)
	if oldAnim ~= anim then
		if oldAnim then
			oldAnim:Stop(transitionTime)
		end
		if anim then
			anim:Play(transitionTime)
		end
		customState = state
		oldAnim = anim
	end
end

local function startEmote(emote)
	if emote then
		startSoundAndAnim(nil, emote, 0.5, "Idle")
		emote.Stopped:Wait()
		task.wait()
		if customState == "Idle" and emote == oldAnim then
			startSoundAndAnim(nil, idle1, 0.1, "Idle")
		end
	end	
end

local function adjustWalkAndRunProperties(walkWaight, runWaight, speed)
	walk:AdjustWeight(walkWaight)
	running:AdjustWeight(runWaight)
	walk:AdjustSpeed(speed)
	running:AdjustSpeed(speed)
end

local localStateFunctions = {
	[Enum.HumanoidStateType.Jumping] = function()
		local sounds = localPlayerTable.sounds
		startSoundAndAnim(sounds.freefall, jumping, 0.1, "jump")
		local jumpingSound = sounds.jumping
		jumpingSound.TimePosition = 0
		jumpingSound.Playing = true
	end,
	[Enum.HumanoidStateType.Freefall] = function(rootPart)
		local freefallSound = localPlayerTable.sounds.freefall
		freefallSound.Volume = 0
		if customState == "jump" then
			task.wait(0.3)--jump duration
			if not freefallSound.IsPlaying then return end
		end
		startSoundAndAnim(freefallSound, freefall, 0.2)
		local thread = task.spawn(function()
			while rootPart.assemblyLinearVelocity.Y > -75 do
				task.wait(0.05)
			end
			for i = 0, 1, 0.05 do
				freefallSound.Volume = i
				task.wait(0.05)
			end
		end) 
		freefallSound.Paused:Wait()
		task.cancel(thread)
	end,
	[Enum.HumanoidStateType.Seated] = function()
		startSoundAndAnim(nil, seated, 0.5)
	end,
}

local stateFunctions = {
	[Enum.HumanoidStateType.Jumping] = function(playerTable)
		local jump = playerTable.sounds.jumping
		jump.TimePosition = 0
		jump.Playing = true
	end,
	[Enum.HumanoidStateType.Freefall] = function(playerTable, rootPart)
		local freefall = playerTable.sounds.freefall
		freefall.Volume = 0
		startSound(playerTable)
		task.wait(0.3)
		if freefall.IsPlaying then
			local thread = task.spawn(function()
				repeat
					freefall.Volume = 0
					while rootPart.assemblyLinearVelocity.Y > -75 do
						task.wait(0.1)
					end
					for i = 0, 1, 0.1 do
						freefall.Volume = i
						task.wait(0.1)
					end
				until rootPart.assemblyLinearVelocity.Y < -75
			end)
			freefall.Paused:Wait()
			task.cancel(thread)
		end
	end,
	[Enum.HumanoidStateType.Swimming] = function(playerTable, rootPart)
		local sounds = playerTable.sounds
		startSound(playerTable, sounds.swimming)
		rootPart = math.abs(rootPart.assemblyLinearVelocity.Y)
		if rootPart > 0.1 then
			local splash = sounds.splash
			splash.Volume = rootPart * 0.00288 - 0.008 --map a value from one range to another (velY - 100)*(1 - 0.28)/(350 - 100) + 0.28
			splash:Play()
		end
	end,
	[Enum.HumanoidStateType.GettingUp] = function(playerTable)
		playerTable.sounds.gettingUp.Playing = true
	end,
	[Enum.HumanoidStateType.Landed] = function(playerTable, rootPart)
		rootPart = -rootPart.assemblyLinearVelocity.Y
		if rootPart > 75 then
			local landing = playerTable.sounds.landing
			landing.Volume = rootPart/50 - 1--map a value from one range to another (velY - 50)*(1 - 0)/(100 - 50) + 0
			landing.Playing = true
		end
	end,
	[Enum.HumanoidStateType.Dead] = function(playerTable)
		startSound(playerTable)
		playerTable.sounds.died.Playing = true
		if playerTable.garbage then--for some reason running event in R15 code doesn't get disconnected so we need to do that manualy
			localPlayerTable.garbage:Disconnect()
		end
		playersData[playerTable.plrID] = nil
	end,
	[Enum.HumanoidStateType.Climbing] = function() end,
	[Enum.HumanoidStateType.Running] = function() end,
}

local function createSystem(roots, char, plrID, localplr)
	local humanoid, rootPart = roots.Humanoid, roots.HumanoidRootPart
	local _, activeState = pcall(function() return humanoid:GetState() end)

	playersData[plrID] = {}
	local playerTable = playersData[plrID]
	playerTable.plrID = plrID

	playerTable.sounds = {}
	local sounds = playerTable.sounds

	local soundsRes = script.Sounds:Clone()
	for _, sound in soundsRes:GetChildren() do
		sound.Parent = rootPart
		sounds[sound.Name] = sound
	end
	soundsRes.Parent = nil
	if not localplr then
		--connections
		humanoid.StateChanged:Connect(function(_, state)--state tracker
			activeState = state
			local func = stateFunctions[activeState]
			if func then
				func(playerTable, rootPart)
			else
				startSound(playerTable)
			end
		end)
		humanoid.running:Connect(function(vel)
			if humanoid.MoveDirection.Magnitude > 0 then
				local runSound = sounds.running
				startSound(playerTable, runSound)
				runSound.PlaybackSpeed = 1.85
			else
				startSound(playerTable)
			end
		end)
		humanoid.climbing:Connect(function(vel)
			if vel ~= 0 then 
				local runSound = sounds.running
				startSound(playerTable, runSound)
				sounds.running.PlaybackSpeed = 1
			else
				startSound(playerTable)
			end
		end)
		--initialize first sound
		local func = stateFunctions[activeState]
		if func then
			func(playerTable, rootPart)
		end
	else --local player handler
		local animate = roots.Animate
		local rig = humanoid.RigType.Name
		local animsPack = script[rig]
		local animator = roots.Animator
		localPlayerTable = playerTable
		emotes = {}

		local function loadNewAnim(anim, priority)
			anim = animator:LoadAnimation(anim)
			anim.Priority = priority
			return anim
		end
		--load animations
		for _, emote in animsPack.emotes:GetChildren() do
			local name = emote.Name
			emotes[name] = loadNewAnim(emote, Enum.AnimationPriority.Core)
			if string.sub(name, 1, 5) ~= "dance" then--roblox set looped to true in every emote animation but only dance's should be looped
				emotes[name].Looped = false
			end
		end
		defaultTool, seated = loadNewAnim(animsPack.defaultTool, Enum.AnimationPriority.Idle), loadNewAnim(animsPack.seated, Enum.AnimationPriority.Core)

		if legacyChat and rig == "R15" or not legacyChat then
			roots.PlayEmote.OnInvoke = function(emote)--custom emote
				if customState == "Idle" then
					local loadedEmote = emotes[emote]
					if loadedEmote then
						startEmote(loadedEmote)
						return true
					elseif emote:IsA("Instance") then
						local name = emote.Name
						emotes[name] = loadNewAnim(emote, Enum.AnimationPriority.Core)
						loadedEmote = emotes[name]
						loadedEmote.Looped = false
						startEmote(loadedEmote)
						return true
					end
				end
			end
		end
		humanoid.StateChanged:Connect(function(_, state)--state tracker
			activeState = state
			local localFunc = localStateFunctions[activeState]
			if localFunc then
				localFunc(rootPart)
				return
			end
			local func = stateFunctions[activeState]
			if func then
				func(localPlayerTable, rootPart)
			else
				startSoundAndAnim()
			end
		end)
		char.ChildAdded:Connect(function(child)
			if child:IsA("Tool") and child:FindFirstChild("Handle") then
				defaultTool:Play()
			end
		end)
		char.ChildRemoved:Connect(function(child)
			if child:IsA("Tool") and child:FindFirstChild("Handle") then
				defaultTool:Stop()
			end
		end)
		if rig == "R15" then
			local humanoidDescription = roots.HumanoidDescription
			local oldHipHeight = humanoid.HipHeight
			local scale = 6.4 * oldHipHeight--16 / 1.25 * 2 / (1 + (oldHipHeight - 2) * 1 / 2)
			
			local walkIsPlaying
			
			localStateFunctions[Enum.HumanoidStateType.Swimming] = nil
			
			local function Connections()
				localPlayerTable.garbage = humanoid.running:Connect(function(vel)
					if humanoid.MoveDirection.Magnitude > 0 then
						local runSound = sounds.running
						startSoundAndAnim(runSound, running, 0.1)
						runSound.PlaybackSpeed = 1.85
						if not walkIsPlaying then
							walkIsPlaying = true
							walk:Play()
						end
						if humanoid.HipHeight ~= oldHipHeight then
							oldHipHeight = humanoid.HipHeight
							scale = 6.4 * oldHipHeight --16 / 1.25 / (1 + (oldHipHeight - 2) * 1 / 2)
						end
						vel /= scale
						if vel <= 0.5 then
							adjustWalkAndRunProperties(1, 0.0001, vel / 0.5)
						elseif vel < 1 then
							vel = vel/0.5 - 1 --(vel-0.5)/0.5
							adjustWalkAndRunProperties(1 - vel, vel , 1)
						else
							adjustWalkAndRunProperties(0.0001, 1, vel)
						end
						if walkIsPlaying == true then
							running.Stopped:Wait()
							walk:Stop()
							walkIsPlaying = nil
						end
					else
						startSoundAndAnim(nil, idle1, 0.2, "Idle")
					end
				end)
				humanoid.climbing:Connect(function(vel)
					if vel ~= 0 then
						local runSound = sounds.running
						startSoundAndAnim(runSound, climbing, 0.1)
						climbing:AdjustSpeed(vel / 5)
						runSound.PlaybackSpeed = 1
					else
						startSound(localPlayerTable)
						climbing:AdjustSpeed(0)
					end
				end)
				humanoid.swimming:Connect(function(vel)
					if vel > 1 then
						startSoundAndAnim(sounds.swimming, swimming, 0.3)
						swimming:AdjustSpeed(vel / 10)
					else
						startSoundAndAnim(sounds.swimming, swimmingIdle, 0.5)
					end
				end)
				idle1.DidLoop:Connect(function()
					if math.random(1, 10) == 1 then
						idle1:Stop()
						idle2:Play()
						oldAnim = idle2
					end
				end)
				idle2.DidLoop:Connect(function()
					idle2:Stop()
					idle1:Play()
					oldAnim = idle1
				end)
			end
			
			--load the rest needed animations
			if allowCustomAnims and humanoidDescription then
				local descriptionAnims = {["ClimbAnimation"] = false, ["FallAnimation"] = false, ["IdleAnimation"] = true, ["JumpAnimation"] = false, ["RunAnimation"] = false, ["WalkAnimation"] = false, ["SwimAnimation"] = true}
				local connections, foundAnims = {}, {}
				local requiredAnims, count = 0, 0
				
				for idx,anim in descriptionAnims do
					if humanoidDescription[idx] ~= 0 then
						if anim then
							requiredAnims += 2
						else
							requiredAnims += 1
						end
					end
				end
				
				if requiredAnims == 0 then
					climbing, freefall, idle1, idle2, jumping, running, walk, swimming, swimmingIdle = loadNewAnim(animsPack.climbing, Enum.AnimationPriority.Core), loadNewAnim(animsPack.freefall, Enum.AnimationPriority.Core), loadNewAnim(animsPack.idle1, Enum.AnimationPriority.Core), loadNewAnim(animsPack.idle2, Enum.AnimationPriority.Core), loadNewAnim(animsPack.jumping, Enum.AnimationPriority.Core), loadNewAnim(animsPack.running, Enum.AnimationPriority.Core), loadNewAnim(animsPack.walk, Enum.AnimationPriority.Core), loadNewAnim(animsPack.swimming, Enum.AnimationPriority.Core), loadNewAnim(animsPack.swimmingIdle, Enum.AnimationPriority.Core)
					Connections()
					return
				end
				
				local function checkChild(parent)
					if not parent:IsA("Animation") then
						for	_,child in parent:GetChildren() do
							checkChild(child)
						end
						table.insert(connections, parent.ChildAdded:Connect(function(child)
							checkChild(child)
						end))
						return
					elseif parent.Parent.Name == "pose" or parent.Parent.Name == "mood" then
					 	return
					end
					foundAnims[parent.Name] = parent
					count += 1
					if count == requiredAnims then-- all Animations found
						for _,conn in connections do
							conn:Disconnect()
						end
						climbing, freefall, idle1, idle2, jumping, running, walk, swimming, swimmingIdle = loadNewAnim(foundAnims.ClimbAnim or animsPack.climbing, Enum.AnimationPriority.Core), loadNewAnim(foundAnims.FallAnim or animsPack.freefall, Enum.AnimationPriority.Core), loadNewAnim(foundAnims.Animation1 or animsPack.idle1, Enum.AnimationPriority.Core), loadNewAnim(foundAnims.Animation2 or animsPack.idle2, Enum.AnimationPriority.Core), loadNewAnim(foundAnims.JumpAnim or animsPack.jumping, Enum.AnimationPriority.Core), loadNewAnim(foundAnims.RunAnim or animsPack.running, Enum.AnimationPriority.Core), loadNewAnim(foundAnims.WalkAnim or animsPack.walk, Enum.AnimationPriority.Core), loadNewAnim(foundAnims.Swim or animsPack.swimming, Enum.AnimationPriority.Core), loadNewAnim(foundAnims.SwimIdle or animsPack.swimmingIdle, Enum.AnimationPriority.Core)
						Connections()
					end
				end
				checkChild(animate)
			else
				climbing, freefall, idle1, idle2, jumping, running, walk, swimming, swimmingIdle = loadNewAnim(animsPack.climbing, Enum.AnimationPriority.Core), loadNewAnim(animsPack.freefall, Enum.AnimationPriority.Core), loadNewAnim(animsPack.idle1, Enum.AnimationPriority.Core), loadNewAnim(animsPack.idle2, Enum.AnimationPriority.Core), loadNewAnim(animsPack.jumping, Enum.AnimationPriority.Core), loadNewAnim(animsPack.running, Enum.AnimationPriority.Core), loadNewAnim(animsPack.walk, Enum.AnimationPriority.Core), loadNewAnim(animsPack.swimming, Enum.AnimationPriority.Core), loadNewAnim(animsPack.swimmingIdle, Enum.AnimationPriority.Core)
				Connections()
			end
		else--R6
			localStateFunctions[Enum.HumanoidStateType.Swimming] = function(velY)
				startSoundAndAnim(sounds.swimming, running, 0.4)
				velY = math.abs(velY.assemblyLinearVelocity.Y)
				if velY > 0.1 then
					local splash = sounds.splash
					splash.Volume = velY * 0.00288 - 0.008 --map a value from one range to another (velY - 100)*(1 - 0.28)/(350 - 100) + 0.28
					splash:Play()
				end
			end
			--load the rest needed animations
			climbing, freefall, idle1, idle2, jumping, running = loadNewAnim(animsPack.climbing, Enum.AnimationPriority.Core), loadNewAnim(animsPack.freefall, Enum.AnimationPriority.Core), loadNewAnim(animsPack.idle1, Enum.AnimationPriority.Core), loadNewAnim(animsPack.idle2, Enum.AnimationPriority.Core), loadNewAnim(animsPack.jumping, Enum.AnimationPriority.Core), loadNewAnim(animsPack.running, Enum.AnimationPriority.Core)

			--connections
			humanoid.running:Connect(function(vel)
				if humanoid.MoveDirection.Magnitude > 0 then
					print(1)
					local runSound = sounds.running
					startSoundAndAnim(runSound, running, 0.1)
					running:AdjustSpeed(vel / 14.5)
					runSound.PlaybackSpeed = 1.85
				else
					startSoundAndAnim(nil, idle1, 0.1, "Idle")
				end
			end)
			humanoid.climbing:Connect(function(vel)
				if vel ~= 0 then
					local runSound = sounds.running
					startSoundAndAnim(runSound, climbing, 0.1)
					climbing:AdjustSpeed(vel / 12)
					runSound.PlaybackSpeed = 1
				else
					startSound(localPlayerTable)
					climbing:AdjustSpeed(0)
				end
			end)
			idle1.DidLoop:Connect(function()
				if math.random(1, 10) == 1 then
					idle1:Stop()
					idle2:Play()
					oldAnim = idle2
				end
			end)
			idle2.DidLoop:Connect(function()
				idle2:Stop()
				idle1:Play()
				oldAnim = idle1
			end)
		end

		--initialize first animation and/or sound
		local func = localStateFunctions[activeState]
		if func then
			func(rootPart)
		else
			startSoundAndAnim(nil, idle1, nil, "Idle")
		end
	end
end

local function WaitForInstances(char, plrId)-- instantly get instances that just loaded
	local requiredInstances, instancesToFind, localplr, allInstancesFound
	local foundInstances, connections = {}, {}
	local count = 0

	if plrId ~= localPlrID then
		localplr = false
		requiredInstances = 2
		instancesToFind = {
			["Humanoid"] = true,
			["HumanoidRootPart"] = true
		}
	else
		localplr = true
		requiredInstances = 6
		instancesToFind = {				
			["Animate"] = "PlayEmote",
			["Humanoid"] = {
				["Animator"] = true,
				["HumanoidDescription"] = true
			},
			["HumanoidRootPart"] = true
		}
	end
	

	local function checkChild(parent, instancesToFind)
		local name = parent.Name
		local nextInstancesToFind = instancesToFind[name] or name == instancesToFind
		if not nextInstancesToFind then
			return
		end
		foundInstances[name] = parent
		count += 1
		if count == requiredInstances then -- all instances found
			allInstancesFound = true
			for _,conn in connections do
				conn:Disconnect()
			end
			createSystem(foundInstances, char, plrId, localplr)
		elseif nextInstancesToFind ~= true then
			for	_,child in parent:GetChildren() do
				checkChild(child, nextInstancesToFind)
			end
			table.insert(connections, parent.ChildAdded:Connect(function(child)
				checkChild(child, nextInstancesToFind)
			end))
		end
	end
	for	_,child in char:GetChildren() do
		checkChild(child, instancesToFind)
	end
	table.insert(connections, char.ChildAdded:Connect(function(child)
		checkChild(child, instancesToFind)
	end))
	--timeout
	task.wait(0.1)
	if not allInstancesFound then
		if count == 5 and not foundInstances.HumanoidDescription then -- there is a possibility that player will load without custom animation  
			for _,conn in connections do
				conn:Disconnect()
			end
			createSystem(foundInstances, char, plrId, localplr)
		else
			for _,conn in connections do
				conn:Disconnect()
			end
			warn("needed instances doesn't exist")
		end
	end
end


local function setup(plr)
	local char = plr.Character
	if char then
		WaitForInstances(char, plr.UserId)
	end
	plr.CharacterAdded:Connect(function(char)
		WaitForInstances(char, plr.UserId)
	end)
end

for _, plr in Players:GetChildren() do -- a bit faster from GetPlayers
	setup(plr)
end

Players.PlayerAdded:Connect(setup)

Players.PlayerRemoving:Connect(function(plr)
	playersData[plr.UserId] = nil
end)

--emote executor
if legacyChat then
	Players.localPlayer.Chatted:connect(function(msg)
		if string.sub(msg, 1, 1) == "/" and customState == "Idle" then
			msg = string.lower(msg)
			if string.sub(msg, 2, 3) == "e " then
				startEmote(emotes[string.sub(msg, 4)])
			elseif string.sub(msg, 2, 7) == "emote " then
				startEmote(emotes[string.sub(msg, 8)])
			end
		end
	end)
end
Leave a Comment