Dark_Agent

Pure Reanimation Module (Rootleak)

Mar 3rd, 2026 (edited)
254
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.53 KB | Gaming | 0 0
  1. -- credits: https://github.com/Rootleak/
  2.  
  3. local stalkie = {
  4.     services = {
  5.         players = cloneref(game:GetService("Players"));
  6.         workspace = cloneref(game:GetService("Workspace"));
  7.         replicated = cloneref(game:GetService("ReplicatedStorage"));
  8.         run_service = cloneref(game:GetService("RunService"));
  9.         user_input_service = cloneref(game:GetService("UserInputService"));
  10.         http_service = cloneref(game:GetService("HttpService"));
  11.     };
  12.     flags = {
  13.         reanimated = false;
  14.     };
  15.     clones = {};
  16.     connections = {
  17.         hb = nil;
  18.         died = nil;
  19.         real_char_child_removed = nil;
  20.         character_removing = nil;
  21.         clone_died = nil;
  22.         clone_char_child_removed = nil;
  23.         animation_hb = nil;
  24.     };
  25.     real_chars = {};
  26.     callbacks = {
  27.         on_play = nil,
  28.         on_stop = nil,
  29.     },
  30.     animation = {
  31.         cache = {};
  32.         state = {
  33.             is_playing = false;
  34.             current_url = nil;
  35.             speed = 1.0;
  36.             keyframes = nil;
  37.             total_duration = 0;
  38.             elapsed_time = 0;
  39.         };
  40.         original_motor_c0s = {};
  41.         joints = {};
  42.     };
  43. };
  44.  
  45. local API = {};
  46.  
  47. local get_game_ragdoll_info = function(enable)
  48.     local place_id = game.PlaceId;
  49.     if place_id == 15546218972 or place_id == 6884319169 then
  50.         -- Mic Up and Mic Up 18+
  51.         local remote = stalkie.services.replicated:WaitForChild("event_rag");
  52.         return remote, {"Ball"}, false;
  53.     elseif place_id == 5991163185 then
  54.         -- Spray Paint
  55.         local remote = stalkie.services.replicated.Remotes.Physics.Ragdoll;
  56.         return remote, {}, false;
  57.     elseif place_id == 5683833663 then
  58.         -- Ragdoll Engine (uses LocalEvent, not RemoteEvent)
  59.         local local_event = stalkie.services.replicated:WaitForChild("LocalRagdollEvent");
  60.         return local_event, {enable}, true;
  61.     elseif place_id == 16893957142 or place == 4832438542 then
  62.         -- VR Hands and VR Hands 18+
  63.         local local_event = stalkie.services.replicated.COM.Ragdoll:WaitForChild("Request");
  64.         return local_event, {9e9}, true;
  65.  
  66.     end;
  67.     return nil, nil, false;
  68. end;
  69.  
  70. local set_model_transparency = function(model, transparency)
  71.     if not model then
  72.         return;
  73.     end;
  74.     for _, part in model:GetDescendants() do
  75.         if part:IsA("BasePart") then
  76.             part.Transparency = transparency;
  77.         end;
  78.     end;
  79. end;
  80.  
  81. local get_local_player = function()
  82.     local player = stalkie.services.players.LocalPlayer;
  83.     if not player then
  84.         return "bad argument to 'get_local_player' (LocalPlayer not found; must run in a LocalScript)";
  85.     end;
  86.     return player;
  87. end;
  88.  
  89. local get_char = function(player)
  90.     if typeof(player) ~= "Instance" or not player:IsA("Player") then
  91.         return ("bad argument #1 to 'get_char' (Player expected, got %s)"):format(typeof(player));
  92.     end;
  93.     local character = player.Character;
  94.     if not character or not character.Parent then
  95.         return ("Player %s has no active character."):format(player.Name);
  96.     end;
  97.     return character;
  98. end;
  99.  
  100. local clone_char = function(model)
  101.     if typeof(model) ~= "Instance" then
  102.         return ("bad argument #1 to 'clone_char' (Instance expected, got %s)"):format(typeof(model));
  103.     end;
  104.     model.Archivable = true;
  105.     local new_clone = model:Clone();
  106.     model.Archivable = false;
  107.     new_clone.Name = "Reanimation";
  108.     new_clone.Parent = stalkie.services.workspace;
  109.     new_clone:WaitForChild("Animate").Disabled = true;
  110.     new_clone.Humanoid.RequiresNeck = false;
  111.     new_clone.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None;
  112.     if new_clone:FindFirstChildWhichIsA("ForceField") then
  113.         new_clone:FindFirstChildWhichIsA("ForceField"):Destroy();
  114.     end;
  115.     return new_clone;
  116. end;
  117.  
  118. local fire_remote = function(remote, is_local, ...)
  119.     if typeof(remote) ~= "Instance" then
  120.         return ("bad argument to 'fire_remote' (Instance expected, got %s)"):format(typeof(remote));
  121.     end;
  122.     if is_local then
  123.         -- Handle local events (BindableEvent)
  124.         if not remote:IsA("BindableEvent") then
  125.             return ("bad argument to 'fire_remote' (BindableEvent expected for local event, got %s)"):format(remote.ClassName);
  126.         end;
  127.         remote:Fire(...);
  128.     else
  129.         -- Handle remote events/functions
  130.         if not (remote:IsA("RemoteEvent") or remote:IsA("RemoteFunction")) then
  131.             return ("bad argument to 'fire_remote' (RemoteEvent or RemoteFunction expected, got %s)"):format(remote.ClassName);
  132.         end;
  133.         if remote:IsA("RemoteEvent") then
  134.             remote:FireServer(...);
  135.         else
  136.             remote:InvokeServer(...);
  137.         end;
  138.     end;
  139. end;
  140.  
  141. --- Stops any currently playing animation.
  142. API.stop_animation = function()
  143.     if not stalkie.animation.state.is_playing then return end;
  144.    
  145.     local stopped_url = stalkie.animation.state.current_url
  146.  
  147.     if stalkie.connections.animation_hb then
  148.         stalkie.connections.animation_hb:Disconnect();
  149.         stalkie.connections.animation_hb = nil;
  150.     end
  151.  
  152.     local player = get_local_player();
  153.     if typeof(player) == "string" then return player end;
  154.  
  155.     local clone_char = API.get_clone(player);
  156.     if clone_char then
  157.         for motor, orig_c0 in pairs(stalkie.animation.original_motor_c0s) do
  158.             if motor and motor.Parent then
  159.                 motor.C0 = orig_c0;
  160.             end
  161.         end
  162.         local clone_animate_script = clone_char:FindFirstChild("Animate")
  163.         if clone_animate_script then
  164.             clone_animate_script.Enabled = true
  165.         end
  166.     end
  167.    
  168.     table.clear(stalkie.animation.original_motor_c0s);
  169.     table.clear(stalkie.animation.joints);
  170.     stalkie.animation.state = { is_playing = false, current_url = nil, speed = 1.0, keyframes = nil, total_duration = 0, elapsed_time = 0 };
  171.  
  172.     if stalkie.callbacks.on_stop then
  173.         pcall(stalkie.callbacks.on_stop, stopped_url)
  174.     end
  175. end;
  176.  
  177. --- Toggles the Reanimate state.
  178. -- @param bool (boolean) - true to enable reanimation, false to disable.
  179. -- @param remote (Instance) [optional] - A RemoteEvent or RemoteFunction to fire.
  180. -- @param args (table) [optional] - Arguments for the remote.
  181. API.reanimate = function(bool, remote, args)
  182.     if bool ~= true and bool ~= false then
  183.         return ("bad argument #1 to 'reanimate' (boolean expected, got %s)"):format(typeof(bool));
  184.     end;
  185.     local player = get_local_player();
  186.     if typeof(player) == "string" then return player end;
  187.  
  188.     -- Auto-detect game ragdoll remote if none provided
  189.     local is_local_event = false;
  190.     if not remote then
  191.         local game_remote, game_args, is_local = get_game_ragdoll_info(bool);
  192.         if game_remote then
  193.             remote = game_remote;
  194.             args = game_args;
  195.             is_local_event = is_local;
  196.         end;
  197.     end;
  198.  
  199.     if bool then
  200.         if stalkie.flags.reanimated then
  201.             return "Already reanimated.";
  202.         end;
  203.         local real_char = get_char(player);
  204.         if typeof(real_char) == "string" then return real_char end;
  205.         if not real_char:FindFirstChild("Humanoid") then
  206.             return "Real character is missing a Humanoid.";
  207.         end;
  208.         local real_hrp = real_char:FindFirstChild("HumanoidRootPart")
  209.         if not real_hrp then
  210.             return "Real character is missing a HumanoidRootPart, cannot reanimate.";
  211.         end
  212.         stalkie.real_chars[player] = real_char;
  213.         local cloned_char = clone_char(real_char);
  214.         if typeof(cloned_char) == "string" then return cloned_char end;
  215.         if not cloned_char:FindFirstChild("Humanoid") then
  216.             return "Cloned character failed to create or is missing a Humanoid.";
  217.         end;
  218.         stalkie.clones[player] = cloned_char;
  219.         set_model_transparency(cloned_char, 1);
  220.         local player_gui = player:FindFirstChildWhichIsA("PlayerGui");
  221.         if player_gui then
  222.             for _, gui in player_gui:GetChildren() do
  223.                 if gui:IsA("ScreenGui") and gui.ResetOnSpawn then
  224.                     gui.ResetOnSpawn = false;
  225.                 end;
  226.             end;
  227.         end;
  228.         player.Character = cloned_char;
  229.         cloned_char:WaitForChild("Animate").Disabled = true;
  230.         cloned_char:WaitForChild("Animate").Disabled = false;
  231.         if player_gui then
  232.             for _, gui in player_gui:GetChildren() do
  233.                 if gui:IsA("ScreenGui") and not gui.ResetOnSpawn then
  234.                     gui.ResetOnSpawn = true;
  235.                 end;
  236.             end;
  237.         end;
  238.         stalkie.connections.hb = stalkie.services.run_service.Heartbeat:Connect(function()
  239.             if not real_char or not real_char.Parent or not cloned_char or not cloned_char.Parent then
  240.                 API.reanimate(false, remote, args);
  241.                 return;
  242.             end;
  243.             for _, p in real_char:GetChildren() do
  244.                 local clone_part = cloned_char:FindFirstChild(p.Name);
  245.                 if p:IsA("BasePart") and clone_part then
  246.                     p.CFrame = clone_part.CFrame;
  247.                     p.AssemblyLinearVelocity = clone_part.AssemblyLinearVelocity; p.AssemblyAngularVelocity = clone_part.AssemblyAngularVelocity;
  248.                 end;
  249.             end;
  250.         end);
  251.         local real_humanoid = real_char.Humanoid;
  252.         local cloned_humanoid = cloned_char.Humanoid;
  253.         stalkie.connections.died = real_humanoid.Died:Connect(function()
  254.             API.reanimate(false, remote, args);
  255.         end);
  256.         stalkie.connections.real_char_child_removed = real_char.ChildRemoved:Connect(function(child)
  257.             if child == real_humanoid or child == real_hrp then
  258.                 API.reanimate(false, remote, args);
  259.             end;
  260.         end);
  261.         stalkie.connections.clone_char_child_removed = cloned_char.ChildRemoved:Connect(function(child)
  262.             if child == cloned_humanoid then
  263.                 API.reanimate(false, remote, args);
  264.             end;
  265.         end);
  266.         stalkie.connections.clone_died = cloned_humanoid.Died:Connect(function()
  267.             local current_real_humanoid = real_char and real_char:FindFirstChild("Humanoid");
  268.             if current_real_humanoid and current_real_humanoid.Health > 0 then
  269.                 current_real_humanoid.Health = 0;
  270.             else
  271.                 API.reanimate(false, remote, args);
  272.             end;
  273.         end);
  274.         stalkie.connections.character_removing = player.CharacterRemoving:Connect(function(character_being_removed)
  275.             if character_being_removed == cloned_char or character_being_removed == real_char then
  276.                 API.reanimate(false, remote, args);
  277.             end;
  278.         end);
  279.         if remote then
  280.             local err = fire_remote(remote, is_local_event, unpack(args or {}));
  281.             if err then return err end;
  282.         end;
  283.         stalkie.flags.reanimated = true;
  284.     else
  285.         if not stalkie.flags.reanimated then
  286.             return;
  287.         end;
  288.         API.stop_animation();
  289.         if remote then
  290.             local err = fire_remote(remote, is_local_event, unpack(args or {}));
  291.             if err then return err end;
  292.         end;
  293.         for key, connection in pairs(stalkie.connections) do
  294.             if connection then
  295.                 connection:Disconnect();
  296.                 stalkie.connections[key] = nil;
  297.             end;
  298.         end;
  299.         local cloned_char = stalkie.clones[player];
  300.         if cloned_char and cloned_char.Parent then
  301.             cloned_char:Destroy();
  302.             stalkie.clones[player] = nil;
  303.         end;
  304.         local real_char = stalkie.real_chars[player];
  305.         if real_char and real_char.Parent then
  306.             set_model_transparency(real_char, 0);
  307.             local hrp = real_char:FindFirstChild("HumanoidRootPart");
  308.             if hrp then
  309.                 hrp.Transparency = 1;
  310.             end;
  311.             local player_gui = player:FindFirstChildWhichIsA("PlayerGui");
  312.             if player_gui then
  313.                 for _, gui in player_gui:GetChildren() do
  314.                     if gui:IsA("ScreenGui") and gui.ResetOnSpawn then
  315.                         gui.ResetOnSpawn = false;
  316.                     end;
  317.                 end;
  318.             end;
  319.             player.Character = real_char;
  320.             if player_gui then
  321.                 for _, gui in player_gui:GetChildren() do
  322.                     if gui:IsA("ScreenGui") and not gui.ResetOnSpawn then
  323.                         gui.ResetOnSpawn = true;
  324.                     end;
  325.                 end;
  326.             end;
  327.         end;
  328.         stalkie.flags.reanimated = false;
  329.     end;
  330. end;
  331.  
  332. --- Plays an animation on the reanimated character.
  333. -- @param url (string) - The URL of the keyframe script.
  334. -- @param speed (number) [optional] - The playback speed multiplier. Defaults to 1.
  335. API.play_animation = function(url, speed)
  336.     if not stalkie.flags.reanimated then
  337.         return "Cannot play animation, not reanimated.";
  338.     end
  339.    
  340.     local player = get_local_player();
  341.     if typeof(player) == "string" then return player end;
  342.    
  343.     local clone_char = API.get_clone(player);
  344.     if not clone_char then
  345.         return "Cannot play animation, clone character not found.";
  346.     end
  347.    
  348.     if stalkie.animation.state.is_playing and stalkie.animation.state.current_url == url then
  349.         API.stop_animation();
  350.         return;
  351.     end
  352.    
  353.     API.stop_animation();
  354.    
  355.     local clone_anim_controller = clone_char:FindFirstChildOfClass("Humanoid") or clone_char:FindFirstChildOfClass("AnimationController")
  356.     if clone_anim_controller then
  357.         for _, track in ipairs(clone_anim_controller:GetPlayingAnimationTracks()) do
  358.             track:Stop()
  359.         end
  360.     end
  361.     local clone_animate_script = clone_char:FindFirstChild("Animate")
  362.     if clone_animate_script then
  363.         clone_animate_script.Enabled = false
  364.     end
  365.    
  366.     local anim = stalkie.animation;
  367.     anim.state.speed = tonumber(speed) or 1.0;
  368.  
  369.     local keyframe_data = anim.cache[url];
  370.     if not keyframe_data then
  371.         local success, response = pcall(game.HttpGet, game, url);
  372.         if not success then return "Animation Error: Failed to fetch URL." end
  373.  
  374.         local loaded_fn, err = loadstring(response);
  375.         if not loaded_fn then return "Animation Error: Invalid script from URL. " .. tostring(err) end;
  376.        
  377.         local success, data = pcall(loaded_fn)
  378.         if not success then return "Animation Error: Script from URL failed to execute. " .. tostring(data) end
  379.         keyframe_data = data;
  380.  
  381.         if typeof(keyframe_data) ~= "table" then return "Animation Error: Script from URL did not return a table." end;
  382.        
  383.         anim.cache[url] = keyframe_data;
  384.     end
  385.  
  386.     local keyframes = keyframe_data[next(keyframe_data)];
  387.     if not keyframes or #keyframes == 0 then
  388.         return "No keyframes array found for animation URL: " .. url;
  389.     end
  390.  
  391.     anim.state.keyframes = keyframes;
  392.  
  393.     table.clear(anim.joints);
  394.     table.clear(anim.original_motor_c0s);
  395.     for _, descendant in ipairs(clone_char:GetDescendants()) do
  396.         if descendant:IsA("Motor6D") then
  397.             anim.joints[descendant.Part1.Name] = descendant;
  398.             anim.original_motor_c0s[descendant] = descendant.C0;
  399.         end
  400.     end
  401.  
  402.     anim.state.is_playing = true;
  403.     anim.state.current_url = url;
  404.     anim.state.total_duration = keyframes[#keyframes].Time;
  405.     if anim.state.total_duration <= 0 then API.stop_animation(); return end;
  406.    
  407.     anim.state.elapsed_time = 0;
  408.    
  409.     if stalkie.callbacks.on_play then
  410.         pcall(stalkie.callbacks.on_play, anim.state.current_url)
  411.     end
  412.    
  413.     stalkie.connections.animation_hb = stalkie.services.run_service.Heartbeat:Connect(function(deltaTime)
  414.         if not anim.state.is_playing then return end;
  415.        
  416.         anim.state.elapsed_time = (anim.state.elapsed_time + (deltaTime * anim.state.speed)) % anim.state.total_duration;
  417.        
  418.         local current_frame, next_frame;
  419.         for i = 1, #anim.state.keyframes - 1 do
  420.             if anim.state.elapsed_time >= anim.state.keyframes[i].Time and anim.state.elapsed_time < anim.state.keyframes[i+1].Time then
  421.                 current_frame = anim.state.keyframes[i];
  422.                 next_frame = anim.state.keyframes[i+1];
  423.                 break;
  424.             end
  425.         end
  426.         if not current_frame then
  427.             current_frame = anim.state.keyframes[#anim.state.keyframes];
  428.             next_frame = anim.state.keyframes[1];
  429.         end
  430.        
  431.         local frame_duration = next_frame.Time - current_frame.Time;
  432.         if frame_duration <= 0 then frame_duration = anim.state.total_duration end;
  433.  
  434.         local alpha = (frame_duration > 0) and (anim.state.elapsed_time - current_frame.Time) / frame_duration or 0;
  435.         alpha = math.clamp(alpha, 0, 1)
  436.  
  437.         for partName, pose_cframe in pairs(current_frame.Data) do
  438.             local motor = anim.joints[partName];
  439.             if motor and anim.original_motor_c0s[motor] then
  440.                 local original_c0 = anim.original_motor_c0s[motor];
  441.                 local next_pose_cframe = next_frame.Data and next_frame.Data[partName];
  442.  
  443.                 if next_pose_cframe then
  444.                     motor.C0 = original_c0 * pose_cframe:Lerp(next_pose_cframe, alpha);
  445.                 else
  446.                     motor.C0 = original_c0 * pose_cframe;
  447.                 end
  448.             end
  449.         end
  450.     end);
  451. end;
  452.  
  453. --- Sets the playback speed for any currently playing animation.
  454. -- @param speed (number) - The new playback speed multiplier.
  455. API.set_animation_speed = function(speed)
  456.     stalkie.animation.state.speed = tonumber(speed) or 1.0;
  457. end;
  458.  
  459. --- Registers a callback function to be called when an animation starts playing.
  460. -- @param callback (function) - The function to call. It receives the animation URL as an argument.
  461. API.on_animation_play = function(callback)
  462.     if type(callback) == "function" then
  463.         stalkie.callbacks.on_play = callback
  464.     end
  465. end
  466.  
  467. --- Registers a callback function to be called when an animation stops.
  468. -- @param callback (function) - The function to call. It receives the animation URL that was stopped.
  469. API.on_animation_stop = function(callback)
  470.     if type(callback) == "function" then
  471.         stalkie.callbacks.on_stop = callback
  472.     end
  473. end
  474.  
  475. --- Returns the current animation playback state.
  476. -- @return boolean, string | nil - is_playing, current_url
  477. API.is_animation_playing = function()
  478.     return stalkie.animation.state.is_playing, stalkie.animation.state.current_url
  479. end
  480.  
  481. --- Returns true if the local player is currently reanimated.
  482. -- @return boolean
  483. API.is_reanimated = function()
  484.     return stalkie.flags.reanimated;
  485. end;
  486.  
  487. --- Gets the active clone character model for a player.
  488. -- @param player (Player) [optional] - The player to get the clone of. Defaults to LocalPlayer.
  489. -- @return Model | nil
  490. API.get_clone = function(player)
  491.     player = player or get_local_player();
  492.     if typeof(player) == "string" then return nil end;
  493.     return stalkie.clones[player];
  494. end;
  495.  
  496. --- Gets the real character model for a player.
  497. -- @param player (Player) [optional] - The player to get the real character of. Defaults to LocalPlayer.
  498. -- @return Model | nil
  499. API.get_real_character = function(player)
  500.     player = player or get_local_player();
  501.     if typeof(player) == "string" then return nil end;
  502.     return stalkie.real_chars[player];
  503. end;
  504.  
  505. return API;
Advertisement
Add Comment
Please, Sign In to add comment