Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- services
- local Players = game:GetService("Players")
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
- local ServerScriptService = game:GetService("ServerScriptService")
- local Workspace = game:GetService("Workspace")
- local Debris = game:GetService("Debris")
- -- modules
- -- References to various server-side modules are defined here.
- -- These modules manage different aspects of the game, such as item carrying, paper tracking, plot management, upgrades, and settings.
- local CarryEvent = ReplicatedStorage.Remotes:WaitForChild("CarryEvent")
- local PaperEvent = ReplicatedStorage.Remotes:WaitForChild("PaperEvent")
- local UpgradeServer = require(ServerScriptService.Modules.UpgradeServer)
- local PaperTracker = require(ServerScriptService.Modules.PaperTracker)
- local PlotManager = require(ServerScriptService.Modules.PlotManager)
- local Settings = require(ServerScriptService.Modules.Settings)
- local Chance = require(ReplicatedStorage.Modules.Chance)
- -- constants and state variables
- local papersFolder = Workspace:WaitForChild("Papers") -- The folder in the game world where all paper instances are stored.
- local pickupdist = 15 -- The maximum distance a player can be from a paper to pick it up.
- local recyclercooldown = 1.2 -- The cooldown duration between consecutive sales at the recycler (selling mechanism).
- -- tables to track player state
- -- These tables are used to track players' current states and the papers they are carrying on the server side.
- local serverCarriedPapers = {} -- [player] = { {paper=instance}, ... } -> Stores a list of paper instances currently carried by each player.
- local allCarriedPaperInstances = {} -- [paperInstance] = true -> Used as a set (hashmap) for quick lookup of all paper instances currently being carried across the server.
- local originalPlayerSpeeds = {} -- [player] = originalSpeed -> Stores players' original walk speeds, as their speed is reduced when carrying papers.
- local sellCooldowns = {} -- [player] = true -> Tracks selling cooldowns for players to prevent spamming the selling mechanism.
- local promptConnections = {} -- [player] = {Prompt=conn, Touch=conn} -> Stores connections for selling triggers (ProximityPrompt and Touched event) associated with each player's plot. This is crucial for properly disconnecting connections when a player leaves or their plot changes.
- -- caches for better performance
- -- Tables used to improve performance by caching frequently calculated values.
- local paperCapacityCache = {} -- [player] = capacity -> Caches the maximum paper carrying capacity for each player.
- local speedReductionCache = {} -- [player] = reduction -> Caches the speed reduction per paper carried for each player.
- local sellBonusChanceTables = {} -- [level] = chanceTable -> Caches chance tables for different sell bonus levels, avoiding their recreation on every sale.
- -- This function retrieves the maximum number of papers a player can carry.
- -- It utilizes a cache to avoid recalculating the value repeatedly, which is beneficial for performance.
- local function getMaxPapers(player)
- if not player then return 5 end -- Returns a default value if the player is invalid.
- if paperCapacityCache[player] then return paperCapacityCache[player] end -- Returns from cache if the value is already present.
- local maxPapers = UpgradeServer.GetValue(player, "CarryCapacity") or 5 -- Retrieves the carry capacity from UpgradeServer, defaults to 5 if not found.
- paperCapacityCache[player] = maxPapers -- Caches the calculated value.
- return maxPapers
- end
- -- This function determines how much a player slows down per paper carried.
- -- It also uses a cache, similar to the previous function, for performance optimization.
- local function getSpeedReduction(player)
- if not player then return 1.5 end -- Returns a default value if the player is invalid.
- if speedReductionCache[player] then return speedReductionCache[player] end -- Returns from cache if the value is already present.
- local speedReduction = UpgradeServer.GetValue(player, "CarryBalance") or 1.5 -- Retrieves the balance (speed reduction) value from UpgradeServer, defaults to 1.5 if not found.
- speedReductionCache[player] = speedReduction -- Caches the calculated value.
- return speedReduction
- end
- -- Clears the relevant caches when a player upgrades something.
- -- This ensures that the next time the `getMaxPapers` or `getSpeedReduction` functions are called, they re-calculate the updated values.
- local function clearCachesOnUpgrade(player, key)
- if key == "CarryCapacity" then
- paperCapacityCache[player] = nil -- Invalidates the paper capacity cache.
- elseif key == "CarryBalance" then
- speedReductionCache[player] = nil -- Invalidates the speed reduction cache.
- end
- end
- --visual and audio effects
- -- This function makes the gears on the recycler model spin.
- -- It runs in a new thread (task.spawn) to prevent blocking the main script execution.
- local function spinRecyclerGears(recyclerModel, recyclerUnion)
- task.spawn(function()
- local gears = {}
- -- Finds all "Gear" named UnionOperation parts within the recycler model.
- for _, child in ipairs(recyclerModel:GetDescendants()) do
- if child.Name == "Gear" and child:IsA("UnionOperation") then
- table.insert(gears, child)
- end
- end
- if #gears == 0 then return end
- -- start sound
- -- Plays the recycler start-up sound. The sound is cloned and then cleaned up by the Debris service after playback.
- local sndMachineStart = recyclerModel:FindFirstChild("MachineStart", true)
- if sndMachineStart and recyclerUnion then
- local sound = sndMachineStart:Clone()
- sound.Parent = recyclerUnion
- sound:Play()
- Debris:AddItem(sound, 1)
- end
- -- spin them
- -- Rotates the gears for a specified duration.
- local startTime, duration, ROTATION_SPEED = os.clock(), 1.0, 120
- while os.clock() - startTime < duration do
- local dt = task.wait() -- Waits for the next frame and gets the elapsed time.
- local rotationCFrame = CFrame.Angles(0, 0, math.rad(ROTATION_SPEED * dt)) -- Calculates how much the gears should rotate in each step.
- for _, gear in ipairs(gears) do
- gear.CFrame = gear.CFrame * rotationCFrame -- Updates the gear's CFrame to apply the rotation.
- end
- end
- -- end sound
- -- Plays the recycler shut-down sound.
- local sndMachineEnd = recyclerModel:FindFirstChild("MachineEnd", true)
- if sndMachineEnd and recyclerUnion then
- local sound = sndMachineEnd:Clone()
- sound.Parent = recyclerUnion
- sound:Play()
- Debris:AddItem(sound, sound.TimeLength + 1)
- end
- end)
- end
- -- This function triggers all visual and audio effects when a player sells paper.
- -- These include playing a sell sound, triggering a local screen shake for the player, and spinning the recycler gears.
- local function triggerRecyclerEffects(player)
- local playerPlot = PlotManager.GetPlayerPlot(player) -- Retrieves the player's plot.
- if not playerPlot then return end
- local recyclerModel = playerPlot:FindFirstChild("Recycler") -- Finds the recycler model within the plot.
- if not recyclerModel then return end
- local recyclerUnion = recyclerModel:FindFirstChild("Recycler") -- Finds the main union part of the recycler.
- if recyclerUnion then
- local sndSell = recyclerModel:FindFirstChild("Sell", true) -- Finds the sell sound.
- if sndSell then
- local sound = sndSell:Clone()
- sound.Parent = recyclerUnion
- sound.PlaybackSpeed = 0.9 + math.random() * (1.05 - 0.9) -- Randomizes the playback speed of the sound.
- sound:Play()
- Debris:AddItem(sound, sound.TimeLength + 1) -- Ensures the sound is automatically cleaned up after playing.
- end
- end
- CarryEvent:FireClient(player, "PlayLocalShake", recyclerModel) -- Fires a client event to play a local screen shake effect for the player.
- spinRecyclerGears(recyclerModel, recyclerUnion) -- Initiates the gear spinning animation for the recycler.
- end
- --selling logic
- -- This function calculates the total money multiplier from all boosts and mutations.
- -- It accounts for cash boosts from player upgrades, mutation multipliers of the paper, and a chance-based sell bonus.
- local function calculateSaleMultiplier(player, paperInstance)
- local multiplier = 1.0
- -- 1. cash boost from upgrades
- local boostLvl = UpgradeServer.GetLevel(player, "CashBoost") -- Gets the player's "CashBoost" upgrade level.
- multiplier += (0.05 * boostLvl) -- Adds a 5% bonus for each level.
- -- 2. mutations multiplier
- local mutation = paperInstance:GetAttribute("Mutation") -- Checks the mutation attribute of the paper.
- if mutation == "Rainbow" then
- multiplier *= Settings.Mutations.Rainbow.ValueMultiplier -- Applies a specific multiplier for "Rainbow" mutation.
- elseif mutation == "Shiny" then
- multiplier *= Settings.Mutations.Shiny.ValueMultiplier -- Applies a specific multiplier for "Shiny" mutation.
- end
- -- 3. sell bonus chance
- local sellBonusLevel = UpgradeServer.GetLevel(player, "SellBonus") -- Gets the player's "SellBonus" upgrade level.
- local sellBonusActivated = false
- if sellBonusLevel > 0 then
- -- Uses the `sellBonusChanceTables` cache to create or retrieve a chance table for each bonus level.
- if not sellBonusChanceTables[sellBonusLevel] then
- sellBonusChanceTables[sellBonusLevel] = Chance.new({
- ["Success"] = sellBonusLevel, -- Success chance is equal to the upgrade level.
- ["Fail"] = 100 - sellBonusLevel, -- Fail chance is 100 minus the upgrade level.
- })
- end
- if sellBonusChanceTables[sellBonusLevel]:Run() == "Success" then -- Runs the chance table to check if the bonus is triggered.
- multiplier *= 2 -- Doubles the multiplier if the bonus is activated.
- sellBonusActivated = true
- end
- end
- return multiplier, sellBonusActivated, mutation -- Returns the calculated multiplier, whether the bonus was activated, and the mutation type.
- end
- -- This is the main sale function that performs all the core selling logic.
- -- It is called by other selling functions (e.g., when a paper is touched or a prompt is triggered).
- local function processPaperSale(player, paperInstance)
- local guid = paperInstance:GetAttribute("GUID") -- Retrieves the unique ID of the paper.
- local baseCash = PaperTracker.SellPaper(player, guid) -- Sells the paper via the PaperTracker module and gets its base cash value.
- if not baseCash then return end -- Exits if the sale fails (e.g., invalid paper).
- -- Get all the bonuses
- local multiplier, sellBonusActivated, mutation = calculateSaleMultiplier(player, paperInstance) -- Calculates all multipliers for the paper.
- local totalCashFromSale = math.floor(baseCash * multiplier + 0.5) -- Calculates the total cash amount and rounds it.
- -- Give the player money
- UpgradeServer.AddCoins(player, totalCashFromSale) -- Adds the earned coins to the player.
- -- Inform the player they received money with cool popups
- PaperEvent:FireClient(player, "showIncome", totalCashFromSale) -- Sends a client event to show an income popup.
- if sellBonusActivated then
- PaperEvent:FireClient(player, "showBonus", "2x BONUS!") -- Shows a 2x bonus popup if the sell bonus was activated.
- end
- if mutation then
- PaperEvent:FireClient(player, "showMutation", mutation .. " PAPER!") -- Shows a mutation popup if a mutated paper was sold.
- end
- -- Play sounds and effects and destroy the paper
- triggerRecyclerEffects(player) -- Triggers the selling effects (sound, screen shake, gear animation).
- paperInstance:Destroy() -- Removes the sold paper instance from the game world.
- return true -- Indicates that the sale was successful.
- end
- -- This function sells a paper when it touches the recycler's hitbox.
- local function sellTouchedPaper(paperInstance)
- local ownerId = paperInstance:GetAttribute("OwnerId") -- Gets the owner's User ID from the paper.
- if not ownerId then return end
- local player = Players:GetPlayerByUserId(ownerId) -- Retrieves the player object using the owner ID.
- if not player then return end
- -- Check if the paper is already being sold or carried, or if the machine is on cooldown.
- if paperInstance:GetAttribute("IsBeingSold") or paperInstance:GetAttribute("IsCarried") then return end
- if sellCooldowns[player] then return end
- -- Set cooldown to prevent spamming.
- sellCooldowns[player] = true -- Activates the selling cooldown for the player.
- task.delay(recyclercooldown, function() sellCooldowns[player] = nil end) -- Resets the cooldown after a specified duration.
- paperInstance:SetAttribute("IsBeingSold", true) -- Sets an attribute on the paper indicating it's being sold.
- if not processPaperSale(player, paperInstance) then -- Calls the main paper selling function.
- -- If the sale fails for some reason, unset the flag and reset the cooldown.
- paperInstance:SetAttribute("IsBeingSold", nil)
- sellCooldowns[player] = nil
- end
- end
- -- This function sells the last paper a player picked up when they activate a ProximityPrompt.
- local function sellLastCarriedPaper(player)
- if sellCooldowns[player] then return end -- Exits if the selling cooldown is active.
- local stack = serverCarriedPapers[player] -- Retrieves the player's stack of carried papers.
- if not stack or #stack == 0 then return end -- Exits if the stack is empty.
- -- Cooldown to prevent spamming.
- sellCooldowns[player] = true -- Activates the selling cooldown for the player.
- task.delay(recyclercooldown, function() sellCooldowns[player] = nil end) -- Resets the cooldown after a specified duration.
- local paperData = table.remove(stack) -- Removes the topmost (most recently picked up) paper from the stack.
- local paper = paperData.paper
- if not paper or not paper.Parent then -- Exits if the paper is invalid or no longer exists in the workspace.
- sellCooldowns[player] = nil
- return
- end
- paper:SetAttribute("IsBeingSold", true) -- Sets an attribute on the paper indicating it's being sold.
- local success = processPaperSale(player, paper) -- Calls the main paper selling function.
- if not success then
- -- If the sale failed, re-insert the paper back into the stack.
- table.insert(stack, paperData)
- paper:SetAttribute("IsBeingSold", nil)
- sellCooldowns[player] = nil
- return
- end
- -- Update player speed since they are carrying one less item.
- local character = player.Character
- if not character or not originalPlayerSpeeds[player] then return end
- local humanoid = character:FindFirstChildOfClass("Humanoid")
- if not humanoid then return end
- if #stack == 0 then -- If the player is no longer carrying any papers, reset their speed to original and stop the carry animation.
- humanoid.WalkSpeed = originalPlayerSpeeds[player]
- originalPlayerSpeeds[player] = nil
- CarryEvent:FireClient(player, "StopCarryAnim")
- else -- If they are still carrying papers, adjust their speed based on the updated stack size.
- humanoid.WalkSpeed = originalPlayerSpeeds[player] - (#stack * getSpeedReduction(player))
- end
- end
- -- carrying and dropping logic
- -- This function handles a player's request to pick up a paper.
- -- It performs validation checks and then attaches the paper to the player's character, adjusting their speed.
- local function handlePickupRequest(player, targetPaper)
- local character = player.Character
- local humanoid = character and character:FindFirstChildOfClass("Humanoid")
- local playerRoot = character and character.HumanoidRootPart
- if not humanoid or not playerRoot then return end -- Exits if the player's character or essential parts are missing.
- -- Validation checks
- if not targetPaper or not targetPaper:IsDescendantOf(papersFolder) then return end -- Checks if the target paper is valid and in the correct folder.
- if targetPaper:GetAttribute("OwnerId") ~= player.UserId then return end -- Ensures the paper belongs to the requesting player.
- if allCarriedPaperInstances[targetPaper] then return end -- Checks if the paper is already being carried.
- if (playerRoot.Position - targetPaper.Position).Magnitude > pickupdist then return end -- Checks if the player is within pickup distance.
- local playerStack = serverCarriedPapers[player] -- Retrieves the player's paper stack.
- if #playerStack >= getMaxPapers(player) then return end -- Checks if the player has reached their maximum carrying capacity.
- -- Save old speed if not already saved.
- -- Stores the player's original speed, so it can be restored when papers are dropped.
- if not originalPlayerSpeeds[player] then
- originalPlayerSpeeds[player] = humanoid.WalkSpeed
- end
- -- Attach the paper to the player.
- -- Welds the paper to the player (using WeldConstraint) and sets its physical properties.
- local mainPart = targetPaper:IsA("Model") and targetPaper.PrimaryPart or targetPaper -- Determines the main part of the paper (PrimaryPart if a model, or the part itself).
- mainPart.CanCollide = false -- Disables collisions for the paper.
- mainPart.Massless = true -- Sets the paper's mass to zero, so it doesn't affect player movement.
- targetPaper:SetAttribute("IsCarried", true) -- Sets an attribute indicating the paper is being carried.
- local anchorPart = (#playerStack == 0) and playerRoot or playerStack[#playerStack].paper -- Determines the part to which the paper will be welded (player's HumanoidRootPart for the first paper, or the previous paper in the stack).
- local randomRotation = CFrame.Angles(0, math.rad(math.random(-5, 5)), 0) -- Adds a slight random rotation for visual variety.
- local anchorCFrame = anchorPart:IsA("Model") and anchorPart.PrimaryPart.CFrame or anchorPart.CFrame -- Gets the CFrame of the anchor part.
- if #playerStack == 0 then -- If it's the first paper, position it in front of the character.
- mainPart.CFrame = anchorCFrame * CFrame.new(0, 1, -2.1) * randomRotation
- else -- Position subsequent papers slightly above the previous one in the stack.
- mainPart.CFrame = anchorCFrame * CFrame.new(0, 0.1, 0) * randomRotation
- end
- local weld = Instance.new("WeldConstraint") -- Creates a WeldConstraint to physically attach the paper.
- weld.Part0 = mainPart
- weld.Part1 = anchorPart:IsA("Model") and anchorPart.PrimaryPart or anchorPart
- weld.Parent = mainPart
- -- Add to our tracking tables.
- table.insert(playerStack, { paper = targetPaper }) -- Adds the paper to the player's stack.
- allCarriedPaperInstances[targetPaper] = true -- Adds to the global set of carried paper instances.
- -- Slow the player down.
- humanoid.WalkSpeed = originalPlayerSpeeds[player] - (#playerStack * getSpeedReduction(player)) -- Reduces the player's walk speed based on the number of papers carried.
- -- Play animations and sounds.
- if #playerStack == 1 then -- If it's the first paper, start the carry animation.
- CarryEvent:FireClient(player, "PlayCarryAnim")
- end
- CarryEvent:FireClient(player, "PlaySound", "Pickup") -- Plays a pickup sound.
- end
- -- This function handles a player's request to drop a paper.
- -- It detaches the paper from the player, restores its physical properties, and adjusts the player's speed.
- local function handleDropRequest(player, dropCFrame)
- local character = player.Character
- local humanoid = character and character:FindFirstChildOfClass("Humanoid")
- if not humanoid then return end
- local playerStack = serverCarriedPapers[player] -- Retrieves the player's paper stack.
- if #playerStack == 0 then return end -- Exits if the stack is empty.
- local paperData = table.remove(playerStack) -- Removes the topmost paper from the stack.
- local paperToDrop = paperData.paper
- if not paperToDrop or not paperToDrop.Parent then return end
- -- Unweld the paper and make it a normal object again.
- -- Detaches the paper from the player and restores its original physical properties.
- local mainPart = paperToDrop:IsA("Model") and paperToDrop.PrimaryPart or paperToDrop
- local weld = mainPart:FindFirstChildOfClass("WeldConstraint") -- Finds the WeldConstraint on the paper.
- if weld then weld:Destroy() end -- Destroys the WeldConstraint, freeing the paper.
- mainPart.CanCollide = true -- Re-enables collisions for the paper.
- mainPart.Massless = false -- Restores the paper's mass.
- paperToDrop:SetAttribute("IsCarried", nil) -- Clears the attribute indicating the paper is carried.
- mainPart:PivotTo(dropCFrame) -- Moves the paper to the specified drop location.
- -- Remove from tracking table.
- allCarriedPaperInstances[paperToDrop] = nil -- Removes from the global set of carried paper instances.
- -- Fix player speed.
- -- Adjusts the player's speed based on the remaining number of papers.
- if #playerStack == 0 and originalPlayerSpeeds[player] then -- If the player is no longer carrying any papers, restore their original speed.
- humanoid.WalkSpeed = originalPlayerSpeeds[player]
- originalPlayerSpeeds[player] = nil
- elseif originalPlayerSpeeds[player] then -- If still carrying papers, adjust speed based on the updated stack size.
- humanoid.WalkSpeed = originalPlayerSpeeds[player] - (#playerStack * getSpeedReduction(player))
- end
- -- Play animations and sounds.
- CarryEvent:FireClient(player, "PlayDropAnim") -- Plays a drop animation.
- CarryEvent:FireClient(player, "PlaySound", "Drop") -- Plays a drop sound.
- if #playerStack == 0 then -- If the player has dropped all papers, stop the carry animation.
- CarryEvent:FireClient(player, "StopCarryAnim")
- end
- end
- -- Listens for events from the client (player) via the CarryEvent RemoteEvent.
- CarryEvent.OnServerEvent:Connect(function(player, action, data)
- if action == "RequestPickup" then
- handlePickupRequest(player, data) -- Handles a paper pickup request.
- elseif action == "RequestDrop" then
- handleDropRequest(player, data) -- Handles a paper drop request.
- end
- end)
- --player and connection management
- -- This function is called when a player dies or leaves the game.
- -- It ensures that any papers they were carrying are automatically dropped (and destroyed).
- -- This prevents inconsistent states and "stuck" papers in the game world.
- local function forceDropAll(player)
- local stack = serverCarriedPapers[player]
- if not stack or #stack == 0 then return end
- local character = player.Character
- if character then
- local humanoid = character:FindFirstChildOfClass("Humanoid")
- if humanoid and originalPlayerSpeeds[player] then
- humanoid.WalkSpeed = originalPlayerSpeeds[player] -- Restores the player's speed to their original value.
- end
- end
- originalPlayerSpeeds[player] = nil -- Clears the original speed record.
- for _, paperData in ipairs(stack) do -- Iterates through each paper in the stack.
- if paperData.paper and paperData.paper.Parent then
- allCarriedPaperInstances[paperData.paper] = nil -- Removes from the global set of carried paper instances.
- paperData.paper:Destroy() -- Destroys the paper instance.
- end
- end
- serverCarriedPapers[player] = {} -- Resets the player's paper stack.
- end
- -- This function sets up the sell prompt and hitbox for a player's plot.
- -- It includes a delay if the plot hasn't loaded yet, accommodating the asynchronous nature of plot loading.
- local function connectSellTriggersForPlayer(player)
- -- Clears existing connections to prevent duplicates and ensure fresh connections are established.
- if promptConnections[player] then
- if promptConnections[player].Prompt then promptConnections[player].Prompt:Disconnect() end
- if promptConnections[player].Touch then promptConnections[player].Touch:Disconnect() end
- promptConnections[player] = nil
- end
- local playerPlot = PlotManager.GetPlayerPlot(player) -- Retrieves the player's plot.
- if not playerPlot then
- task.delay(2, function() connectSellTriggersForPlayer(player) end) -- If the plot is not found, retries after 2 seconds.
- return
- end
- local recyclerModel = playerPlot:FindFirstChild("Recycler") -- Finds the recycler model within the plot.
- if not recyclerModel then return end
- local prompt = recyclerModel:FindFirstChildOfClass("ProximityPrompt") -- Finds the ProximityPrompt within the recycler.
- local hitbox = recyclerModel:FindFirstChild("RecycleHitbox") -- Finds the touch hitbox within the recycler.
- local connections = {}
- if prompt then
- -- Connects the ProximityPrompt's Triggered event to the `sellLastCarriedPaper` function.
- connections.Prompt = prompt.Triggered:Connect(function() sellLastCarriedPaper(player) end)
- end
- if hitbox then
- -- Connects the hitbox's Touched event to the `sellTouchedPaper` function.
- connections.Touch = hitbox.Touched:Connect(function(hit)
- -- Validates if the touched part is a valid paper instance.
- if hit and hit.Parent and hit:IsDescendantOf(papersFolder) and hit:GetAttribute("GUID") then
- sellTouchedPaper(hit)
- end
- end)
- end
- promptConnections[player] = connections -- Stores the created connections.
- end
- -- This function sets up a player when they join the game.
- -- It initializes player-specific data tables, connects attribute change signals to clear caches, and sets up selling triggers.
- local function onPlayerAdded(player)
- -- Create tables to store their data.
- serverCarriedPapers[player] = {} -- Creates an empty paper stack table for the player.
- paperCapacityCache[player] = nil -- Clears player-specific caches.
- speedReductionCache[player] = nil
- sellCooldowns[player] = nil
- -- If they upgrade, we need to clear the cache.
- -- Connects signals to clear caches when "CarryCapacity" or "CarryBalance" attributes change.
- player:GetAttributeChangedSignal("CarryCapacity"):Connect(function() clearCachesOnUpgrade(player, "CarryCapacity") end)
- player:GetAttributeChangedSignal("CarryBalance"):Connect(function() clearCachesOnUpgrade(player, "CarryBalance") end)
- task.spawn(connectSellTriggersForPlayer, player) -- Spawns a new task to set up the player's selling triggers.
- player.CharacterAdded:Connect(function(character) -- Connects to the CharacterAdded event (triggered when a player's character spawns or respawns).
- forceDropAll(player) -- Forces dropping any leftover papers from a previous life (for a clean start).
- local humanoid = character:WaitForChild("Humanoid")
- humanoid.Died:Connect(function() -- Connects to the Humanoid.Died event (triggered when the player's character dies).
- forceDropAll(player) -- Ensures papers are dropped upon death.
- end)
- end)
- end
- -- This function cleans up data when a player leaves the game.
- -- It ensures all associated data and connections are cleared to prevent memory leaks and unnecessary processing.
- local function onPlayerRemoving(player)
- forceDropAll(player) -- Forces dropping all carried papers before the player leaves.
- -- Clear all data associated with the player.
- serverCarriedPapers[player] = nil
- originalPlayerSpeeds[player] = nil
- sellCooldowns[player] = nil
- paperCapacityCache[player] = nil
- speedReductionCache[player] = nil
- -- Disconnects and cleans up selling trigger connections.
- if promptConnections[player] then
- if promptConnections[player].Prompt then promptConnections[player].Prompt:Disconnect() end
- if promptConnections[player].Touch then promptConnections[player].Touch:Disconnect() end
- promptConnections[player] = nil
- end
- end
- -- Connect the functions to game events.
- -- Binds the `onPlayerAdded` and `onPlayerRemoving` functions to the respective player events.
- Players.PlayerAdded:Connect(onPlayerAdded)
- Players.PlayerRemoving:Connect(onPlayerRemoving)
- -- Set up for any players who are already in the game when the script runs.
- -- This loop ensures that `onPlayerAdded` is called for any players already present when the script is initialized,
- -- which is important for hot reloads or if the script starts late.
- for _, player in ipairs(Players:GetPlayers()) do
- task.spawn(onPlayerAdded, player) -- Spawns a new task to set up each existing player.
- end
Advertisement
Add Comment
Please, Sign In to add comment