-- ceat_ceat
-- missing textures and colors LOL
-- original https://pastebin.com/KAjDQugX
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 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 settings = {
tileWidth = DEFAULT_TILE_WIDTH,
use3DFlags = false,
width = 25,
height = 25,
mineCount = 90,
debug = false
}
local currentGame
local function createTilePart(cf, tileWidth)
local part = Instance.new("Part")
part.Anchored = true
part.CFrame = cf
part.Size = Vector3.new(tileWidth, 0.5, tileWidth)
local tileDecal = Instance.new("Decal")
tileDecal.Name = "TileTexture"
tileDecal.Texture = UNOPENED_TEX
tileDecal.Face = Enum.NormalId.Top
tileDecal.Parent = part
local clickDetector = Instance.new("ClickDetector")
clickDetector.Name = "ClickDetector"
clickDetector.MaxActivationDistance = 30
clickDetector.Parent = part
return part
end
local function createFlagDecal(part)
local decal = Instance.new("Decal")
decal.Name = "FlagDecal"
decal.Texture = FLAG_TEX
decal.Face = Enum.NormalId.Top
decal.ZIndex = 5
decal.Parent = part
return decal
end
local function displayNumber(tile)
local surfaceGui = Instance.new("SurfaceGui")
surfaceGui.Name = "NumberFace"
surfaceGui.Face = Enum.NormalId.Top
surfaceGui.SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud
surfaceGui.PixelsPerStud = 25
local label = Instance.new("TextLabel")
label.Name = "NumberLabel"
label.Text = tostring(tile.minesAround)
label.Font = NUMBER_FONT
label.TextColor3 = COLORS[tile.minesAround]
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
local flashDecal = Instance.new("Decal")
flashDecal.Texture = BLANK_TEX
flashDecal.Face = Enum.NormalId.Top
flashDecal.ZIndex = 5
flashDecal.Transparency = 0
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
-- 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 = workspace
local neighborParts = {}
for _, position in tile.neighbors do
local part = highlightTile(tiles[position.x][position.y], Color3.new(0, 0, 1))
part.Parent = workspace
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
gameObject.active = true
gameObject.didFirstClick = false
gameObject.players = {}
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)
gameObject.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)
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 gameObject.active then
return
end
if tile.isMine then
onLose(plr, tile)
return
end
if tile.opened then
chordTile(plr, tile)
return
end
if not table.find(gameObject.players, plr.Name) then
table.insert(gameObject.players, plr.Name)
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 tile.isFlagged then
return
end
if not gameObject.didFirstClick then
gameObject.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 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 = workspace
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
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 mode {settings.debug}</i>`)
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/cmds</b> this thing
]])
else
printf("<i>invalid ms/ command</i>")
end
end)
print("ms/cmds for list of commands (works with /e)")