local W=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local w=W:WaitForChild("\099\111\109\109\111\110")local Z=require(w:WaitForChild("\065\115\115\101\116\115"))local d=require(w.util:WaitForChild("\103\101\116\065\115\115\101\116\077\111\100\101\108"))local a={}local function p(W)local w=(d(W)):Clone()w.CanCollide=false w.Anchored=true w.Locked=true return w end function a.new(W,w,d)local o=setmetatable({},{__index=a})o.assetId=W o.rig=w o.viewModel=p(W)local c=w:WaitForChild("\084\111\114\115\111")o.viewModel.CFrame=c.CFrame*CFrame.new(0,0,-5)o.viewModel.Parent=workspace o.lastCF=CFrame.new(0,0,0)o.cframe=CFrame.new(0,0,0)o.offset=Vector3.new(0,0,0)o.followDist=o.viewModel.Size.Magnitude/2+3 o.hoverTimeOffset=(math.random()*2)*math.pi o.cycleTime=.3+math.random()*.2 local h=Z.byId[W]o.metadata=h.metadata or{}o.nextSound=tick()+1 return o end function a.update(w,W)local Z=w.rig:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")if not Z then return end if not w.viewModel then return end local d=w.cframe.p-Z.Position local a=d.Unit*w.followDist local p=Z.Position+a local o=CFrame.new(p)*(w.cframe-w.cframe.p)local c=math.sin(((((tick()+w.hoverTimeOffset)*math.pi)*2)*w.cycleTime)%(math.pi*2))*.5 if(p-w.lastCF.p).Magnitude>.3 then o=CFrame.new(p,p+(p-w.lastCF.p))local W=w.viewModel:FindFirstChild("\111\102\102\115\101\116")if W then o=o*W.CFrame:inverse()end end w.offset=Vector3.new(0,c,0)w.cframe=w.cframe:lerp(o,.1)w.lastCF=w.cframe w.viewModel.CFrame=w.cframe+w.offset if w.metadata.soundId then local Z=w.metadata.basePitch or 1 local d=w.metadata.pitchDevitation or.4 local a=w.metadata.pitchDevitationOffset or.2 local p=w.metadata.minWait or 4 local o=w.metadata.maxWait or 10 if tick()>=w.nextSound then w.nextSound=tick()+math.random(p,o);(W:getModule("\083\111\117\110\100")):playSound(w.metadata.soundId,1,Z-(math.random()*d-a),w.viewModel,nil)end end end function a.destroy(W)W.viewModel:Destroy()W.rig=nil end return a
-- object based pets renderer, default renderer for pets.

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local common = ReplicatedStorage:WaitForChild("common")
local Assets = require(common:WaitForChild("Assets"))

local getAssetModel = require(common.util:WaitForChild("getAssetModel"))

local PetRenderer = {}

local function createModel(assetId)
    local newModel = getAssetModel(assetId):Clone()

    newModel.CanCollide = false
    newModel.Anchored = true
    newModel.Locked = true

    return newModel
end

function PetRenderer.new(assetId,rig, client)
    -- create hat for this rig
    local self = setmetatable({},{__index = PetRenderer})

    self.assetId = assetId
    self.rig = rig

    self.viewModel = createModel(assetId)

    local torso = rig:WaitForChild("Torso")

    self.viewModel.CFrame = torso.CFrame * CFrame.new(0,0,-5)

    self.viewModel.Parent = workspace

    self.lastCF = CFrame.new(0,0,0)
    self.cframe = CFrame.new(0,0,0)
    self.offset = Vector3.new(0,0,0)

    self.followDist = (self.viewModel.Size.Magnitude/2) + 3

    self.hoverTimeOffset = math.random()*2*math.pi
    self.cycleTime = 0.3 + math.random()*0.2

    local petAsset = Assets.byId[assetId]
    self.metadata = petAsset.metadata or {}
    self.nextSound = tick()+1

    return self
end

function PetRenderer:update(client)
    local root = self.rig:FindFirstChild("HumanoidRootPart")
    if not root then return end
    if not self.viewModel then return end

    local diffVector = self.cframe.p-root.Position
    local separationVector = diffVector.Unit * self.followDist
    local targetPos = root.Position + separationVector
    local targetCFrame = CFrame.new(targetPos) * (self.cframe - self.cframe.p)
    local heightOffset = math.sin(((tick() + self.hoverTimeOffset)*math.pi*2*self.cycleTime)%(math.pi*2))*0.5

    if (targetPos-self.lastCF.p).Magnitude > 0.3 then -- moving, need to turn
        targetCFrame = CFrame.new(targetPos,targetPos + (targetPos-self.lastCF.p))
        local offsetAttachment = self.viewModel:FindFirstChild("offset")
        if offsetAttachment then
            targetCFrame = targetCFrame * offsetAttachment.CFrame:inverse()
        end
    end


    self.offset = Vector3.new(0,heightOffset,0)
    self.cframe = self.cframe:lerp(targetCFrame,0.1)
    self.lastCF = self.cframe

    self.viewModel.CFrame = self.cframe + self.offset

    if self.metadata.soundId then

        local pitch = self.metadata.basePitch or 1
        local deviation = self.metadata.pitchDevitation or 0.4
        local offset = self.metadata.pitchDevitationOffset or 0.2
        local minWait = self.metadata.minWait or 4
        local maxWait = self.metadata.maxWait or 10

        if tick() >= self.nextSound then
            self.nextSound = tick() + math.random(minWait,maxWait)

            client:getModule("Sound"):playSound(
                self.metadata.soundId,
                1,
                pitch - (math.random()*deviation - offset),
                self.viewModel,
                nil)
        end
    end
end

function PetRenderer:destroy()
    self.viewModel:Destroy()
    self.rig = nil
end

return PetRenderer