Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local WormManager = {}
- -- Get required services
- local CollectionService = game:GetService("CollectionService")
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
- local DataStoreService = game:GetService("DataStoreService")
- local Players = game:GetService("Players")
- local DWPevent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("DoubleWormPotionEvent")
- local MarketPlaceService = game:GetService("MarketplaceService")
- local ServerStorage = game:GetService("ServerStorage")
- local wormStore = DataStoreService:GetDataStore("PlayerWorms")
- local teleportModule = require(script.Parent.TeleportHandler)
- -- Golden Worm function
- local FoundGoldenWorm = ReplicatedStorage.Functions.FoundGoldenWorm
- -- events
- local eventsFolder = ReplicatedStorage.Events
- local uiEvent = eventsFolder.UIEvent
- local pickUpEvent = ReplicatedStorage.Events.PickUpSound
- local wormCountDown = ReplicatedStorage.Events.WormCountdown
- -- positioning for worms
- local random = Random.new()
- -- store of bools if number of worms has changed
- local dataChanged = {}
- local playerWorms = {}
- -- for storing players with double worm potion or gamepass
- local playersWithPotionActive = {}
- local playersWithGamepass = {}
- -- worm values
- local WORM_VALUE = 1
- local GOLDEN_WORM_VALUE = 200
- -- xyz coords for terrain to spawn worms
- local areaForWorms = workspace["Dig site mountain"].AreaForWorms
- local cf = areaForWorms.CFrame
- local size = areaForWorms.Size
- -- Calculate bounds
- local WORM_GENERATION_BOUNDS = {
- minX = cf.X - size.X/2,
- maxX = cf.X + size.X/2,
- minZ = cf.Z - size.Z/2,
- maxZ = cf.Z + size.Z/2,
- minY = cf.Y - size.Y/2,
- maxY = cf.Y + size.Y/2
- }
- areaForWorms:Destroy()
- -- when re adding worm >> collectedWorms[worm] = nil
- -- how often worm count should be saved
- local AUTOSAVE_INTERVAL = 120
- -- used to keep track of worms to stop multiple touched fires unintentionally giving more than one worm
- local collectedWorms = {}
- -- worm templates
- local worm = workspace.Worms.Worm
- local goldWorm = workspace.Worms.Golden_Worm
- -- gamepass stuff
- local purchasedGamePass = eventsFolder.PurchasedGamePass
- local GAMEPASS_ID = 1261762621
- -- for reseting the dig area
- local wormResetFolder = ServerStorage.WormResetObjects
- local Terrain = workspace.Terrain
- local AreaForResetTp = workspace["Dig site mountain"].AreaForResetTP
- -- area for top worms on server leaderboard
- local textOfLeaderBoard = workspace:WaitForChild("LeaderBoards"):WaitForChild("TWLeaderboard"):WaitForChild("SurfaceGui"):WaitForChild("TextLabel")
- -- cache for player names
- local playerNameCache = {}
- -- function to find spawn position of worm and spawn
- local function findSpawnPosition(newWorm)
- local hitbox = newWorm:FindFirstChild("Hitbox")
- if hitbox and hitbox:IsA("BasePart") then
- newWorm.PrimaryPart = hitbox
- newWorm.Parent = workspace.Worms.CopyOfWorms
- -- finding random position to spawn worm at
- local newPos = Vector3.new(
- random:NextInteger(WORM_GENERATION_BOUNDS.minX, WORM_GENERATION_BOUNDS.maxX),
- random:NextInteger(WORM_GENERATION_BOUNDS.minY, WORM_GENERATION_BOUNDS.maxY),
- random:NextInteger(WORM_GENERATION_BOUNDS.minZ, WORM_GENERATION_BOUNDS.maxZ)
- )
- -- Use SetPrimaryPartCFrame instead of MoveTo to set exact position
- newWorm:SetPrimaryPartCFrame(CFrame.new(newPos))
- end
- end
- -- spawining worms function
- local function spawnWorms()
- for i = 1, 2000 do
- -- clones worms
- local newWorm = worm:Clone()
- findSpawnPosition(newWorm)
- -- spawn golden worms for every three hundered worms
- if (i % 300) == 0 then
- local newWorm = goldWorm:Clone()
- findSpawnPosition(newWorm)
- end
- end
- end
- -- get players in dig area
- local function getPlayersInsideDigArea(area)
- -- get dimensions of part parsed (part encompassing dig area)
- local regionMin = (area.CFrame * CFrame.new(-area.Size / 2)).Position
- local regionMax = (area.CFrame * CFrame.new(area.Size / 2)).Position
- -- loop through all players and storea and return ones who are in those dimensions
- local playersInside = {}
- for _, player in pairs(game.Players:GetPlayers()) do
- local character = player.Character
- if character and character:FindFirstChild("HumanoidRootPart") then
- local pos = character.HumanoidRootPart.Position
- if pos.X >= math.min(regionMin.X, regionMax.X) and pos.X <= math.max(regionMin.X, regionMax.X)
- and pos.Y >= math.min(regionMin.Y, regionMax.Y) and pos.Y <= math.max(regionMin.Y, regionMax.Y)
- and pos.Z >= math.min(regionMin.Z, regionMax.Z) and pos.Z <= math.max(regionMin.Z, regionMax.Z) then
- table.insert(playersInside, player)
- end
- end
- end
- return playersInside
- end
- -- worm spawner
- local function wormSpawnAndReset()
- -- infinitely call
- while true do
- -- spawn worms
- spawnWorms()
- -- every 15 minutes fire countdown
- task.wait(895)
- wormCountDown:FireAllClients()
- task.wait(5)
- -- destroy all current worm clones
- for _, worm in ipairs(workspace.Worms.CopyOfWorms:GetChildren()) do
- worm:Destroy()
- end
- -- clone grass and ground parts
- local grassClone = wormResetFolder.Grass
- local groundClone = wormResetFolder.Ground
- -- get players to tp and send them back to the lobby
- local playersToTP = getPlayersInsideDigArea(AreaForResetTp)
- for _, Aplayer in ipairs(playersToTP) do
- teleportModule.teleportPlayerToLobby(Aplayer)
- end
- grassClone.Parent = workspace
- groundClone.Parent = workspace
- -- get info from ground
- local groundCFrame = groundClone.CFrame
- local groundSize = groundClone.Size
- --get info from grass
- local grassCFrame = grassClone.CFrame
- local grassSize = grassClone.Size
- -- fill aresa with grass and ground (cframe, size, material)
- Terrain:FillBlock(groundCFrame, groundSize, Enum.Material.Ground)
- Terrain:FillBlock(grassCFrame, grassSize, Enum.Material.Grass)
- -- delete parts
- groundClone:Destroy()
- grassClone:Destroy()
- end
- end
- -- spawn worms on loop in anoter thread
- task.spawn(wormSpawnAndReset)
- -- function for updating ui
- local function updateWormUI(player, wormAmount, doTween, showAlert)
- uiEvent:FireClient(player, {
- sentworm = wormAmount,
- doTween = doTween,
- showAlert = showAlert
- })
- end
- -- touching worms function
- local function processWormCollect(worm, wormValue, player)
- -- removes the worm to simulate it being collected
- worm.Parent = nil
- -- add worm to players worm count
- local playerID = player.UserId
- if playerWorms[playerID] then
- -- check if worm is golden by value and makes clean variables for logic
- local isGoldenWorm = wormValue == GOLDEN_WORM_VALUE
- local playerHasPotion = playersWithPotionActive[tostring(player.UserId)]
- local playerOwnsGamepass = playersWithGamepass[tostring(player.UserId)]
- -- create and change multiplier depending on what potion/gamepass
- local multiplier = 1
- if not isGoldenWorm then
- if playerHasPotion and playerOwnsGamepass then
- multiplier = 4
- elseif playerHasPotion or playerOwnsGamepass then
- multiplier = 2
- end
- end
- -- update worms
- playerWorms[playerID] += wormValue * multiplier
- end
- -- update ui to reflect new worm count
- local uiValue = playerWorms[playerID] or 0
- updateWormUI(player, uiValue, true, false)
- end
- -- find worms and connect touch detection
- local goldColour = "#ffcc00"
- for _, worm in pairs(CollectionService:GetTagged("Worm")) do
- -- get hitbox of worm
- local hitbox = worm:FindFirstChild("Hitbox")
- if hitbox then
- hitbox.Touched:Connect(function(otherPart)
- -- find player for part that touched
- local partParent = otherPart.Parent
- local player = Players:GetPlayerFromCharacter(partParent)
- -- double check worm hasn't been touched before
- if player and not collectedWorms[worm] then
- collectedWorms[worm] = true
- -- send message to server if player found a golden worm
- if CollectionService:HasTag(worm, "GoldenWorm") then
- local message = "[" .. player.Name .. " has found a golden worm!]"
- ReplicatedStorage.Events.ChatEvent:FireAllClients(message, goldColour)
- FoundGoldenWorm:Invoke(player, 1)
- processWormCollect(worm, GOLDEN_WORM_VALUE, player)
- else
- -- plays sound on pickup
- pickUpEvent:FireClient(player)
- processWormCollect(worm, WORM_VALUE, player)
- end
- end
- end)
- end
- end
- -- saves a player's worm count
- local function saveData(dataStorekey, value)
- local success, err = pcall(function()
- wormStore:SetAsync(dataStorekey, value)
- end)
- if not success then
- warn("Error saving worm count")
- end
- end
- -- Auto saves player worm count when they leave the server
- function WormManager.SavePlayer(player)
- local playerID = player.UserId
- if playerWorms[playerID] then
- saveData(playerID, playerWorms[playerID])
- end
- end
- -- sets worm count when a player joins
- function WormManager.LoadPlayer(player)
- -- check at start if the player has the gamepass
- if MarketPlaceService:UserOwnsGamePassAsync(player.UserId, GAMEPASS_ID) then
- playersWithGamepass[tostring(player.UserId)] = true
- end
- local playerID = player.UserId
- -- retrive previous worm count
- local success, storedWormCount = pcall(function()
- return wormStore:GetAsync(playerID)
- end)
- if success then
- local currentWorms = storedWormCount or 0
- playerWorms[playerID] = currentWorms
- -- Initial UI update
- updateWormUI(player, currentWorms, false, false)
- -- if the player somehow dies or resets
- player.CharacterAdded:Connect(function()
- task.wait(1)
- local currentWorms = playerWorms[playerID] or 0
- --update ui
- updateWormUI(player, currentWorms, false, false)
- end)
- else
- uiEvent:FireClient(player, { showAlert = true })
- end
- -- auto-save every 60 seconds
- coroutine.wrap(function()
- while player and player.Parent do
- task.wait(AUTOSAVE_INTERVAL)
- if dataChanged[playerID] then
- saveData(playerID, playerWorms[playerID])
- dataChanged[playerID] = false
- end
- end
- end)()
- end
- -- Connect player join/leave events, potion and purchasing of gamepass
- function WormManager.Init()
- -- double potion
- DWPevent.OnServerEvent:Connect(function(player)
- -- give for 5 mins then remove potion effect of doubling worm
- playersWithPotionActive[tostring(player.UserId)] = true
- task.wait(300)
- playersWithPotionActive[tostring(player.UserId)] = nil
- end)
- -- game pass event firing
- purchasedGamePass.OnServerEvent:Connect(function(player)
- -- double check they have the game pass
- if MarketPlaceService:UserOwnsGamePassAsync(player.UserId, GAMEPASS_ID) then
- playersWithGamepass[tostring(player.UserId)] = true
- end
- end)
- Players.PlayerAdded:Connect(WormManager.LoadPlayer)
- Players.PlayerRemoving:Connect(WormManager.SavePlayer)
- end
- -- adding worms ie when buying in shop
- function WormManager.AddWorms(player, amount)
- local playerID = player.UserId
- playerWorms[playerID] = (playerWorms[playerID] or 0) + amount
- dataChanged[playerID] = true
- -- update UI
- updateWormUI(player, playerWorms[playerID], true, false)
- end
- -- when trying to buy items in shop
- function WormManager.DeductWorms(player, amount)
- local playerID = player.UserId
- if not playerWorms[playerID] then
- return false
- end
- -- check players worms against amount, if the player has enough deducts and returns true, else returns false
- if playerWorms[playerID] >= amount then
- playerWorms[playerID] -= amount
- dataChanged[playerID] = true
- -- update ui
- updateWormUI(player, playerWorms[playerID], true, false)
- return true
- else
- return false -- not enough worms
- end
- end
- -- get name of player from id
- local function getPlayerName(userId)
- if playerNameCache[userId] then
- return playerNameCache[userId]
- end
- local success, name = pcall(function()
- return Players:GetNameFromUserIdAsync(userId)
- end)
- if success and name then
- playerNameCache[userId] = name
- return name
- else
- return "Unknown"
- end
- end
- -- get top five players by worm count
- local function getTop5Players(playerWorms)
- local topList = {}
- -- Convert dictionary to sortable array
- for userId, wormCount in pairs(playerWorms) do
- local playerName = getPlayerName(userId)
- table.insert(topList, {
- PlayerName = playerName,
- Worms = wormCount
- })
- end
- -- Sort descending by worm count
- table.sort(topList, function(a, b)
- return a.Worms > b.Worms
- end)
- -- Return only the top 5
- local top5 = {}
- for i = 1, math.min(5, #topList) do
- table.insert(top5, topList[i])
- end
- return top5
- end
- -- updates teh leaderboard every 30 seconds
- local function updateLeaderBoard()
- while true do
- textOfLeaderBoard.Text = ""
- local topFive = getTop5Players(playerWorms)
- for rank, data in ipairs(topFive) do
- textOfLeaderBoard.Text = textOfLeaderBoard.Text .. string.format("%d. %s - %d\n", rank, data.PlayerName, data.Worms)
- end
- task.wait(30)
- end
- end
- -- run leaderboard updater in parallel thread
- task.spawn(updateLeaderBoard)
- return WormManager
Advertisement
Add Comment
Please, Sign In to add comment