--// ๐Ÿฑ SMART CAT COMPANION AI --// FULL LIFE + BEHAVIOR + RAGDOLL SYSTEM --// Model: 18314616360 --// Client-side LocalScript --// --// ๐Ÿšซ NO SITTING --// ๐Ÿšซ NO SLEEPING --// โค๏ธ PETTING --// ๐Ÿ– FOOD --// ๐Ÿ’ง DRINKING --// ๐Ÿ‘ƒ SNIFFING --// ๐Ÿงผ GROOMING --// ๐Ÿพ PAWING --// ๐Ÿง˜ STRETCHING --// ๐Ÿ‘€ LOOKING --// ๐ŸŽพ PLAYING --// ๐Ÿƒ ZOOMIES --// ๐Ÿพ EXPLORATION --// ๐Ÿฆ WATCHING / CHASING --// ๐Ÿ˜บ RANDOM MEOWS --// ๐Ÿงฑ WALL AVOIDANCE --// ๐Ÿง  STUCK RECOVERY --// ๐Ÿธ SMART JUMPING --// ๐ŸŒฑ GROWTH --// โฑ๏ธ 30-MINUTE LIFESPAN --// โ˜ ๏ธ NON-GRAPHIC DEATH CAUSES --// ๐Ÿฆด PHYSICAL RAGDOLL --// --// Put this LocalScript in: --// StarterPlayer > StarterPlayerScripts local Players = game:GetService("Players") local RunService = game:GetService("RunService") local CollectionService = game:GetService("CollectionService") local player = Players.LocalPlayer local random = Random.new() -------------------------------------------------- -- SETTINGS -------------------------------------------------- local MODEL_ID = "rbxassetid://18314616360" -------------------------------------------------- -- ๐ŸŒฑ LIFE -------------------------------------------------- local LIFE_TIME = 30 * 60 local START_SIZE = 0.70 local FINAL_SIZE = 1.15 local birthTime = time() local currentScale = START_SIZE local catIsDead = false local deathCause = "Unknown" -------------------------------------------------- -- ๐Ÿฑ FOLLOWING -------------------------------------------------- local SPAWN_DISTANCE = 6 local STOP_DISTANCE = 5 local FOLLOW_RESUME_DISTANCE = 18 local WALK_SPEED = 8 local RUN_SPEED = 12 local RUN_DISTANCE = 30 local TOO_FAR_DISTANCE = 45 -------------------------------------------------- -- ๐Ÿง  STUCK -------------------------------------------------- local STUCK_CHECK_TIME = 1 local STUCK_DISTANCE = 1.2 local lastStuckPosition local stuckTimer = 0 local RECOVERY_TIME = 1.6 local recovering = false local recoveryUntil = 0 -------------------------------------------------- -- ๐Ÿงฑ WALLS -------------------------------------------------- local OBSTACLE_CHECK_DISTANCE = 5 local SIDE_CHECK_DISTANCE = 5 local HARD_BLOCK_DISTANCE = 3.5 local BLOCKCAST_SIZE = Vector3.new(3.5, 4, 3.5) local turningAroundWall = false local wallTurnDirection local wallTurnUntil = 0 -------------------------------------------------- -- ๐Ÿธ JUMPING -------------------------------------------------- local NORMAL_JUMP_POWER = 45 local RECOVERY_JUMP_POWER = 55 local JUMP_COOLDOWN = 1.25 local JUMP_CONTROL_TIME = 0.28 local MAX_JUMP_TIME = 1.25 local LANDING_BUFFER = 0.12 local jumping = false local jumpStartedAt = 0 local jumpEndTime = 0 local lastJumpTime = 0 -------------------------------------------------- -- ๐Ÿ‘ƒ SNIFFING -------------------------------------------------- local SMELL_ENABLED = true local SMELL_CHANCE = 0.13 local SMELL_MIN_TIME = 2 local SMELL_MAX_TIME = 4 -------------------------------------------------- -- ๐Ÿงผ GROOMING -------------------------------------------------- local GROOM_ENABLED = true local GROOM_CHANCE = 0.10 local GROOM_MIN_TIME = 2 local GROOM_MAX_TIME = 5 -------------------------------------------------- -- ๐Ÿพ PAWING -------------------------------------------------- local PAW_ENABLED = true local PAW_CHANCE = 0.08 local PAW_MIN_TIME = 1 local PAW_MAX_TIME = 2.5 -------------------------------------------------- -- ๐Ÿง˜ STRETCHING -------------------------------------------------- local STRETCH_ENABLED = true local STRETCH_CHANCE = 0.07 local STRETCH_MIN_TIME = 1.5 local STRETCH_MAX_TIME = 3 -------------------------------------------------- -- ๐Ÿ‘€ WATCHING -------------------------------------------------- local WATCH_ENABLED = true local WATCH_CHANCE = 0.08 local WATCH_MIN_TIME = 1.5 local WATCH_MAX_TIME = 4 -------------------------------------------------- -- ๐ŸŽพ PLAY -------------------------------------------------- local PLAY_ENABLED = true local PLAY_CHANCE = 0.08 local PLAY_MIN_TIME = 1.5 local PLAY_MAX_TIME = 3 -------------------------------------------------- -- ๐Ÿƒ ZOOMIES -------------------------------------------------- local RANDOM_RUN_ENABLED = true local RANDOM_RUN_CHANCE = 0.08 local RANDOM_RUN_MIN_TIME = 2 local RANDOM_RUN_MAX_TIME = 5 -------------------------------------------------- -- ๐Ÿพ EXPLORE -------------------------------------------------- local EXPLORE_ENABLED = true local EXPLORE_CHANCE = 0.30 local EXPLORE_MIN_DISTANCE = 8 local EXPLORE_MAX_DISTANCE = 35 local EXPLORE_WAIT_MIN = 2 local EXPLORE_WAIT_MAX = 7 local EXPLORE_TIMEOUT = 12 -------------------------------------------------- -- ๐Ÿ– FOOD -------------------------------------------------- local FOOD_ENABLED = true local FOOD_SEARCH_CHANCE = 0.16 local FOOD_EAT_TIME_MIN = 3 local FOOD_EAT_TIME_MAX = 6 local FOOD_SEARCH_RADIUS = 45 local FOOD_DETECT_DISTANCE = 3 -------------------------------------------------- -- ๐Ÿ’ง WATER -------------------------------------------------- local WATER_ENABLED = true local WATER_CHANCE = 0.07 local WATER_DRINK_TIME_MIN = 2 local WATER_DRINK_TIME_MAX = 5 local WATER_SEARCH_RADIUS = 40 -------------------------------------------------- -- ๐Ÿฆ CHASING -------------------------------------------------- local CHASE_ENABLED = true local CHASE_CHANCE = 0.05 local CHASE_TIME_MIN = 2 local CHASE_TIME_MAX = 5 local CHASE_RADIUS = 25 -------------------------------------------------- -- โค๏ธ PET -------------------------------------------------- local PET_ENABLED = true local PET_TIME = 2.5 local PET_MAX_DISTANCE = 8 local petting = false local petUntil = 0 -------------------------------------------------- -- ๐Ÿ”Š SOUND -------------------------------------------------- local RANDOM_SOUND_ID = "rbxassetid://133371725828981" local MIN_SOUND_DELAY = 5 local MAX_SOUND_DELAY = 15 local SOUND_VOLUME = 1 -------------------------------------------------- -- ๐ŸŽฌ WALK -------------------------------------------------- local R6_WALK = "rbxassetid://180426354" local R15_WALK = "rbxassetid://913402848" -------------------------------------------------- -- โ˜ ๏ธ DEATH -------------------------------------------------- local RAGDOLL_ON_DEATH = true local HIGH_SPEED_IMPACT = 70 local HIGH_FALL_DISTANCE = 35 local deathProcessed = false -------------------------------------------------- -- CHARACTER -------------------------------------------------- local character = player.Character or player.CharacterAdded:Wait() local playerRoot = character:WaitForChild("HumanoidRootPart") -------------------------------------------------- -- LOAD CAT -------------------------------------------------- local objects = game:GetObjects(MODEL_ID) if not objects or not objects[1] then warn("๐Ÿฑ Could not load cat model.") return end local rig = objects[1] rig.Parent = workspace -------------------------------------------------- -- HUMANOID -------------------------------------------------- local humanoid = rig:FindFirstChildOfClass("Humanoid") if not humanoid then warn("๐Ÿฑ Cat has no Humanoid.") return end local rigRoot = rig:FindFirstChild("HumanoidRootPart") if not rigRoot then warn("๐Ÿฑ Cat has no HumanoidRootPart.") return end -------------------------------------------------- -- ๐Ÿ›ก๏ธ HUMANOID SAFETY -------------------------------------------------- humanoid.BreakJointsOnDeath = false humanoid.RequiresNeck = false -------------------------------------------------- -- ๐Ÿงฑ COLLISION -------------------------------------------------- for _, object in ipairs(rig:GetDescendants()) do if object:IsA("BasePart") then object.Anchored = false object.CanCollide = true object.CanTouch = true object.CanQuery = true object.Massless = false pcall(function() object.CollisionGroup = "Default" end) end end rigRoot.CanCollide = true rigRoot.CanTouch = true rigRoot.CanQuery = true -------------------------------------------------- -- SPAWN -------------------------------------------------- rig:PivotTo( playerRoot.CFrame * CFrame.new(0, 0, -SPAWN_DISTANCE) ) -------------------------------------------------- -- SIZE -------------------------------------------------- pcall(function() rig:ScaleTo(START_SIZE) end) currentScale = START_SIZE -------------------------------------------------- -- HUMANOID SETTINGS -------------------------------------------------- humanoid.WalkSpeed = WALK_SPEED humanoid.AutoRotate = true humanoid.UseJumpPower = true humanoid.JumpPower = NORMAL_JUMP_POWER humanoid.Sit = false pcall(function() humanoid:SetStateEnabled( Enum.HumanoidStateType.Seated, false ) end) -------------------------------------------------- -- ANIMATOR -------------------------------------------------- local animator = humanoid:FindFirstChildOfClass("Animator") if not animator then animator = Instance.new("Animator") animator.Parent = humanoid end -------------------------------------------------- -- WALK ANIMATION -------------------------------------------------- local animation = Instance.new("Animation") if humanoid.RigType == Enum.HumanoidRigType.R15 then animation.AnimationId = R15_WALK else animation.AnimationId = R6_WALK end local walkTrack pcall(function() walkTrack = animator:LoadAnimation(animation) end) if walkTrack then walkTrack.Looped = true walkTrack.Priority = Enum.AnimationPriority.Movement end -------------------------------------------------- -- ๐Ÿ”Š SOUND -------------------------------------------------- local sound = Instance.new("Sound") sound.Name = "RandomCatSound" sound.SoundId = RANDOM_SOUND_ID sound.Volume = SOUND_VOLUME sound.RollOffMaxDistance = 60 sound.RollOffMinDistance = 5 sound.Parent = rigRoot -------------------------------------------------- -- โค๏ธ PET PROMPT -------------------------------------------------- local petPrompt = Instance.new("ProximityPrompt") petPrompt.Name = "PetCatPrompt" petPrompt.ActionText = "Pet Cat" petPrompt.ObjectText = "๐Ÿฑ Cat" petPrompt.KeyboardKeyCode = Enum.KeyCode.E petPrompt.HoldDuration = 0.5 petPrompt.MaxActivationDistance = PET_MAX_DISTANCE petPrompt.RequiresLineOfSight = false petPrompt.Style = Enum.ProximityPromptStyle.Default petPrompt.Parent = rigRoot -------------------------------------------------- -- STATE -------------------------------------------------- local state = "FOLLOWING" local targetPosition local stateUntil = 0 local randomRunDirection local randomRunUntil = 0 local watchPosition local behaviorTarget -------------------------------------------------- -- RAYCAST -------------------------------------------------- local rayParams = RaycastParams.new() rayParams.FilterType = Enum.RaycastFilterType.Exclude rayParams.FilterDescendantsInstances = { rig, character } rayParams.IgnoreWater = true -------------------------------------------------- -- INITIAL -------------------------------------------------- lastStuckPosition = rigRoot.Position -------------------------------------------------- -- ๐ŸŽฌ JOINTS -------------------------------------------------- local joints = {} for _, object in ipairs(rig:GetDescendants()) do if object:IsA("Motor6D") then joints[object.Name] = object end end local function findJoint(...) for _, name in ipairs({...}) do if joints[name] then return joints[name] end end for _, object in ipairs(rig:GetDescendants()) do if object:IsA("Motor6D") then local lower = object.Name:lower() for _, wanted in ipairs({...}) do if string.find( lower, wanted:lower(), 1, true ) then return object end end end end return nil end local leftShoulder = findJoint( "Left Shoulder", "LeftShoulder" ) local rightShoulder = findJoint( "Right Shoulder", "RightShoulder" ) local leftHip = findJoint( "Left Hip", "LeftHip" ) local rightHip = findJoint( "Right Hip", "RightHip" ) local neck = findJoint("Neck") -------------------------------------------------- -- SAVE ORIGINAL JOINTS -------------------------------------------------- local originalTransforms = {} for _, object in ipairs(rig:GetDescendants()) do if object:IsA("Motor6D") then originalTransforms[object] = object.Transform end end -------------------------------------------------- -- ANIMATION -------------------------------------------------- local function animateJoint( joint, cframe, weight ) if not joint then return end local original = originalTransforms[joint] if not original then original = CFrame.identity end joint.Transform = original:Lerp( original * cframe, weight or 1 ) end local function updateLimbAnimation() if catIsDead then return end local now = time() -------------------------------------------------- -- IDLE -------------------------------------------------- if state == "IDLE" then local breathe = math.sin(now * 2) * math.rad(2) animateJoint( leftShoulder, CFrame.Angles(breathe, 0, 0), 0.8 ) animateJoint( rightShoulder, CFrame.Angles(-breathe, 0, 0), 0.8 ) return end -------------------------------------------------- -- SNIFFING -------------------------------------------------- if state == "SMELLING" or state == "SEARCHING_FOOD" or state == "SEARCHING_WATER" then local sniff = math.sin(now * 10) * math.rad(5) local head = math.sin(now * 7) * math.rad(7) animateJoint( neck, CFrame.Angles( math.rad(-18) + head, 0, 0 ), 0.9 ) animateJoint( leftShoulder, CFrame.Angles( sniff, 0, math.rad(-4) ), 0.8 ) animateJoint( rightShoulder, CFrame.Angles( -sniff, 0, math.rad(4) ), 0.8 ) return end -------------------------------------------------- -- EATING -------------------------------------------------- if state == "EATING" then local chew = math.sin(now * 8) * math.rad(4) local head = math.sin(now * 5) * math.rad(10) animateJoint( neck, CFrame.Angles( math.rad(20) + head, 0, 0 ), 1 ) animateJoint( leftShoulder, CFrame.Angles( chew, 0, 0 ), 0.6 ) animateJoint( rightShoulder, CFrame.Angles( -chew, 0, 0 ), 0.6 ) return end -------------------------------------------------- -- DRINKING -------------------------------------------------- if state == "DRINKING" then local drink = math.sin(now * 5) * math.rad(5) animateJoint( neck, CFrame.Angles( math.rad(30) + drink, 0, 0 ), 1 ) return end -------------------------------------------------- -- GROOMING -------------------------------------------------- if state == "GROOMING" then local paw = math.sin(now * 9) * math.rad(25) animateJoint( leftShoulder, CFrame.Angles( math.rad(-35) + paw, math.rad(5), math.rad(-10) ), 0.9 ) animateJoint( neck, CFrame.Angles( math.rad(12), math.sin(now * 4) * math.rad(8), 0 ), 0.8 ) return end -------------------------------------------------- -- PAWING -------------------------------------------------- if state == "PAWING" then local paw = math.sin(now * 12) * math.rad(35) animateJoint( leftShoulder, CFrame.Angles( math.rad(-25) + paw, 0, math.rad(-8) ), 1 ) animateJoint( rightShoulder, CFrame.Angles( math.rad(-25) - paw, 0, math.rad(8) ), 1 ) return end -------------------------------------------------- -- STRETCHING -------------------------------------------------- if state == "STRETCHING" then local stretch = math.sin( math.clamp( (now % 2) * 2, 0, 1 ) * math.pi ) animateJoint( leftShoulder, CFrame.Angles( math.rad(-25) * stretch, 0, 0 ), 1 ) animateJoint( rightShoulder, CFrame.Angles( math.rad(-25) * stretch, 0, 0 ), 1 ) animateJoint( leftHip, CFrame.Angles( math.rad(18) * stretch, 0, 0 ), 1 ) animateJoint( rightHip, CFrame.Angles( math.rad(18) * stretch, 0, 0 ), 1 ) return end -------------------------------------------------- -- WATCHING -------------------------------------------------- if state == "WATCHING" or state == "CHASING" then local head = math.sin(now * 2) * math.rad(4) animateJoint( neck, CFrame.Angles( math.rad(-3), head, 0 ), 0.8 ) return end -------------------------------------------------- -- PLAYING -------------------------------------------------- if state == "PLAYING" then local bounce = math.sin(now * 9) * math.rad(18) animateJoint( leftShoulder, CFrame.Angles( bounce, 0, math.rad(-10) ), 0.9 ) animateJoint( rightShoulder, CFrame.Angles( -bounce, 0, math.rad(10) ), 0.9 ) return end -------------------------------------------------- -- RUNNING -------------------------------------------------- if state == "RUNNING" or state == "RUNNING_RANDOM" then local run = math.sin(now * 13) * math.rad(35) animateJoint( leftShoulder, CFrame.Angles(run, 0, 0), 0.8 ) animateJoint( rightShoulder, CFrame.Angles(-run, 0, 0), 0.8 ) animateJoint( leftHip, CFrame.Angles(-run, 0, 0), 0.8 ) animateJoint( rightHip, CFrame.Angles(run, 0, 0), 0.8 ) return end -------------------------------------------------- -- WALKING -------------------------------------------------- if state == "FOLLOWING" or state == "WANDERING" then local walk = math.sin(now * 10) * math.rad(15) animateJoint( leftShoulder, CFrame.Angles(walk, 0, 0), 0.65 ) animateJoint( rightShoulder, CFrame.Angles(-walk, 0, 0), 0.65 ) animateJoint( leftHip, CFrame.Angles(-walk, 0, 0), 0.65 ) animateJoint( rightHip, CFrame.Angles(walk, 0, 0), 0.65 ) return end -------------------------------------------------- -- PETTING -------------------------------------------------- if state == "PETTING" then local happy = math.sin(now * 5) * math.rad(6) animateJoint( leftShoulder, CFrame.Angles( happy, 0, math.rad(-8) ), 0.8 ) animateJoint( rightShoulder, CFrame.Angles( -happy, 0, math.rad(8) ), 0.8 ) return end end -------------------------------------------------- -- WALK HELPERS -------------------------------------------------- local function stopWalk() if walkTrack and walkTrack.IsPlaying then walkTrack:Stop(0.15) end end local function playWalk() if walkTrack and not walkTrack.IsPlaying then walkTrack:Play(0.15) end end -------------------------------------------------- -- FORCE STAND -------------------------------------------------- local function forceStand() if humanoid.Sit then humanoid.Sit = false end if humanoid:GetState() == Enum.HumanoidStateType.Seated then humanoid:ChangeState( Enum.HumanoidStateType.GettingUp ) end end -------------------------------------------------- -- GROUNDED -------------------------------------------------- local function isGrounded() if humanoid.FloorMaterial ~= Enum.Material.Air then return true end local result = workspace:Raycast( rigRoot.Position + Vector3.new(0, 0.5, 0), Vector3.new(0, -4, 0), rayParams ) return result ~= nil end -------------------------------------------------- -- RAY -------------------------------------------------- local function rayHit(direction, distance) if direction.Magnitude < 0.05 then return nil end local origin = rigRoot.Position + Vector3.new(0, 1.5, 0) return workspace:Raycast( origin, direction.Unit * distance, rayParams ) end -------------------------------------------------- -- BLOCKCAST -------------------------------------------------- local function getHardObstacle(direction) if direction.Magnitude < 0.05 then return nil end local flat = Vector3.new( direction.X, 0, direction.Z ) if flat.Magnitude < 0.05 then return nil end flat = flat.Unit local origin = rigRoot.Position + Vector3.new(0, 1.5, 0) return workspace:Blockcast( CFrame.lookAt( origin, origin + flat ), BLOCKCAST_SIZE, flat * HARD_BLOCK_DISTANCE, rayParams ) end -------------------------------------------------- -- WALL AVOIDANCE -------------------------------------------------- local function getWallAvoidance(direction) if direction.Magnitude < 0.05 then return direction end local forward = Vector3.new( direction.X, 0, direction.Z ) if forward.Magnitude < 0.05 then return direction end forward = forward.Unit local right = Vector3.new( -forward.Z, 0, forward.X ) local left = -right local forwardHit = rayHit( forward, OBSTACLE_CHECK_DISTANCE ) if not forwardHit then if turningAroundWall and wallTurnDirection and time() < wallTurnUntil then if not rayHit( wallTurnDirection, SIDE_CHECK_DISTANCE ) then return ( forward * 0.35 + wallTurnDirection * 0.65 ).Unit end end turningAroundWall = false wallTurnDirection = nil return forward end local leftHit = rayHit( left, SIDE_CHECK_DISTANCE ) local rightHit = rayHit( right, SIDE_CHECK_DISTANCE ) if not leftHit and not rightHit then wallTurnDirection = random:NextNumber() < 0.5 and left or right turningAroundWall = true wallTurnUntil = time() + 1.5 return ( forward * 0.2 + wallTurnDirection * 0.8 ).Unit end if not leftHit then wallTurnDirection = left turningAroundWall = true wallTurnUntil = time() + 1.5 return ( forward * 0.2 + left * 0.8 ).Unit end if not rightHit then wallTurnDirection = right turningAroundWall = true wallTurnUntil = time() + 1.5 return ( forward * 0.2 + right * 0.8 ).Unit end local side = random:NextNumber() < 0.5 and left or right return side.Unit end -------------------------------------------------- -- ๐Ÿธ JUMP -------------------------------------------------- local function tryJump(force) if catIsDead or petting or jumping or humanoid.Health <= 0 then return false end if time() - lastJumpTime < JUMP_COOLDOWN then return false end if not force and not isGrounded() then return false end forceStand() lastJumpTime = time() jumping = true jumpStartedAt = time() jumpEndTime = time() + MAX_JUMP_TIME stopWalk() humanoid.JumpPower = RECOVERY_JUMP_POWER humanoid.Jump = true humanoid:ChangeState( Enum.HumanoidStateType.Jumping ) task.delay( JUMP_CONTROL_TIME, function() if rig.Parent and not catIsDead and humanoid.Health > 0 then humanoid.JumpPower = NORMAL_JUMP_POWER end end ) return true end -------------------------------------------------- -- UPDATE JUMP -------------------------------------------------- local function updateJump() if not jumping then return false end local now = time() if isGrounded() and now - jumpStartedAt > LANDING_BUFFER then jumping = false humanoid.JumpPower = NORMAL_JUMP_POWER return false end if now >= jumpEndTime then jumping = false humanoid.JumpPower = NORMAL_JUMP_POWER return false end humanoid:Move( Vector3.zero, false ) return true end -------------------------------------------------- -- MOVE -------------------------------------------------- local function moveDirection(direction, speed) if catIsDead or jumping or petting then return end if direction.Magnitude < 0.05 then return end forceStand() local flat = Vector3.new( direction.X, 0, direction.Z ) if flat.Magnitude < 0.05 then return end flat = flat.Unit local hardHit = getHardObstacle(flat) if hardHit then local right = Vector3.new( -flat.Z, 0, flat.X ) local left = -right local leftBlocked = getHardObstacle(left) ~= nil local rightBlocked = getHardObstacle(right) ~= nil if leftBlocked and rightBlocked then if isGrounded() then if tryJump(false) then return end end humanoid:Move( Vector3.zero, false ) stopWalk() return end if not leftBlocked then flat = ( flat * 0.15 + left * 0.85 ).Unit elseif not rightBlocked then flat = ( flat * 0.15 + right * 0.85 ).Unit end end local movement = getWallAvoidance(flat) if movement.Magnitude < 0.05 then humanoid:Move( Vector3.zero, false ) stopWalk() return end humanoid.WalkSpeed = speed humanoid:Move( movement, false ) playWalk() end -------------------------------------------------- -- STOP -------------------------------------------------- local function stopMoving() if jumping then return end humanoid:Move( Vector3.zero, false ) stopWalk() forceStand() stuckTimer = 0 lastStuckPosition = rigRoot.Position end -------------------------------------------------- -- LOOK -------------------------------------------------- local function lookAt(position) if jumping or petting then return end local current = rigRoot.Position local target = Vector3.new( position.X, current.Y, position.Z ) local difference = target - current if difference.Magnitude < 0.05 then return end rigRoot.CFrame = CFrame.lookAt( current, target ) end -------------------------------------------------- -- PET -------------------------------------------------- local function startPetting() if not PET_ENABLED or catIsDead or petting or humanoid.Health <= 0 then return end petting = true petUntil = time() + PET_TIME state = "PETTING" targetPosition = nil recovering = false jumping = false humanoid:Move( Vector3.zero, false ) humanoid.WalkSpeed = 0 humanoid.AutoRotate = false stopWalk() forceStand() end petPrompt.Triggered:Connect( function(triggeringPlayer) if triggeringPlayer ~= player then return end startPetting() end ) -------------------------------------------------- -- PET UPDATE -------------------------------------------------- local function updatePetting() if not petting then return end forceStand() humanoid:Move( Vector3.zero, false ) humanoid.WalkSpeed = 0 if time() >= petUntil then petting = false state = "IDLE" humanoid.AutoRotate = true humanoid.WalkSpeed = WALK_SPEED stateUntil = time() + 3 end end -------------------------------------------------- -- GENERIC IDLE -------------------------------------------------- local function startIdle() if jumping or petting then return end state = "IDLE" stopMoving() humanoid.WalkSpeed = 0 stateUntil = time() + random:NextNumber(2, 5) end -------------------------------------------------- -- SNIFF -------------------------------------------------- local function startSmelling() if jumping or petting then return end state = "SMELLING" stopMoving() humanoid.WalkSpeed = 0 stateUntil = time() + random:NextNumber( SMELL_MIN_TIME, SMELL_MAX_TIME ) end local function updateSmelling() forceStand() humanoid:Move( Vector3.zero, false ) if time() >= stateUntil then startIdle() end end -------------------------------------------------- -- GROOM -------------------------------------------------- local function startGrooming() if jumping or petting then return end state = "GROOMING" stopMoving() humanoid.WalkSpeed = 0 stateUntil = time() + random:NextNumber( GROOM_MIN_TIME, GROOM_MAX_TIME ) end local function updateGrooming() forceStand() humanoid:Move( Vector3.zero, false ) if time() >= stateUntil then startIdle() end end -------------------------------------------------- -- PAW -------------------------------------------------- local function startPawing() if jumping or petting then return end state = "PAWING" stopMoving() humanoid.WalkSpeed = 0 stateUntil = time() + random:NextNumber( PAW_MIN_TIME, PAW_MAX_TIME ) end local function updatePawing() forceStand() humanoid:Move( Vector3.zero, false ) if time() >= stateUntil then startIdle() end end -------------------------------------------------- -- STRETCH -------------------------------------------------- local function startStretching() if jumping or petting then return end state = "STRETCHING" stopMoving() humanoid.WalkSpeed = 0 stateUntil = time() + random:NextNumber( STRETCH_MIN_TIME, STRETCH_MAX_TIME ) end local function updateStretching() forceStand() humanoid:Move( Vector3.zero, false ) if time() >= stateUntil then startIdle() end end -------------------------------------------------- -- PLAY -------------------------------------------------- local function startPlay() if jumping or petting then return end state = "PLAYING" local direction = Vector3.new( random:NextNumber(-1, 1), 0, random:NextNumber(-1, 1) ) if direction.Magnitude < 0.05 then direction = rigRoot.CFrame.LookVector end randomRunDirection = Vector3.new( direction.X, 0, direction.Z ).Unit stateUntil = time() + random:NextNumber( PLAY_MIN_TIME, PLAY_MAX_TIME ) end local function updatePlaying() if time() >= stateUntil then startIdle() return end moveDirection( randomRunDirection, RUN_SPEED ) end -------------------------------------------------- -- RANDOM RUN -------------------------------------------------- local function startRandomRun() if jumping or petting then return end state = "RUNNING_RANDOM" local direction = Vector3.new( random:NextNumber(-1, 1), 0, random:NextNumber(-1, 1) ) if direction.Magnitude < 0.05 then direction = rigRoot.CFrame.LookVector end randomRunDirection = Vector3.new( direction.X, 0, direction.Z ).Unit randomRunUntil = time() + random:NextNumber( RANDOM_RUN_MIN_TIME, RANDOM_RUN_MAX_TIME ) end local function updateRandomRun( playerPosition ) if time() >= randomRunUntil then startIdle() return end local distance = ( rigRoot.Position - playerPosition ).Magnitude if distance > 20 then state = "FOLLOWING" return end moveDirection( randomRunDirection, RUN_SPEED ) end -------------------------------------------------- -- EXPLORE POSITION -------------------------------------------------- local function getRandomExplorePosition() for _ = 1, 15 do local angle = random:NextNumber( 0, math.pi * 2 ) local distance = random:NextNumber( EXPLORE_MIN_DISTANCE, EXPLORE_MAX_DISTANCE ) local offset = Vector3.new( math.cos(angle) * distance, 0, math.sin(angle) * distance ) local desired = playerRoot.Position + offset local ground = workspace:Raycast( desired + Vector3.new(0, 25, 0), Vector3.new(0, -50, 0), rayParams ) if ground then return ground.Position + Vector3.new(0, 2, 0) end end return playerRoot.Position + Vector3.new( random:NextNumber(-10, 10), 0, random:NextNumber(-10, 10) ) end -------------------------------------------------- -- EXPLORE -------------------------------------------------- local function startExplore() if petting then return end state = "WANDERING" targetPosition = getRandomExplorePosition() stateUntil = time() + EXPLORE_TIMEOUT humanoid.WalkSpeed = WALK_SPEED end local function updateExplore( playerPosition ) local distance = ( rigRoot.Position - playerPosition ).Magnitude if distance > FOLLOW_RESUME_DISTANCE then state = "FOLLOWING" targetPosition = nil return end if not targetPosition then targetPosition = getRandomExplorePosition() end local difference = targetPosition - rigRoot.Position difference = Vector3.new( difference.X, 0, difference.Z ) if difference.Magnitude < 2 or time() >= stateUntil then stopMoving() targetPosition = nil state = "IDLE" stateUntil = time() + random:NextNumber( EXPLORE_WAIT_MIN, EXPLORE_WAIT_MAX ) return end moveDirection( difference, WALK_SPEED * 0.65 ) end -------------------------------------------------- -- WATCH -------------------------------------------------- local function startWatching() if jumping or petting then return end state = "WATCHING" stopMoving() humanoid.WalkSpeed = 0 watchPosition = rigRoot.Position + Vector3.new( random:NextNumber(-15, 15), 0, random:NextNumber(-15, 15) ) stateUntil = time() + random:NextNumber( WATCH_MIN_TIME, WATCH_MAX_TIME ) end local function updateWatching() forceStand() humanoid:Move( Vector3.zero, false ) if watchPosition then lookAt(watchPosition) end if time() >= stateUntil then startIdle() end end -------------------------------------------------- -- ๐Ÿ– FIND FOOD -------------------------------------------------- local function findFood() local closest local closestDistance = FOOD_SEARCH_RADIUS for _, object in ipairs( workspace:GetDescendants() ) do if object:IsA("BasePart") then local name = object.Name:lower() if string.find(name, "food", 1, true) or string.find(name, "fish", 1, true) or string.find(name, "meat", 1, true) or string.find(name, "catfood", 1, true) then local distance = ( object.Position - rigRoot.Position ).Magnitude if distance < closestDistance then closest = object closestDistance = distance end end end end return closest end -------------------------------------------------- -- ๐Ÿ– START EATING -------------------------------------------------- local function startFoodSearch() if not FOOD_ENABLED or jumping or petting then return false end local food = findFood() if not food then return false end behaviorTarget = food state = "SEARCHING_FOOD" stateUntil = time() + 10 return true end local function updateFoodSearch() if not behaviorTarget or not behaviorTarget.Parent then startIdle() return end local difference = behaviorTarget.Position - rigRoot.Position local distance = difference.Magnitude if distance <= FOOD_DETECT_DISTANCE then stopMoving() state = "EATING" stateUntil = time() + random:NextNumber( FOOD_EAT_TIME_MIN, FOOD_EAT_TIME_MAX ) return end if time() >= stateUntil then startIdle() return end moveDirection( difference, WALK_SPEED ) end local function updateEating() if behaviorTarget and behaviorTarget.Parent then lookAt( behaviorTarget.Position ) end humanoid:Move( Vector3.zero, false ) if time() >= stateUntil then behaviorTarget = nil startIdle() end end -------------------------------------------------- -- ๐Ÿ’ง FIND WATER -------------------------------------------------- local function findWater() local closest local closestDistance = WATER_SEARCH_RADIUS for _, object in ipairs( workspace:GetDescendants() ) do if object:IsA("BasePart") then local name = object.Name:lower() if string.find(name, "water", 1, true) or string.find(name, "bowl", 1, true) or string.find(name, "drink", 1, true) then local distance = ( object.Position - rigRoot.Position ).Magnitude if distance < closestDistance then closest = object closestDistance = distance end end end end return closest end -------------------------------------------------- -- ๐Ÿ’ง START DRINKING -------------------------------------------------- local function startWaterSearch() if not WATER_ENABLED or jumping or petting then return false end local water = findWater() if not water then return false end behaviorTarget = water state = "SEARCHING_WATER" stateUntil = time() + 10 return true end local function updateWaterSearch() if not behaviorTarget or not behaviorTarget.Parent then startIdle() return end local difference = behaviorTarget.Position - rigRoot.Position local distance = difference.Magnitude if distance <= 3 then stopMoving() state = "DRINKING" stateUntil = time() + random:NextNumber( WATER_DRINK_TIME_MIN, WATER_DRINK_TIME_MAX ) return end if time() >= stateUntil then startIdle() return end moveDirection( difference, WALK_SPEED * 0.75 ) end local function updateDrinking() if behaviorTarget and behaviorTarget.Parent then lookAt( behaviorTarget.Position ) end humanoid:Move( Vector3.zero, false ) if time() >= stateUntil then behaviorTarget = nil startIdle() end end -------------------------------------------------- -- ๐Ÿฆ CHASE -------------------------------------------------- local function findMovingObject() local closest local closestDistance = CHASE_RADIUS for _, object in ipairs( workspace:GetDescendants() ) do if object:IsA("BasePart") and object ~= rigRoot then local velocity = object.AssemblyLinearVelocity if velocity.Magnitude > 5 then local distance = ( object.Position - rigRoot.Position ).Magnitude if distance < closestDistance then closest = object closestDistance = distance end end end end return closest end local function startChasing() if not CHASE_ENABLED or jumping or petting then return false end local target = findMovingObject() if not target then return false end behaviorTarget = target state = "CHASING" stateUntil = time() + random:NextNumber( CHASE_TIME_MIN, CHASE_TIME_MAX ) return true end local function updateChasing() if not behaviorTarget or not behaviorTarget.Parent then startIdle() return end if time() >= stateUntil then behaviorTarget = nil startIdle() return end local difference = behaviorTarget.Position - rigRoot.Position if difference.Magnitude < 2 then startIdle() return end moveDirection( difference, RUN_SPEED ) end -------------------------------------------------- -- ๐Ÿง  STUCK -------------------------------------------------- local function updateStuck(moving, dt) if jumping or petting then stuckTimer = 0 lastStuckPosition = rigRoot.Position return end if not moving then stuckTimer = 0 lastStuckPosition = rigRoot.Position return end local movement = ( rigRoot.Position - lastStuckPosition ).Magnitude if movement < STUCK_DISTANCE then stuckTimer += dt else stuckTimer = 0 lastStuckPosition = rigRoot.Position end if stuckTimer >= STUCK_CHECK_TIME then stuckTimer = 0 recovering = true recoveryUntil = time() + RECOVERY_TIME if isGrounded() then tryJump(true) end end end -------------------------------------------------- -- โ˜ ๏ธ DEATH CAUSE DETECTION -------------------------------------------------- local highestFallY = rigRoot.Position.Y local wasAirborne = false local function updateDeathCause() if catIsDead then return end local velocity = rigRoot.AssemblyLinearVelocity local speed = velocity.Magnitude local grounded = isGrounded() if not grounded then if not wasAirborne then wasAirborne = true highestFallY = rigRoot.Position.Y else highestFallY = math.max( highestFallY, rigRoot.Position.Y ) end else if wasAirborne then local fallDistance = highestFallY - rigRoot.Position.Y if fallDistance >= HIGH_FALL_DISTANCE then deathCause = "Fall from height" end end wasAirborne = false highestFallY = rigRoot.Position.Y end if speed >= HIGH_SPEED_IMPACT then deathCause = "High-speed impact" end end -------------------------------------------------- -- โ˜ ๏ธ RAGDOLL -------------------------------------------------- local function ragdollCat() if not RAGDOLL_ON_DEATH or deathProcessed then return end deathProcessed = true if not rig.Parent then return end humanoid.AutoRotate = false humanoid.WalkSpeed = 0 humanoid.JumpPower = 0 humanoid.PlatformStand = true pcall(function() humanoid:ChangeState( Enum.HumanoidStateType.Physics ) end) -------------------------------------------------- -- Create physical constraints -------------------------------------------------- for _, joint in ipairs( rig:GetDescendants() ) do if joint:IsA("Motor6D") and joint.Part0 and joint.Part1 then local part0 = joint.Part0 local part1 = joint.Part1 local attachment0 = Instance.new("Attachment") attachment0.Name = "CatRagdollA0" attachment0.CFrame = joint.C0 attachment0.Parent = part0 local attachment1 = Instance.new("Attachment") attachment1.Name = "CatRagdollA1" attachment1.CFrame = joint.C1 attachment1.Parent = part1 local constraint = Instance.new( "BallSocketConstraint" ) constraint.Name = "CatRagdollConstraint" constraint.Attachment0 = attachment0 constraint.Attachment1 = attachment1 constraint.LimitsEnabled = true constraint.UpperAngle = 45 constraint.TwistLimitsEnabled = true constraint.TwistLowerAngle = -35 constraint.TwistUpperAngle = 35 constraint.Parent = part0 joint.Enabled = false end end -------------------------------------------------- -- Physical parts -------------------------------------------------- for _, object in ipairs( rig:GetDescendants() ) do if object:IsA("BasePart") then object.Anchored = false object.CanCollide = true object.CanTouch = true object.CanQuery = true object.Massless = false pcall(function() object.CollisionGroup = "Default" end) end end print( "โ˜ ๏ธ Cat ragdolled. Cause: " .. tostring(deathCause) ) end -------------------------------------------------- -- ๐ŸŒฑ AGE -------------------------------------------------- local function updateAge() if catIsDead then return end local age = time() - birthTime if age >= LIFE_TIME then deathCause = "Old age" catIsDead = true state = "DEAD" targetPosition = nil behaviorTarget = nil jumping = false petting = false recovering = false humanoid:Move( Vector3.zero, false ) humanoid.WalkSpeed = 0 humanoid.AutoRotate = false stopWalk() if petPrompt then petPrompt.Enabled = false end if sound then sound:Stop() end -------------------------------------------------- -- Gentle non-graphic death transition -------------------------------------------------- local started = time() local duration = 2.5 task.spawn(function() while rig.Parent and time() - started < duration do local progress = math.clamp( (time() - started) / duration, 0, 1 ) local smooth = progress * progress * (3 - 2 * progress) animateJoint( leftShoulder, CFrame.Angles( math.rad(35) * smooth, 0, math.rad(-8) * smooth ), 1 ) animateJoint( rightShoulder, CFrame.Angles( math.rad(35) * smooth, 0, math.rad(8) * smooth ), 1 ) animateJoint( neck, CFrame.Angles( math.rad(15) * smooth, 0, 0 ), 1 ) RunService.Heartbeat:Wait() end if rig.Parent then ragdollCat() end end) return end -------------------------------------------------- -- GROWTH -------------------------------------------------- local progress = math.clamp( age / LIFE_TIME, 0, 1 ) local smooth = progress * progress * (3 - 2 * progress) local newScale = START_SIZE + (FINAL_SIZE - START_SIZE) * smooth if math.abs( newScale - currentScale ) > 0.001 then currentScale = newScale pcall(function() rig:ScaleTo(currentScale) end) end end -------------------------------------------------- -- ๐Ÿ”Š RANDOM SOUNDS -------------------------------------------------- task.spawn(function() while rig.Parent and not catIsDead do task.wait( random:NextNumber( MIN_SOUND_DELAY, MAX_SOUND_DELAY ) ) if rig.Parent and not catIsDead and state ~= "DEAD" then sound:Stop() sound:Play() end end end) -------------------------------------------------- -- ๐Ÿง  CHOOSE BEHAVIOR -------------------------------------------------- local function chooseIdleBehavior() if petting or catIsDead then return end -------------------------------------------------- -- Food gets special priority if nearby -------------------------------------------------- if FOOD_ENABLED and random:NextNumber() < FOOD_SEARCH_CHANCE then if startFoodSearch() then return end end -------------------------------------------------- -- Water -------------------------------------------------- if WATER_ENABLED and random:NextNumber() < WATER_CHANCE then if startWaterSearch() then return end end -------------------------------------------------- -- Chase -------------------------------------------------- if CHASE_ENABLED and random:NextNumber() < CHASE_CHANCE then if startChasing() then return end end -------------------------------------------------- -- Other behaviors -------------------------------------------------- local roll = random:NextNumber() if roll < 0.13 and SMELL_ENABLED then startSmelling() elseif roll < 0.23 and GROOM_ENABLED then startGrooming() elseif roll < 0.31 and PAW_ENABLED then startPawing() elseif roll < 0.38 and STRETCH_ENABLED then startStretching() elseif roll < 0.46 and WATCH_ENABLED then startWatching() elseif roll < 0.54 and PLAY_ENABLED then startPlay() elseif roll < 0.62 and RANDOM_RUN_ENABLED then startRandomRun() elseif roll < 0.88 and EXPLORE_ENABLED then startExplore() else startIdle() end end -------------------------------------------------- -- ๐Ÿง  MAIN AI -------------------------------------------------- RunService.Heartbeat:Connect( function(dt) if not rig.Parent or catIsDead or not character.Parent or not playerRoot.Parent or humanoid.Health <= 0 then return end -------------------------------------------------- -- AGE -------------------------------------------------- updateAge() if catIsDead then return end -------------------------------------------------- -- DEATH DETECTION -------------------------------------------------- updateDeathCause() -------------------------------------------------- -- ANIMATION -------------------------------------------------- updateLimbAnimation() -------------------------------------------------- -- NEVER SIT -------------------------------------------------- forceStand() -------------------------------------------------- -- PET -------------------------------------------------- if petting then updatePetting() return end -------------------------------------------------- -- JUMP -------------------------------------------------- if updateJump() then return end local playerPosition = playerRoot.Position local distance = ( rigRoot.Position - playerPosition ).Magnitude -------------------------------------------------- -- FLOOR SAFETY -------------------------------------------------- if not isGrounded() and not jumping then tryJump(true) return end -------------------------------------------------- -- FOOD SEARCH -------------------------------------------------- if state == "SEARCHING_FOOD" then updateFoodSearch() return end -------------------------------------------------- -- EATING -------------------------------------------------- if state == "EATING" then updateEating() return end -------------------------------------------------- -- WATER SEARCH -------------------------------------------------- if state == "SEARCHING_WATER" then updateWaterSearch() return end -------------------------------------------------- -- DRINKING -------------------------------------------------- if state == "DRINKING" then updateDrinking() return end -------------------------------------------------- -- SNIFF -------------------------------------------------- if state == "SMELLING" then updateSmelling() if distance > FOLLOW_RESUME_DISTANCE then state = "FOLLOWING" end return end -------------------------------------------------- -- GROOM -------------------------------------------------- if state == "GROOMING" then updateGrooming() if distance > FOLLOW_RESUME_DISTANCE then state = "FOLLOWING" end return end -------------------------------------------------- -- PAW -------------------------------------------------- if state == "PAWING" then updatePawing() return end -------------------------------------------------- -- STRETCH -------------------------------------------------- if state == "STRETCHING" then updateStretching() return end -------------------------------------------------- -- WATCH -------------------------------------------------- if state == "WATCHING" then updateWatching() return end -------------------------------------------------- -- PLAY -------------------------------------------------- if state == "PLAYING" then updatePlaying() if distance > 25 then state = "FOLLOWING" end return end -------------------------------------------------- -- CHASE -------------------------------------------------- if state == "CHASING" then updateChasing() return end -------------------------------------------------- -- RANDOM RUN -------------------------------------------------- if state == "RUNNING_RANDOM" then updateRandomRun( playerPosition ) return end -------------------------------------------------- -- EXPLORE -------------------------------------------------- if state == "WANDERING" then updateExplore( playerPosition ) return end -------------------------------------------------- -- RECOVERY -------------------------------------------------- if recovering then if time() < recoveryUntil then moveDirection( playerPosition - rigRoot.Position, WALK_SPEED ) updateStuck( true, dt ) return else recovering = false end end -------------------------------------------------- -- TOO FAR -------------------------------------------------- if distance >= TOO_FAR_DISTANCE then state = "RUNNING" moveDirection( playerPosition - rigRoot.Position, RUN_SPEED ) updateStuck(true, dt) return end -------------------------------------------------- -- RUNNING -------------------------------------------------- if state == "RUNNING" then if distance <= RUN_DISTANCE then state = "FOLLOWING" humanoid.WalkSpeed = WALK_SPEED return end moveDirection( playerPosition - rigRoot.Position, RUN_SPEED ) updateStuck(true, dt) return end -------------------------------------------------- -- FOLLOWING -------------------------------------------------- if state == "FOLLOWING" then if distance <= STOP_DISTANCE then stopMoving() humanoid.WalkSpeed = 0 lookAt(playerPosition) if random:NextNumber() < 0.012 then chooseIdleBehavior() end return end moveDirection( playerPosition - rigRoot.Position, WALK_SPEED ) updateStuck(true, dt) return end -------------------------------------------------- -- IDLE -------------------------------------------------- if state == "IDLE" then stopMoving() humanoid.WalkSpeed = 0 lookAt(playerPosition) if distance > FOLLOW_RESUME_DISTANCE then state = "FOLLOWING" humanoid.WalkSpeed = WALK_SPEED return end if time() >= stateUntil then chooseIdleBehavior() end return end end ) -------------------------------------------------- -- ๐Ÿ”„ PLAYER RESPAWN -------------------------------------------------- player.CharacterAdded:Connect( function(newCharacter) character = newCharacter local newRoot = newCharacter:WaitForChild( "HumanoidRootPart" ) playerRoot = newRoot rayParams.FilterDescendantsInstances = { rig, character } -------------------------------------------------- -- CAT DOES NOT RESET -------------------------------------------------- if rig.Parent and not catIsDead then jumping = false recovering = false petting = false behaviorTarget = nil rig:PivotTo( newRoot.CFrame * CFrame.new( 0, 0, -SPAWN_DISTANCE ) ) state = "FOLLOWING" targetPosition = nil humanoid.JumpPower = NORMAL_JUMP_POWER humanoid.WalkSpeed = WALK_SPEED humanoid.AutoRotate = true forceStand() if petPrompt then petPrompt.Enabled = true end end end ) -------------------------------------------------- -- โ˜ ๏ธ HUMANOID DEATH -------------------------------------------------- humanoid.Died:Connect( function() if catIsDead then return end -------------------------------------------------- -- Determine cause -------------------------------------------------- if deathCause == "Unknown" then local velocity = rigRoot.AssemblyLinearVelocity if velocity.Magnitude >= HIGH_SPEED_IMPACT then deathCause = "High-speed impact" else deathCause = "Unexpected accident" end end catIsDead = true state = "DEAD" jumping = false petting = false recovering = false targetPosition = nil behaviorTarget = nil humanoid:Move( Vector3.zero, false ) humanoid.WalkSpeed = 0 humanoid.JumpPower = 0 humanoid.AutoRotate = false stopWalk() if petPrompt then petPrompt.Enabled = false end if sound then sound:Stop() end task.defer(function() if rig.Parent then ragdollCat() end end) print( "โ˜ ๏ธ Cat died from: " .. tostring(deathCause) ) end ) -------------------------------------------------- -- ๐Ÿ›ก๏ธ ANTI-STUCK / COLLISION -------------------------------------------------- task.spawn(function() while rig.Parent and not catIsDead do task.wait(0.15) if humanoid.Health <= 0 then break end forceStand() for _, object in ipairs( rig:GetDescendants() ) do if object:IsA("BasePart") then object.Anchored = false object.CanCollide = true object.CanTouch = true object.CanQuery = true object.Massless = false pcall(function() object.CollisionGroup = "Default" end) end end if not jumping and not petting and state ~= "IDLE" and state ~= "SMELLING" and state ~= "GROOMING" and state ~= "PAWING" and state ~= "WATCHING" and state ~= "EATING" and state ~= "DRINKING" and state ~= "STRETCHING" then local velocity = rigRoot.AssemblyLinearVelocity local moving = humanoid.MoveDirection.Magnitude > 0.2 if velocity.Magnitude < 0.15 and moving and isGrounded() then tryJump(true) end end end end) -------------------------------------------------- -- ๐Ÿฑ DONE -------------------------------------------------- print("๐Ÿฑ Smart Cat Companion AI loaded!") print("----------------------------------") print("๐Ÿšซ Sitting: OFF") print("๐Ÿšซ Sleeping: OFF") print("โค๏ธ Pet Cat: ON") print("๐Ÿ– Food system: ON") print("๐Ÿ’ง Water system: ON") print("๐Ÿ‘ƒ Sniffing: ON") print("๐Ÿงผ Grooming: ON") print("๐Ÿพ Pawing: ON") print("๐Ÿง˜ Stretching: ON") print("๐Ÿ‘€ Watching: ON") print("๐Ÿฆ Chasing: ON") print("๐ŸŽพ Playing: ON") print("๐Ÿƒ Zoomies: ON") print("๐Ÿพ Exploration: ON") print("๐Ÿ˜บ Random sounds: ON") print("๐Ÿงฑ Wall avoidance: ON") print("๐Ÿ›ก๏ธ Hard collision: ON") print("๐Ÿง  Stuck recovery: ON") print("๐Ÿธ Smart jumping: ON") print("๐ŸŒฑ Growth: ON") print("โฑ๏ธ Lifespan: 30 minutes") print("โ˜ ๏ธ Death causes: ON") print("๐Ÿฆด Ragdoll death: ON") print("๐Ÿ”„ Player respawn protection: ON") print("----------------------------------")