-- ceat_ceat
-- missing textures and colors LOL
-- original https://pastebin.com/KAjDQugX
-- whitelist feature + some code cleanup by datz
warn("mineswep by ceat yay")
if game:GetService("RunService"):IsStudio() then
require(game.ServerStorage.lsbenv)()
owner,printf,warnf,LoadLibrary,LoadAssets,NLS=owner,printf,warnf,LoadLibrary,LoadAssets,NLS
end
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")
local DEFAULT_TILE_WIDTH = 1.5
local FIELD_RADIUS = 1
local RESTART_TIME = 3
local UNOPENED_TEX = "rbxassetid://15144071117"
local OPENED_TEX = "rbxassetid://13348346487"
local MINE_TEX = "rbxassetid://13348250535"
local FLAG_TEX = "rbxassetid://10720664742"
local BLANK_TEX = "rbxassetid://12381187927"
local NUMBER_FONT = Enum.Font.SourceSansBold
local WIN_MINE_COLOR = Color3.new(0.5, 1, 0.5)
local DIG_SOUND = "rbxassetid://3498493622"
local FLAG_SOUND = "rbxassetid://8388724806"
local CORRECT_SOUND = "rbxassetid://3422389728"
local YAY_SOUND = "rbxassetid://9068897474"
local ALARM_SOUND = "rbxassetid://2778386920"
local EXPLODE_SOUND = "rbxassetid://165969964"
local COLORS = {
Color3.fromRGB(0, 0, 255),
Color3.fromRGB(39, 107, 31),
Color3.fromRGB(255, 0, 0),
Color3.fromRGB(0, 0, 130),
Color3.fromRGB(157, 0, 0),
Color3.fromRGB(0, 214, 146),
Color3.fromRGB(98, 0, 168),
Color3.fromRGB(100, 100, 100),
}
local FLASH_TWEENINFO = TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.In)
local WIN_FLASH_TWEENINFO = TweenInfo.new(0.75, Enum.EasingStyle.Linear, Enum.EasingDirection.In)
local whitelist = false
local whitelisted = {}
local settings = {
tileWidth = DEFAULT_TILE_WIDTH,
use3DFlags = false,
width = 25,
height = 25,
mineCount = 90,
debug = false
}
local currentGame
local baseTilePart do
baseTilePart = Instance.new("Part")
baseTilePart.Anchored = true
local tileDecal = Instance.new("Decal")
tileDecal.Name = "TileTexture"
tileDecal.Texture = UNOPENED_TEX
tileDecal.Face = Enum.NormalId.Top
tileDecal.Parent = baseTilePart
local clickDetector = Instance.new("ClickDetector")
clickDetector.Name = "ClickDetector"
clickDetector.MaxActivationDistance = 30
clickDetector.Parent = baseTilePart
end
local function createTilePart(cf, tileWidth)
local part = baseTilePart:Clone()
part.CFrame = cf
part.Size = Vector3.new(tileWidth, 0.5, tileWidth)
return part
end
local baseFlagDecal do
baseFlagDecal = Instance.new("Decal")
baseFlagDecal.Name = "FlagDecal"
baseFlagDecal.Texture = FLAG_TEX
baseFlagDecal.Face = Enum.NormalId.Top
baseFlagDecal.ZIndex = 5
end
local function createFlagDecal(part)
local decal = baseFlagDecal:Clone()
decal.Parent = part
return decal
end
local baseNumberSurface, baseTileFlashDecal do
baseNumberSurface = Instance.new("SurfaceGui")
baseNumberSurface.Name = "NumberFace"
baseNumberSurface.Face = Enum.NormalId.Top
baseNumberSurface.SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud
baseNumberSurface.PixelsPerStud = 25
local label = Instance.new("TextLabel")
label.Name = "NumberLabel"
label.Font = NUMBER_FONT
label.TextScaled = true
label.BackgroundTransparency = 1
label.AnchorPoint = Vector2.new(0.5, 0.5)
label.Position = UDim2.fromScale(0.5, 0.5)
label.Size = UDim2.fromScale(2, 2)
label.TextTransparency = 1
label.Rotation = -90
label.Parent = baseNumberSurface
baseTileFlashDecal = Instance.new("Decal")
baseTileFlashDecal.Texture = BLANK_TEX
baseTileFlashDecal.Face = Enum.NormalId.Top
baseTileFlashDecal.ZIndex = 5
baseTileFlashDecal.Transparency = 0
baseTileFlashDecal.Parent = baseNumberSurface
end
local function displayNumber(tile)
local surfaceGui = baseNumberSurface:Clone()
local label = surfaceGui.NumberLabel
local flashDecal = baseTileFlashDecal:Clone()
label.Text = tostring(tile.minesAround)
label.TextColor3 = COLORS[tile.minesAround]
flashDecal.Parent = tile.part
label.Parent = surfaceGui
surfaceGui.Parent = tile.part
TweenService:Create(flashDecal, FLASH_TWEENINFO, { Transparency = 1 }):Play()
TweenService:Create(label, TweenInfo.new(0.2), {
TextTransparency = 0
}):Play()
TweenService:Create(label, TweenInfo.new(0.2 + math.random()*0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {
Size = UDim2.fromScale(1, 1)
}):Play()
Debris:AddItem(flashDecal, FLASH_TWEENINFO.Time)
end
local function iterTiles(tiles)
local width = #tiles
local height = #tiles[1]
local x = 1
local y = 1
return function()
if x > width then
x = 1
y += 1
if y > height then
return
end
end
local x1 = x
x += 1
return x1, y, tiles[x1][y]
end
end
local function disableTile(tile)
tile.enabled = false
tile.leftClickConnection:Disconnect()
tile.rightClickConnection:Disconnect()
tile.leftClickConnection = nil
tile.rightClickConnection = nil
tile.clickDetector:Destroy()
end
local function unflagTile(tile)
if tile.flagInstance then
tile.flagInstance:Destroy()
tile.flagInstance = nil
end
end
local function getTilesToFlash(tiles, x, y, offset)
local flashyGuys = {}
-- top left -> top right
for xOff = -offset, offset do
local tile = tiles[x - xOff] and tiles[x - xOff][y - offset]
if tile then
table.insert(flashyGuys, tile)
end
end
-- bottom left -> bottom right
for xOff = -offset, offset do
local tile = tiles[x + xOff] and tiles[x + xOff][y + offset]
if tile and not table.find(flashyGuys, tile) then
table.insert(flashyGuys, tile)
end
end
-- top left -> bottom left
if tiles[x - offset] then
for yOff = -offset, offset do
local tile = tiles[x - offset][y + yOff]
if tile and not table.find(flashyGuys, tile) then
table.insert(flashyGuys, tile)
end
end
end
-- top right -> bottom right
if tiles[x + offset] then
for yOff = -offset, offset do
local tile = tiles[x + offset][y + yOff]
if tile and not table.find(flashyGuys, tile) then
table.insert(flashyGuys, tile)
end
end
end
return flashyGuys
end
local function flashTilesWave(tiles, tile, width, height, winner, color)
local baseX = tile.x
local baseY = tile.y
for i = 0, math.max(width, height) do
for _, tile in getTilesToFlash(tiles, baseX, baseY, i) do
if tile.isFlagged then
tile.isFlagged = false
unflagTile(tile)
end
if winner then
tile.tileTexture.Texture = OPENED_TEX
else
if tile.isMine then
tile.tileTexture.Texture = MINE_TEX
end
end
tile.tileTexture.Color3 = color
if not tile.isMine then
TweenService:Create(tile.tileTexture, WIN_FLASH_TWEENINFO, { Color3 = Color3.new(1, 1, 1) }):Play()
end
end
task.wait()
end
end
local function playSound(part, soundId, pitch)
local sound = Instance.new("Sound")
sound.SoundId = soundId
sound.Pitch = pitch
sound.Parent = part
sound:Play()
Debris:AddItem(sound, 4)
return sound
end
local function isWhitelisted(plr)
if whitelist and not whitelisted[plr.UserId] and plr ~= owner then
return false
end
return true
end
-- debug visuals
local function highlightTile(tile, color)
local part = Instance.new("Part")
part.Anchored = true
part.CanCollide = false
part.CanTouch = false
part.CanQuery = false
part.Material = Enum.Material.Neon
part.Color = color
part.Size = Vector3.new(settings.tileWidth, 0.7, settings.tileWidth)
part.Transparency = 0.8
part.CFrame = tile.part.CFrame
return part
end
local function showNeighbors(tiles, tile)
local main = highlightTile(tile, Color3.new(1, 0, 0))
main.Parent = script
local neighborParts = {}
for _, position in tile.neighbors do
local part = highlightTile(tiles[position.x][position.y], Color3.new(0, 0, 1))
part.Parent = script
table.insert(neighborParts, part)
end
task.wait()
main:Destroy()
for _, part in neighborParts do
part:Destroy()
end
end
-- game wow
local restartTask
local function restart(spawnCF, width, height, numMines)
restartTask = task.delay(RESTART_TIME, function()
restartTask = nil
stopGame(currentGame)
currentGame = createGame(spawnCF, width, height, numMines)
end)
end
function createGame(spawnCF, width, height, numMines)
local gameObject = {}
local gameSettings = table.clone(settings)
gameObject.settings = gameSettings
local model = Instance.new("Model")
model.Name = "Minesweeper"
gameObject.model = model
local tiles = {}
gameObject.tiles = tiles
local active = true
local didFirstClick = false
local function layMines(clickedTile)
local validPositions = {}
-- filter out start field
for x, y, tile in iterTiles(tiles) do
if math.abs(x - clickedTile.x) <= FIELD_RADIUS and math.abs(y - clickedTile.y) <= FIELD_RADIUS then
continue
end
table.insert(validPositions, { x = x, y = y })
end
for i = 1, math.min(numMines, #validPositions) do
local pos = table.remove(validPositions, math.random(#validPositions))
local tile = tiles[pos.x][pos.y]
tile.isMine = true
if gameSettings.debug then
tile.tileTexture.Color3 = Color3.new(1, 0.5, 0.5)
end
end
-- count mines around tiles
for x, y, tile in iterTiles(tiles) do
for _, position in tile.neighbors do
if tiles[position.x][position.y].isMine then
tile.minesAround += 1
end
end
if gameSettings.debug then
showNeighbors(tiles, tile)
end
end
end
local function onLose(plr, tile)
active = false
for x, y, tile in iterTiles(tiles) do
if tile.enabled then
disableTile(tile)
end
if tile.isMine then
tile.tileTexture.Texture = MINE_TEX
end
end
tile.part.CFrame *= CFrame.new(0, 1, 0)
tile.part.Anchored = false
tile.part.Velocity = Vector3.new(-3 + math.random()*6, math.random(20, 30), -3 + math.random()*6)
tile.part.RotVelocity = Vector3.new(math.random(-10, 10), math.random(-10, 10), math.random(-10, 10))
playSound(tile.part, ALARM_SOUND, 1).TimePosition = 0.35
playSound(tile.part, EXPLODE_SOUND, 1)
local explod = Instance.new("Explosion")
explod.BlastPressure = 0
explod.BlastRadius = 0
explod.Position = tile.part.Position
explod.Parent = workspace
flashTilesWave(tiles, tile, width, height, false, Color3.new(1, 0.5, 0.5))
restart(spawnCF, width, height, numMines)
warn(`{plr} lost the game`)
end
local function onWin(tile)
for x, y, tile in iterTiles(tiles) do
if tile.enabled then
disableTile(tile)
end
end
playSound(tile.part, CORRECT_SOUND, 1)
playSound(tile.part, YAY_SOUND, 1)
flashTilesWave(tiles, tile, width, height, true, WIN_MINE_COLOR)
restart(spawnCF, width, height, numMines)
end
local mineTile -- scream
local function chordTile(plr, tile)
local unflaggedTiles = {}
local numFlagged = 0
for _, position in tile.neighbors do
local otherTile = tiles[position.x][position.y]
if otherTile.opened then continue end
if otherTile.isFlagged then
numFlagged += 1
else
table.insert(unflaggedTiles, otherTile)
end
end
if numFlagged == tile.minesAround then
for _, otherTile in unflaggedTiles do
mineTile(plr, otherTile)
end
end
end
function mineTile(plr, tile)
if not active then
return
end
if tile.isMine then
onLose(plr, tile)
return
end
if tile.opened then
chordTile(plr, tile)
return
end
if tile.isFlagged then
tile.isFlagged = false
unflagTile(tile)
end
tile.opened = true
tile.tileTexture.Texture = OPENED_TEX
if tile.minesAround == 0 then
disableTile(tile)
for _, position in tile.neighbors do
local otherTile = tiles[position.x][position.y]
if otherTile.opened then continue end
mineTile(plr, otherTile)
end
else
displayNumber(tile)
end
end
local function checkWin()
local numNonMines = 0
local numOpened = 0
for x, y, tile in iterTiles(tiles) do
if not tile.isMine then
numNonMines += 1
end
if tile.opened then
numOpened += 1
end
end
return numNonMines == numOpened
end
local function onLeftClick(tile)
return function(plr)
if not isWhitelisted(plr) then
return
end
if tile.isFlagged then
return
end
if not didFirstClick then
didFirstClick = true
layMines(tile)
end
playSound(tile.part, DIG_SOUND, 0.9 + math.random()*0.2)
mineTile(plr, tile)
if checkWin() then
onWin(tile)
end
end
end
local function onRightClick(tile)
return function(plr)
if not isWhitelisted(plr) then
return
end
if tile.opened then
return
end
tile.isFlagged = not tile.isFlagged
playSound(tile.part, FLAG_SOUND, 0.8 + math.random()*0.4)
if tile.isFlagged then
if gameSettings.use3DFlags then
-- not implemented
else
tile.flagInstance = createFlagDecal(tile.part)
end
else
unflagTile(tile)
end
end
end
local originCF = spawnCF * CFrame.new(-width/2*gameSettings.tileWidth, 0, -height/2*gameSettings.tileWidth)
for x = 1, width do
local row = {}
for y = 1, height do
local tileCF = originCF * CFrame.new(x*gameSettings.tileWidth, 0, y*gameSettings.tileWidth)
local part = createTilePart(tileCF, gameSettings.tileWidth)
--part.CanCollide = false
--part.Transparency = 1
local self = {
x = x,
y = y,
enabled = true,
opened = false,
isMine = false,
isFlagged = false,
neighbors = {}, -- {x = num, y = num}
minesAround = 0, -- decided by layMines
part = part,
tileTexture = part.TileTexture,
clickDetector = part.ClickDetector,
flagInstance = nil, -- decal or instance
leftClickConnection = nil,
rightClickConnection = nil,
}
self.leftClickConnection = self.clickDetector.MouseClick:Connect(onLeftClick(self))
self.rightClickConnection = self.clickDetector.RightMouseClick:Connect(onRightClick(self))
for xOffset = -1, 1 do
for yOffset = -1, 1 do
if xOffset == 0 and yOffset == 0 then continue end
local x2 = x + xOffset
local y2 = y + yOffset
if x2 < 1 or x2 > width then continue end
if y2 < 1 or y2 > height then continue end
table.insert(self.neighbors, { x = x2, y = y2 })
end
end
part.Parent = model
--local delayTime = (y - 1)*(width)*0.00005 + x*0.00005
--TweenService:Create(part, TweenInfo.new(0.15 + math.random()*0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {
-- Transparency = 0,
-- CFrame = tileCF,
-- CanCollide = true
--}):Play()
row[y] = self
end
tiles[x] = row
end
model.Parent = script
return gameObject
end
function stopGame(gameObject)
for x, y, tile in iterTiles(gameObject.tiles) do
if tile.enabled then
disableTile(tile)
end
end
gameObject.model:Destroy()
end
-- currentGame = createGame(CFrame.new(0, 5, 0), settings.width, settings.height, settings.mineCount)
local function getRootPos()
local humanoid = owner.Character and owner.Character:FindFirstChildOfClass("Humanoid")
if humanoid and humanoid.RootPart then
return humanoid.RootPart.CFrame.Position
end
end
local function findPlayer(target)
target = target:lower()
for _, plr in Players:GetPlayers() do
if plr.Name:lower():sub(1, #target) == target then
return plr
end
end
end
owner.Chatted:Connect(function(msg)
local cmdString = msg:match("ms/(.+)")
if not cmdString then return end
local args = cmdString:split("/")
local cmdName = table.remove(args, 1)
if cmdName == "size" then
if args[2] then
settings.width = tonumber(args[1])
settings.height = tonumber(args[2])
printf(`set size to <b>{settings.width}x{settings.height}</b>`)
else
local size = tonumber(args[1])
if not size then
return printf("<i>pls provide size</i>")
end
settings.width = size
settings.height = size
printf(`set size to <b>{size}x{size}</b>`)
end
elseif cmdName == "mines" then
local mineCount = tonumber(args[1])
if not mineCount then
return printf("<i>pls provide minecount</i>")
end
settings.mineCount = mineCount
printf(`set mine count to <b>{settings.mineCount}</b>`)
elseif cmdName == "minefreq" then
local freq = tonumber(args[1])
if not freq then
return printf("<i>pls provide freq</i>")
end
settings.mineCount = math.floor(settings.width*settings.height*freq)
printf(`set mine count to <b>{settings.mineCount}</b>`)
elseif cmdName == "game" then
local rootPos = getRootPos()
if not rootPos then
return printf("<i>could not find rootpos</i>")
end
if currentGame then
stopGame(currentGame)
end
currentGame = createGame(CFrame.new(rootPos + Vector3.new(0, -1.75, 0)), settings.width, settings.height, settings.mineCount)
printf("<i>started game</i>")
elseif cmdName == "stop" then
if restartTask then
task.cancel(restartTask)
restartTask = nil
end
if currentGame then
stopGame(currentGame)
else
printf("<i>no game active</i>")
end
elseif cmdName == "debug" then
settings.debug = not settings.debug
printf(`<i>debug {settings.debug and "enabled" or "disabled"}</i>`)
elseif cmdName == "whitelist" then
whitelist = not whitelist
printf(`<i>whitelist {whitelist and "enabled" or "disabled"}</i>`)
elseif cmdName == "allow" then
local username = tostring(args[1])
if not username then
return printf("<i>pls provide username</i>")
end
local plr = findPlayer(username)
if not plr then
return printf("<i>player not found</i>")
end
if whitelisted[plr.UserId] then
return printf("<i>player is already whitelisted</i>")
end
whitelisted[plr.UserId] = true
printf(`added <b>{plr}</b> to whitelist`)
elseif cmdName == "unallow" then
local username = tostring(args[1])
if not username then
return printf("<i>pls provide username</i>")
end
local plr = findPlayer(username)
if not plr then
return printf("<i>player not found</i>")
end
if not whitelisted[plr.UserId] then
return printf("<i>player is not whitelisted</i>")
end
whitelisted[plr.UserId] = nil
printf(`removed <b>{plr}</b> from whitelist`)
elseif cmdName == "cmds" then
printf([[
<b>ms/size/[width]/[height]</b> set board size to (width, height)
<b>ms/size/[sideLength]</b> set board size to (sideLength, sideLength)
<b>ms/mines/[mineCount]</b> set mine count to mineCount
<b>ms/minefreq/[mineFrequency]</b> set mine count to width*height*mineFrequency
<b>ms/game</b> start game
<b>ms/stop</b> end game
<b>ms/whitelist</b> toggle the whitelist
<b>ms/allow/[name]</b> add a player to the whitelist
<b>ms/unallow/[name]</b> remove a player from the whitelist
<b>ms/cmds</b> this thing]])
else
printf("<i>invalid ms/ command</i>")
end
end)
print("ms/cmds for list of commands (works with /e)")