Tail Wag (server)
腳本程式碼
--tungsten88474 (hat tool) and mxsynry (fix) on discord
local env = (getgenv and getgenv()) or _G
env.__TailAnim = env.__TailAnim or {}
local TA = env.__TailAnim
if type(TA._cleanup) == "function" then
pcall(TA._cleanup)
end
TA.Enabled = true
TA.Speed = 10.0
TA.Amplitude = 35
-- Optional:
-- Set this to the exact accessory name when automatic detection fails.
-- Example:
-- TA.AccessoryName = "White Fox Tail"
TA.AccessoryName = TA.AccessoryName or nil
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local localPlayer = Players.LocalPlayer
if not localPlayer then
error("TailAnim: LocalPlayer is unavailable")
end
local characterConnection = nil
local childConnection = nil
local renderConnection = nil
local currentCharacter = nil
local currentAccessory = nil
local realHandle = nil
local fakeAccessory = nil
local fakeHandle = nil
local fakeJoint = nil
local fakeBaseC0 = nil
local savedJoint = nil
local savedJointParent = nil
local animationTime = 0
local currentYaw = 0
local lastSearchTime = 0
local bindGeneration = 0
local function disconnect(connection)
if connection then
pcall(function()
connection:Disconnect()
end)
end
end
local function lower(value)
return string.lower(tostring(value or ""))
end
local function containsPlain(value, searchText)
return string.find(
lower(value),
lower(searchText),
1,
true
) ~= nil
end
local function isHairRelatedName(value)
local name = lower(value)
return string.find(name, "ponytail", 1, true) ~= nil
or string.find(name, "pony tail", 1, true) ~= nil
or string.find(name, "pigtail", 1, true) ~= nil
or string.find(name, "pig tail", 1, true) ~= nil
or string.find(name, "hair", 1, true) ~= nil
or string.find(name, "braid", 1, true) ~= nil
or string.find(name, "bangs", 1, true) ~= nil
or string.find(name, "hairstyle", 1, true) ~= nil
end
local function hasTailWord(value)
local text = lower(value)
if text == "" or isHairRelatedName(text) then
return false
end
-- Match "tail" as a separate word.
if string.match(text, "%f[%a]tail%f[%A]") then
return true
end
-- Common separators used in accessory names.
if string.find(text, "_tail", 1, true)
or string.find(text, "tail_", 1, true)
or string.find(text, "-tail", 1, true)
or string.find(text, "tail-", 1, true)
then
return true
end
return false
end
local function getHandle(accessory)
if not accessory or not accessory:IsA("Accessory") then
return nil
end
local handle = accessory:FindFirstChild("Handle")
if handle and handle:IsA("BasePart") then
return handle
end
return nil
end
local function getAccessoryJoint(accessory)
local handle = getHandle(accessory)
if not handle then
return nil
end
local accessoryWeld = handle:FindFirstChild("AccessoryWeld")
if accessoryWeld and (
accessoryWeld:IsA("Weld")
or accessoryWeld:IsA("Motor6D")
) then
return accessoryWeld
end
for _, object in ipairs(handle:GetChildren()) do
if object:IsA("Motor6D") or object:IsA("Weld") then
return object
end
end
return nil
end
local function getAccessoryType(accessory)
local success, accessoryType = pcall(function()
return accessory.AccessoryType
end)
if success then
return accessoryType
end
return nil
end
local function hasHairAttachment(accessory)
local handle = getHandle(accessory)
if not handle then
return false
end
for _, object in ipairs(handle:GetDescendants()) do
if object:IsA("Attachment") then
local attachmentName = lower(object.Name)
if string.find(
attachmentName,
"hairattachment",
1,
true
) then
return true
end
end
end
return false
end
local function hasTailAttachment(accessory)
local handle = getHandle(accessory)
if not handle then
return false
end
for _, object in ipairs(handle:GetDescendants()) do
if object:IsA("Attachment") then
if hasTailWord(object.Name) then
return true
end
end
end
return false
end
local function hasWaistOrBackAttachment(accessory)
local handle = getHandle(accessory)
if not handle then
return false
end
for _, object in ipairs(handle:GetDescendants()) do
if object:IsA("Attachment") then
local attachmentName = lower(object.Name)
if string.find(
attachmentName,
"waistbackattachment",
1,
true
)
or string.find(
attachmentName,
"waistcenterattachment",
1,
true
)
or string.find(
attachmentName,
"bodybackattachment",
1,
true
)
then
return true
end
end
end
return false
end
local function hasTailMarkerInDescendants(accessory)
local handle = getHandle(accessory)
if not handle then
return false
end
for _, object in ipairs(handle:GetDescendants()) do
if hasTailWord(object.Name) then
return true
end
if object:IsA("SpecialMesh") then
local meshId = ""
local textureId = ""
pcall(function()
meshId = object.MeshId
end)
pcall(function()
textureId = object.TextureId
end)
if hasTailWord(meshId) or hasTailWord(textureId) then
return true
end
end
end
return false
end
local function matchesConfiguredAccessory(accessory)
if type(TA.AccessoryName) ~= "string"
or TA.AccessoryName == ""
then
return false
end
return lower(accessory.Name) == lower(TA.AccessoryName)
end
local function isTailAccessory(accessory)
if not accessory or not accessory:IsA("Accessory") then
return false
end
if accessory:GetAttribute("__TailAnimFake") == true then
return false
end
if not getHandle(accessory) then
return false
end
-- An explicitly configured accessory name takes priority.
if matchesConfiguredAccessory(accessory) then
return true
end
local accessoryType = getAccessoryType(accessory)
-- Never select an accessory Roblox identifies as hair.
if accessoryType == Enum.AccessoryType.Hair then
return false
end
-- Reject hair attachments even if the accessory type is incorrect.
if hasHairAttachment(accessory) then
return false
end
-- Reject common hairstyle names containing the word "tail."
if isHairRelatedName(accessory.Name) then
return false
end
local nameHasTail = hasTailWord(accessory.Name)
local attachmentHasTail = hasTailAttachment(accessory)
local descendantsHaveTail =
hasTailMarkerInDescendants(accessory)
if not nameHasTail
and not attachmentHasTail
and not descendantsHaveTail
then
return false
end
-- A tail normally uses a waist or back attachment.
if hasWaistOrBackAttachment(accessory) then
return true
end
-- Accept clearly named tail accessories unless they are hair.
if nameHasTail then
return true
end
return attachmentHasTail or descendantsHaveTail
end
local function configureInvisibleAccessory(accessory)
for _, object in ipairs(accessory:GetDescendants()) do
if object:IsA("BasePart") then
object.Transparency = 1
object.CanCollide = false
object.CanTouch = false
object.CanQuery = false
object.Massless = true
pcall(function()
object.CastShadow = false
end)
end
end
end
local function configureRealHandle(handle)
handle.Anchored = false
handle.CanCollide = false
handle.CanTouch = false
handle.CanQuery = false
handle.Massless = true
end
local function destroyFakeAccessory()
if fakeAccessory then
pcall(function()
fakeAccessory:Destroy()
end)
end
fakeAccessory = nil
fakeHandle = nil
fakeJoint = nil
fakeBaseC0 = nil
end
local function restoreOriginalJoint()
if not savedJoint then
return
end
local joint = savedJoint
savedJoint = nil
if currentAccessory
and currentAccessory.Parent
and realHandle
and realHandle.Parent
and not getAccessoryJoint(currentAccessory)
then
local restored = pcall(function()
joint.Parent = savedJointParent or realHandle
end)
if restored then
savedJointParent = nil
return
end
end
pcall(function()
joint:Destroy()
end)
savedJointParent = nil
end
local function releaseCurrentAccessory(restoreOriginal)
destroyFakeAccessory()
if restoreOriginal then
restoreOriginalJoint()
elseif savedJoint then
pcall(function()
savedJoint:Destroy()
end)
savedJoint = nil
savedJointParent = nil
end
currentAccessory = nil
realHandle = nil
animationTime = 0
currentYaw = 0
end
local function removeOldFakeAccessories(character)
if not character then
return
end
for _, child in ipairs(character:GetChildren()) do
if child:IsA("Accessory")
and child:GetAttribute("__TailAnimFake") == true
then
pcall(function()
child:Destroy()
end)
end
end
end
local function attachTail(accessory)
if not accessory
or not accessory.Parent
or not accessory:IsA("Accessory")
then
return false
end
if not isTailAccessory(accessory) then
return false
end
local handle = getHandle(accessory)
local originalJoint = getAccessoryJoint(accessory)
if not handle or not originalJoint then
return false
end
releaseCurrentAccessory(true)
local previousArchivable = accessory.Archivable
accessory.Archivable = true
local cloneSuccess, clonedAccessory = pcall(function()
return accessory:Clone()
end)
accessory.Archivable = previousArchivable
if not cloneSuccess or not clonedAccessory then
warn(
"TailAnim: unable to clone accessory "
.. tostring(accessory.Name)
)
return false
end
clonedAccessory.Name =
"__TailAnimFake_" .. tostring(accessory.Name)
clonedAccessory:SetAttribute("__TailAnimFake", true)
configureInvisibleAccessory(clonedAccessory)
clonedAccessory.Parent = accessory.Parent
local clonedHandle = getHandle(clonedAccessory)
local clonedJoint = getAccessoryJoint(clonedAccessory)
if not clonedHandle or not clonedJoint then
clonedAccessory:Destroy()
warn(
"TailAnim: cloned tail has no valid handle or joint"
)
return false
end
local jointClone = nil
local jointCloneSuccess = pcall(function()
originalJoint.Archivable = true
jointClone = originalJoint:Clone()
end)
if not jointCloneSuccess or not jointClone then
clonedAccessory:Destroy()
warn(
"TailAnim: unable to preserve the original joint"
)
return false
end
currentAccessory = accessory
realHandle = handle
fakeAccessory = clonedAccessory
fakeHandle = clonedHandle
fakeJoint = clonedJoint
fakeBaseC0 = clonedJoint.C0
savedJoint = jointClone
savedJointParent = originalJoint.Parent
configureRealHandle(realHandle)
local destroyed = pcall(function()
originalJoint:Destroy()
end)
if not destroyed then
releaseCurrentAccessory(false)
return false
end
animationTime = 0
currentYaw = 0
return true
end
local function findBestTail(character)
if not character or not character.Parent then
return nil
end
local configuredMatch = nil
local namedMatch = nil
local otherMatch = nil
for _, child in ipairs(character:GetChildren()) do
if child:IsA("Accessory")
and child:GetAttribute("__TailAnimFake") ~= true
then
if matchesConfiguredAccessory(child) then
configuredMatch = child
break
end
if isTailAccessory(child) then
if hasTailWord(child.Name) then
namedMatch = namedMatch or child
else
otherMatch = otherMatch or child
end
end
end
end
return configuredMatch or namedMatch or otherMatch
end
local function findAndAttachTail(character)
local accessory = findBestTail(character)
if not accessory then
return false
end
return attachTail(accessory)
end
local function bindCharacter(character)
bindGeneration += 1
local generation = bindGeneration
disconnect(childConnection)
childConnection = nil
TA._childConn = nil
releaseCurrentAccessory(true)
removeOldFakeAccessories(character)
currentCharacter = character
lastSearchTime = 0
-- Give Roblox a moment to add all character accessories.
task.delay(0.35, function()
if generation ~= bindGeneration then
return
end
if character ~= localPlayer.Character then
return
end
findAndAttachTail(character)
end)
childConnection = character.ChildAdded:Connect(function(child)
if not child:IsA("Accessory") then
return
end
if child:GetAttribute("__TailAnimFake") == true then
return
end
task.delay(0.2, function()
if generation ~= bindGeneration then
return
end
if child.Parent ~= character then
return
end
if isTailAccessory(child) then
attachTail(child)
end
end)
end)
TA._childConn = childConnection
end
function TA:SetEnabled(value)
self.Enabled = not not value
end
function TA:SetSpeed(value)
local numericValue = tonumber(value)
if numericValue then
self.Speed = math.max(numericValue, 0)
end
end
function TA:SetAmplitude(value)
local numericValue = tonumber(value)
if numericValue then
self.Amplitude = math.clamp(
numericValue,
0,
180
)
end
end
function TA:SetAccessoryName(name)
if name == nil or tostring(name) == "" then
self.AccessoryName = nil
else
self.AccessoryName = tostring(name)
end
local character = localPlayer.Character
if character then
releaseCurrentAccessory(true)
task.defer(function()
findAndAttachTail(character)
end)
end
end
function TA:GetSelectedAccessory()
return currentAccessory
end
function TA:Stop()
self.Enabled = false
if type(self._cleanup) == "function" then
self._cleanup()
end
end
characterConnection =
localPlayer.CharacterAdded:Connect(function(character)
bindCharacter(character)
end)
TA._charConn = characterConnection
if localPlayer.Character then
bindCharacter(localPlayer.Character)
end
renderConnection =
RunService.RenderStepped:Connect(function(deltaTime)
local character = localPlayer.Character
if not character then
return
end
local tailIsValid =
currentAccessory
and currentAccessory.Parent == character
and realHandle
and realHandle.Parent
and fakeAccessory
and fakeAccessory.Parent == character
and fakeHandle
and fakeHandle.Parent
and fakeJoint
and fakeJoint.Parent
and fakeBaseC0
if not tailIsValid then
local now = os.clock()
if now - lastSearchTime >= 1 then
lastSearchTime = now
findAndAttachTail(character)
end
return
end
local targetYaw = 0
if TA.Enabled then
animationTime +=
deltaTime * (tonumber(TA.Speed) or 10)
targetYaw =
math.sin(animationTime)
* math.rad(
tonumber(TA.Amplitude) or 35
)
end
local smoothing =
1 - math.exp(-deltaTime * 18)
currentYaw +=
(targetYaw - currentYaw) * smoothing
fakeJoint.C0 =
fakeBaseC0
* CFrame.Angles(0, currentYaw, 0)
realHandle.CFrame = fakeHandle.CFrame
pcall(function()
realHandle.AssemblyLinearVelocity =
Vector3.new(0, 0, 30)
realHandle.AssemblyAngularVelocity =
Vector3.new(0, 0, 0)
end)
end)
TA._conn = renderConnection
TA._cleanup = function()
bindGeneration += 1
disconnect(renderConnection)
disconnect(characterConnection)
disconnect(childConnection)
renderConnection = nil
characterConnection = nil
childConnection = nil
TA._conn = nil
TA._charConn = nil
TA._childConn = nil
releaseCurrentAccessory(true)
currentCharacter = nil
end描述
AI is used to make guide for you in the script ---------------------------------------------- **Moony2HP (@tungsten88474)** i found it on scriptblox and i just made it server **XII (@syniyrz)** how did you even make it server 😭 **Moony2HP (@tungsten88474)** skidding, i look at a tool hat hold script or whatever that let you hold ur hats as tools saw that it destroyed the weld and used cframe and i kinda ported that over **XII (@syniyrz)** ouuuu creative **Moony2HP (@tungsten88474)** yeah
評論 (0)
- 成為第一個評論的人。
常見問題
- 如何使用 Tail Wag (server) 腳本?
- 複製上方的腳本代碼,在 Roblox 中進入 Universal,然後將代碼貼到你的執行器中並執行。腳本運行後功能會立即生效。
- Tail Wag (server) 還能用嗎?
- 可以 — Tail Wag (server) 目前標記為可用(最近更新:2026年9月1日)。如果在 Roblox 更新後失效,請稍後回來查看修復版本。
- 哪個執行器相容 Tail Wag (server)?
- 此腳本相容大多數熱門 Roblox 執行器。查看我們的執行器頁面,為你的裝置找到可信的免費或付費執行器。







