Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- if script.ClassName == "LocalScript" then error("Run with r/, not rl/.") return end
- if workspace:FindFirstChild("NullMinesweeperServer") then error("The script is already being ran. Use the client sided script\nIf you believe this is an error, run the following:\n\"c/workspace.NullMinesweeperServer:Destroy()\"") return end
- script.Name = "NullMinesweeperServer"
- --Creates the Remote for Client to Server communication
- local mineRemote = Instance.new("RemoteEvent")
- mineRemote.Parent = game:GetService"ReplicatedStorage"
- mineRemote.Name = "MineRemote"
- --Creates a function that makes every part Smooth instead of studded
- function noOutlines(partNoOutlines)
- if partNoOutlines.ClassName == "Part" or partNoOutlines.ClassName == "WedgePart" then
- partNoOutlines.TopSurface = "SmoothNoOutlines"
- partNoOutlines.BottomSurface = "SmoothNoOutlines"
- partNoOutlines.LeftSurface = "SmoothNoOutlines"
- partNoOutlines.RightSurface = "SmoothNoOutlines"
- partNoOutlines.FrontSurface = "SmoothNoOutlines"
- partNoOutlines.BackSurface = "SmoothNoOutlines"
- else
- error('what')
- end
- end
- --Creates the ID system for placing the boards
- local boardID = {}
- for i = 1, game:GetService"Players".MaxPlayers do --[[Generates the boardID table to be of the MaxPlayers allowed in the game.
- Allows for easy customization of the Players, as well as not having unneccessary 32s and 54s]]
- boardID[i] = i
- end
- --Time for the big boy function
- function generateBoard(width, height, lmineCount, x, y, z, plr)
- --Creates two variables that will keep count of the mines
- local minesRemaining = lmineCount
- local mineCount = lmineCount
- local spacesRemaining = (width * height) - lmineCount
- local allowTouch = false -- It's a surprise tool that will help us later. (makes it so you can't uncover the spaces if lost)
- --Creates the Model for the spaces
- local minesweepTempStorage = Instance.new("Folder")
- local modelSpace = Instance.new("Model")
- modelSpace.Parent = minesweepTempStorage
- modelSpace.Name = "Space"
- local spaceCenter = Instance.new("Part")
- spaceCenter.Name = "Center"
- spaceCenter.Anchored = true
- spaceCenter.Parent = modelSpace
- noOutlines(spaceCenter)
- spaceCenter.Color = Color3.fromRGB(163,162,165)
- spaceCenter.Size = Vector3.new(4,0.5,4)
- spaceCenter.CFrame = CFrame.new(-16.5,0.25,-23.5)
- modelSpace.PrimaryPart = spaceCenter
- local spaceOutline = Instance.new("Part")
- spaceOutline.Name = "Outline"
- spaceOutline.Parent = modelSpace
- noOutlines(spaceOutline)
- spaceOutline.Color = Color3.fromRGB(99,95,98)
- spaceOutline.Size = Vector3.new(5,0.5,5)
- spaceOutline.CFrame = CFrame.new(-16.5,0.226,-23.5)
- spaceOutline.Anchored = true
- local spaceCheck = Instance.new("NumberValue")
- spaceCheck.Name = "Check"
- spaceCheck.Value = 0
- spaceCheck.Parent = modelSpace
- local spaceFlag = Instance.new("BoolValue")
- spaceFlag.Parent = modelSpace
- spaceFlag.Value = false
- spaceFlag.Name = "IsFlagged"
- local spaceClick = Instance.new("ClickDetector")
- spaceClick.Parent = spaceCenter
- spaceClick.Name = "ToFlag"
- --This will help with RNG
- math.randomseed(tick())
- --Generating the Array. First step towards the board generation
- local board = {}
- for i = 1, height do
- local newRow = {} -- This is an array for each new row
- for j = 1, width do
- if mineCount > 0 then -- If there is still meant to be mines
- if math.random(1,10) == 1 then -- use RNG to see if one should be placed
- table.insert(newRow,j,10)
- mineCount = mineCount - 1
- else -- If RNG fails, don't place a mine.
- table.insert(newRow,j,0)
- end
- else -- If there shouldn't be anymore mines, just place an empty space
- table.insert(newRow,j,0)
- end
- end
- table.insert(board,i,newRow) -- Insert the generated mode into the board. Repeat this "height" times
- end
- --If there still needs to be mines, it'll create them here.
- while mineCount ~= 0 do
- for n,i in ipairs(board) do
- for m,j in ipairs(board[n]) do
- if board[n][m] ~= 10 then -- Check if the space is not a mine already
- if math.random(1,15) == 1 then -- If not, go through with RNG. Tougher this time.
- board[n][m] = 10 -- Replaces board[n][m] with a mine.
- mineCount = mineCount - 1
- end
- end
- if mineCount == 0 then
- break
- end
- end
- if mineCount == 0 then -- Double break because one break only stops part of it
- break
- end
- end
- end
- -- Check for Mines and put numbers next to them
- for n,i in ipairs(board) do
- for m,j in ipairs(board[n]) do
- if board[n][m] == 10 then -- If there is a mine, execute all of the following. Each direction shows where it goes towards below.
- --Left
- if m-1 ~= 0 then
- if board[n][m-1] ~= 10 then
- board[n][m-1] = board[n][m-1] + 1
- end
- end
- --Right
- if m+1 <= width then
- if board[n][m+1] ~= 10 then
- board[n][m+1] = board[n][m+1] + 1
- end
- end
- --Up
- if n-1 ~= 0 then
- if board[n-1][m] ~= 10 then
- board[n-1][m] = board[n-1][m] + 1
- end
- end
- --Down
- if n+1 <= height then
- if board[n+1][m] ~= 10 then
- board[n+1][m] = board[n+1][m] + 1
- end
- end
- --Up Left
- if n-1 ~= 0 and m-1 ~= 0 then
- if board[n-1][m-1] ~= 10 then
- board[n-1][m-1] = board[n-1][m-1] + 1
- end
- end
- --Up Right
- if n-1 ~= 0 and m+1 <= width then
- if board[n-1][m+1] ~= 10 then
- board[n-1][m+1] = board[n-1][m+1] + 1
- end
- end
- --Down Left
- if n+1 <= height and m-1 ~= 0 then
- if board[n+1][m-1] ~= 10 then
- board[n+1][m-1] = board[n+1][m-1] + 1
- end
- end
- --Down Right
- if n+1 <= height and m+1 <= width then
- if board[n+1][m+1] ~= 10 then
- board[n+1][m+1] = board[n+1][m+1] + 1
- end
- end
- end
- end
- end
- -- Now create the actual board
- local physicalBoard = Instance.new("Model") -- Creates a model to hold the board
- physicalBoard.Name = "Board"
- physicalBoard.Parent = workspace
- for n,i in ipairs(board) do
- for m,j in ipairs(board[n]) do
- local newSpace = minesweepTempStorage.Space:Clone() -- Clone the space.
- local numCheck = newSpace.Check -- Get the number container for where it's meant to be.
- newSpace.Parent = physicalBoard -- The Clone is put into the Board Model (in the workspace)
- newSpace:SetPrimaryPartCFrame(CFrame.new(x+(n*5),y,z+(m*5))) --Place it 5 away from the other spaces
- numCheck.Value = board[n][m] --Assign the number container with the corrisponding value
- newSpace.Center.ToFlag.MouseClick:Connect(function(playerClick) -- To Flag Stuff
- if playerClick == plr then -- Makes it so other people can mess stuff up
- if newSpace.IsFlagged.Value then -- If it is flagged, unflag it.
- newSpace.IsFlagged.Value = false
- newSpace.Center.Color = Color3.fromRGB(163,162,165)
- if numCheck.Value == 10 then -- This will check if you flagged the right space, and will adjust the mines remaining after.
- minesRemaining = minesRemaining + 1
- end
- else -- If it isn't flagged, flag it.
- newSpace.IsFlagged.Value = true
- newSpace.Center.Color = Color3.new(1,0,0)
- if numCheck.Value == 10 then -- If it was a mine, reduce the mines remaining.
- minesRemaining = minesRemaining - 1
- end
- end
- end
- end)
- newSpace.Center.Touched:Connect(function(part) -- If they touched a space, clear it.
- if ((part.Parent.ClassName == "Model" and part.Parent.Name == plr.Character.Name) or (part.Name == "0Detect"..plr.Character.Name)) and allowTouch then -- If you are a model, are named the player playing, AND are allowed to touch stuff (disabled when lost), it will allow.
- if not newSpace.IsFlagged.Value then -- Checks if the space isn't flagged
- newSpace.Center:Destroy() -- Destroys the center. Prevents the Touched part to run indefinetly
- spacesRemaining = spacesRemaining - 1
- if numCheck.Value == 0 then -- If its 0, make it semi-transparent
- newSpace.Outline.Transparency = 0.75
- --Place a block that will clear everything around it.
- local clearBlock = Instance.new("Part")
- clearBlock.Parent = physicalBoard
- clearBlock.Size = Vector3.new(10,10,10)
- clearBlock.CFrame = newSpace.Outline.CFrame --Set the CFrame to be where the space that was touched was.
- clearBlock.Transparency = 1
- clearBlock.Name = "0Detect"..plr.Character.Name --Name it like the Player so it doesn't activate anything else on accident, or anything else doesn't activate it.
- clearBlock.CanCollide = false
- wait()
- clearBlock:Destroy()
- --Colors each one
- --1 : RGB(0,0,255)
- --2 : RGB(0,255,0)
- --3 : RGB(255,0,0)
- --4 : RGB(107,50,124)
- --5 : RGB(117,0,0)
- --6 : RGB(64,224,208)
- --7 : RGB(0,0,0)
- --8 : RGB(99,95,98)
- --10 : RGB(255,255,255)
- elseif numCheck.Value == 1 then
- newSpace.Outline.Color = Color3.fromRGB(0,0,255)
- --newSpace.Outline.SurfaceGui.Number.Text = "1"
- elseif numCheck.Value == 2 then
- newSpace.Outline.Color = Color3.fromRGB(0,255,0)
- --newSpace.Outline.SurfaceGui.Number.Text = "2"
- elseif numCheck.Value == 3 then
- newSpace.Outline.Color = Color3.fromRGB(255,0,0)
- --newSpace.Outline.SurfaceGui.Number.Text = "3"
- elseif numCheck.Value == 4 then
- newSpace.Outline.Color = Color3.fromRGB(107,50,124)
- --newSpace.Outline.SurfaceGui.Number.Text = "4"
- elseif numCheck.Value == 5 then
- newSpace.Outline.Color = Color3.fromRGB(117,0,0)
- --newSpace.Outline.SurfaceGui.Number.Text = "5"
- elseif numCheck.Value == 6 then
- newSpace.Outline.Color = Color3.fromRGB(64,224,208)
- --newSpace.Outline.SurfaceGui.Number.Text = "6"
- elseif numCheck.Value == 7 then
- newSpace.Outline.Color = Color3.fromRGB(0,0,0)
- --newSpace.Outline.SurfaceGui.Number.Text = "7"
- elseif numCheck.Value == 8 then
- newSpace.Outline.Color = Color3.fromRGB(99,95,98)
- --newSpace.Outline.SurfaceGui.Number.Text = "8"
- elseif numCheck.Value == 10 then -- Oh hecc, you stepped on a mine!
- spacesRemaining = spacesRemaining + 1 -- Make sure it doesn't trigger the Win sequence
- newSpace.Outline.Color = Color3.fromRGB(255,255,255)
- allowTouch = false -- The player can no longer interact with the board.
- mineRemote:FireClient(plr,"Recieving",nil) -- Grab the ID from the player.
- for _,i in ipairs(physicalBoard:GetChildren()) do -- Highlight all the mines in green
- if i.ClassName == "Model" then
- if i.Check.Value == 10 then
- if i ~= newSpace then -- This checks if it ISN'T the current space, as it will break everything because the Center doesn't exist.
- i.Center.Color = Color3.new(0,1,0)
- --i.Center.ToFlag:Destroy()
- end
- end
- end
- end
- wait(5) -- Wait a little for the player to see the mines
- physicalBoard:Destroy()
- end
- if spacesRemaining == 0 then
- for _,i in ipairs(physicalBoard:GetChildren()) do -- Mark all of the mines with green.
- if i.ClassName == "Model" then
- if i.Check.Value == 10 then
- i.Center.Color = Color3.new(0,1,0)
- i.Center.ToFlag:Destroy()
- end
- end
- end
- wait(10) -- Wait 10 for the person who won to see their work.
- mineRemote:FireClient(plr,"Recieving",nil) -- Grab the ID they were given back.
- wait(1)
- physicalBoard:Destroy() -- Destroy the board
- end
- end
- end
- end)
- end
- end
- --Create Overhead stuff to start
- local platformBegin = Instance.new("Part") --Creates a platform the player can stand on to move raround to find a starting spot.
- platformBegin.Parent = physicalBoard
- platformBegin.Size = Vector3.new(height*5,0.5,width*5)
- platformBegin.Position = Vector3.new(x+(2.5*height),y+10,z+(2.5*width))
- platformBegin.Anchored = true
- platformBegin.Transparency = 1
- wait(0.5)
- plr.Character.HumanoidRootPart.CFrame = CFrame.new(x+(2.5*height),y+13,z+(2.5*width)) -- Teleports the player
- wait(0.25)
- local touchToBegin = Instance.new("Part") -- Creates the Overhead part that is used to start the game.
- touchToBegin.Parent = physicalBoard
- touchToBegin.Size = Vector3.new(height*5,0.5,width*5)
- touchToBegin.Position = Vector3.new(x+(2.5*height),y+18,z+(2.5*width))
- touchToBegin.Anchored = true
- touchToBegin.Transparency = 1
- touchToBegin.Touched:Connect(function(part) -- If the player jumped, start the game.
- if part.Parent.ClassName == "Model" and part.Parent.Name == plr.Character.Name then
- touchToBegin:Destroy()
- platformBegin:Destroy()
- end
- end)
- minesweepTempStorage:Destroy() -- Destroys the inital part that was cloned to create the board
- allowTouch = true
- plr.Character:FindFirstChildOfClass("Humanoid").Died:Connect(function() -- Failsafe in case the player dies
- physicalBoard:Destroy() -- Destroy the board
- end)
- plr.Chatted:Connect(function(msg)
- if msg:sub(1,4):lower() == "/m r" then
- physicalBoard:Destroy()
- elseif msg:sub(1,4):lower() == "/m t" then
- allowTouch = false
- plr.Character.HumanoidRootPart.CFrame = CFrame.new(x+(2.5*height),y+13,z+(2.5*width)) -- Teleports the player
- wait(5)
- allowTouch = true
- end
- end)
- end
- mineRemote.OnServerEvent:Connect(function(plrRec, action, w, h, m, plrID) -- OH BOY SERVER TO CLIENT COMMUNICATION
- if action == "Board" then -- If the client wants to create the board
- generateBoard(w,h,m, --[[plrID*(w*5+5)]]0,plrID*20-19,h, plrRec) --Create the board
- elseif action == "ID" then -- The client wants to give an ID
- boardID[plrID] = plrID --Takes the player ID and puts it back to be used
- elseif action == "Grab" then -- If the Client requests an ID
- for n,i in ipairs(boardID) do --Checks every spot in the board
- if boardID[n] ~= 0 then -- If the ID is available
- mineRemote:FireClient(plrRec,"Sending",boardID[i]) -- Send the ID to the player
- boardID[n] = 0 -- Set that ID to be nothing so it isn't used again
- break -- break the loop
- end
- end
- end
- end)
- -- OH BOI LOCALSCRIPT TIME
- local localscript = NLS([=[--alright minesweeper time
- --kill me
- --[[ This is grabbing the play and waiting for controls and stuff.
- mostly just mobile stuff but whatever]]--
- local plr = game:GetService"Players".LocalPlayer -- Grabs player
- local chr = plr.Character -- Grabs the player's Model
- if workspace:FindFirstChild(plr.Name.."MinesweeperClient") then error("The local is already being ran. If you believe this is an error, run the following:\n\"c/owner.Character:FindFirstChild(plr.Name..\"MinesweeperClient\"):Destroy()\"") return end
- local storage = game:GetService"ReplicatedStorage" -- Grab the ReplicatedStorage which contains the RemoteEvent
- local remote = storage.MineRemote -- Grab the RemoteEVent
- script.Name = plr.Name.."MinesweeperClient"
- local idForPlr = 0 -- This will be grabbed later. It will help in placing the boards
- local ignoreMessages = false
- local widthChatRequest, heightChatRequest, mineChatRequest = 2, 2, 1 -- This will come in the "Chatted" Event
- local beginner, intermediate, expert = { -- The width, height, and mine count for each difficulty
- ["width"] = 8,
- ["height"] = 8,
- ["mines"] = 10
- },{
- ["width"] = 16,
- ["height"] = 16,
- ["mines"] = 40
- },{
- ["width"] = 16,
- ["height"] = 30,
- ["mines"] = 99
- }
- function roundNum(num)
- local baseNum, newNum
- if num > 0 then
- baseNum = math.floor(num)
- newNum = num - baseNum
- if newNum >= 0.5 then
- return math.ceil(num)
- elseif newNum < 0.5 then
- return math.floor(num)
- end
- else
- baseNum = math.ceil(num)
- newNum = num - baseNum
- if newNum > -0.5 then
- return math.ceil(num)
- elseif newNum <= -0.5 then
- return math.floor(num)
- end
- end
- end
- function returnID()
- remote:FireServer("ID",0,0,0,idForPlr) -- Send the ID
- idForPlr = 0
- end
- plr.Chatted:Connect(function(msg)
- if msg:sub(1,3):lower() == "/m " then
- local cmd = msg:sub(4)
- if not ignoreMessages then
- ignoreMessages = true
- if cmd:sub(1,1) == "b" then
- widthChatRequest, heightChatRequest, mineChatRequest = beginner.width, beginner.height, beginner.mines -- Sets beginner difficulty
- remote:FireServer("Grab", nil, nil, nil, nil) -- Requests an ID
- elseif cmd:sub(1,1) == "i" then
- widthChatRequest, heightChatRequest, mineChatRequest = intermediate.width, intermediate.height, intermediate.mines -- Sets intermediate difficulty
- remote:FireServer("Grab", nil, nil, nil, nil) -- Requests an ID
- elseif cmd:sub(1,1) == "e" then
- widthChatRequest, heightChatRequest, mineChatRequest = expert.width, expert.height, expert.mines -- Sets expert difficulty
- remote:FireServer("Grab", nil, nil, nil, nil) -- Requests an ID
- elseif cmd:sub(1,1) == "c" then -- Custom Board
- local customVariables = cmd:sub(2) -- grab ONLY the custom variables
- local count = 0 -- This will help in sorting the request
- for i in customVariables:gmatch("%S+") do -- Splits the variables into their own seperate parts
- count = count + 1 -- Adds one to the count, which changes which part to change about the board
- local a = tonumber(i) -- Changes the string to a number
- if count == 1 then -- 1 is requesting width
- if a <= 32 then
- widthChatRequest = a
- elseif a < 2 then
- error("There were less than 2 width in the field. Please try again")
- else
- warn("The width of the board is too big! Setting to 32...")
- widthChatRequest = 32
- end
- elseif count == 2 then -- 2 is requesting height
- if a <= 32 then
- heightChatRequest = a
- elseif a < 2 then
- error("There were less than 2 height in the field. Please try again")
- else
- warn("The height of the board is too big! Setting to 32...")
- heightChatRequest = 32
- end
- elseif count == 3 then -- 3 is requesting mine count
- local maxMines = roundNum((widthChatRequest * heightChatRequest) / 1.25) -- This is to make it so there isn't more mines than the board itself
- if a <= maxMines then
- mineChatRequest = a
- elseif a < 0 then
- error("There were less than 1 mine in the field. Please try again")
- else
- warn("The mine count is too much! Setting to "..maxMines.."...")
- mineChatRequest = maxMines
- end
- end
- end
- remote:FireServer("Grab", nil, nil, nil, nil) -- Requests the ID
- end
- else
- if cmd:sub(1,1) == "r" then -- Restart Minesweeper
- returnID()
- ignoreMessages = false
- end
- end
- end
- end)
- remote.OnClientEvent:Connect(function(action, newID) -- Server to Client stuff
- if action == "Sending" then -- When the server gives an ID
- idForPlr = newID -- Store the ID
- remote:FireServer("Board", widthChatRequest,heightChatRequest,mineChatRequest,idForPlr) -- Then request a board
- warn("Jump to start. Do not walk off the invisible platform you are on.")
- print("0-Transparent") -- Gives the player instructions on to what each color represents
- print("1-Blue")
- print("2-Green")
- print("3-Red")
- print("4-Purple")
- print("5-Crimson")
- print("6-Turquoise")
- print("7-Black")
- print("8-Gray")
- elseif action == "Recieving" then -- When the server requests for the ID
- returnID()
- ignoreMessages = false
- end
- end)
- chr:FindFirstChildOfClass("Humanoid").Died:Connect(function() -- If the humanoid dies, return the ID. Failsafe.
- returnID()
- script:Destroy()
- end)
- print("Prefix: /m\nb, i, e run the beginner, intermediate, and expert boards respectively.\nc [w] [h] [m] to set a custom board [w]*[h] big with [m] mines.\nr to reset your board.\nt to teleport back to your board.")]=], owner.Character):WaitForChild"MineRemote"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement