Super-Nostalgia-Zone/Server/Scripts/Bevels.server.lua

479 lines
11 KiB
Lua

------------------------------------------------------------------------------------------------
-- Initialization
------------------------------------------------------------------------------------------------
local Debris = game:GetService("Debris")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
local function getFlag(name)
local flag = ServerStorage:FindFirstChild(name)
return (flag and flag:IsA("BoolValue") and flag.Value)
end
local enableBevels = getFlag("EnableBevels")
local debugMode = getFlag("DevTestMode")
local bevelData = Instance.new("StringValue")
bevelData.Name = "BevelData"
bevelData.Archivable = false
local bevelCache = ServerStorage:FindFirstChild("BevelCache")
local bevelsReady = bevelCache and bevelCache:FindFirstChild("BevelsReady")
if not bevelCache then
bevelCache = Instance.new("Folder")
bevelCache.Name = "BevelCache"
bevelCache.Parent = ServerStorage
end
if not bevelsReady then
bevelsReady = Instance.new("BoolValue")
bevelsReady.Name = "BevelsReady"
bevelsReady.Parent = bevelCache
bevelsReady.Archivable = false
end
if not enableBevels then
bevelData.Parent = ReplicatedStorage
bevelsReady.Value = true
return
else
local ids = {}
local idMap = {}
for _,part in pairs(bevelCache:GetChildren()) do
if part:IsA("MeshPart") then
local id = part.MeshId
if not idMap[id] then
idMap[id] = true
table.insert(ids, id)
end
end
end
bevelData.Value = table.concat(ids, ";")
bevelData.Parent = ReplicatedStorage
end
local regen = ServerStorage:FindFirstChild("Regeneration")
if regen then
local ready = regen:WaitForChild("Ready")
while not ready.Value do
ready.Changed:Wait()
end
end
local loadBuildTools = ServerStorage:FindFirstChild("LoadBuildTools")
local hasBuildTools = (loadBuildTools ~= nil)
------------------------------------------------------------------------------------------------
local edgeDepth = 1 / 15
local cornerDepth = edgeDepth * math.sqrt(8 / 3)
local mirrorProps =
{
"Anchored",
"CanCollide",
"CastShadow",
"CFrame",
"CollisionGroupId",
"CustomPhysicalProperties",
"Color",
"Locked",
"Material",
"Name",
"Reflectance",
"RotVelocity",
"Transparency",
"Velocity",
}
local surfaceProps =
{
"ParamA",
"ParamB",
"Surface",
"SurfaceInput"
}
local bevelHash = "%.2f ~ %.2f ~ %.2f"
local isStudio = RunService:IsStudio()
local negateBase = Instance.new("Part")
negateBase.Name = "__negateplane"
negateBase.CanCollide = false
negateBase.BottomSurface = 0
negateBase.Transparency = 1
negateBase.Anchored = true
negateBase.TopSurface = 0
negateBase.Locked = true
CollectionService:AddTag(negateBase, "NoBevels")
for _,normalId in pairs(Enum.NormalId:GetEnumItems()) do
local name = normalId.Name
for _,surfaceProp in pairs(surfaceProps) do
table.insert(mirrorProps, name .. surfaceProp)
end
end
------------------------------------------------------------------------------------------------
local overload = 0
local threshold = Vector3.new(30, 30, 30)
if ServerStorage:FindFirstChild("BevelThreshold") then
threshold = ServerStorage.BevelThreshold.Value
end
local function debugPrint(...)
if debugMode then
warn("[BEVELS DEBUG]:", ...)
end
end
local function isPartOfHumanoid(object)
local model = object:FindFirstAncestorOfClass("Model")
if model then
if model:FindFirstChildOfClass("Humanoid") then
return true
else
return isPartOfHumanoid(model)
end
end
return false
end
local function canGiveBevels(part)
if part.Parent and part:IsA("Part") and not CollectionService:HasTag(part, "NoBevels") then
if not isPartOfHumanoid(part) and not part:FindFirstChildWhichIsA("DataModelMesh") then
local inThreshold = false
local diff = threshold - part.Size
if diff.X >= 0 and diff.Y >= 0 and diff.Z >= 0 then
inThreshold = true
end
if inThreshold then
if CollectionService:HasTag(part, "ForceBevels") then
return true
else
return part.Shape.Name == "Block" and part.Transparency < 1
end
end
end
end
return false
end
local function createProxyPart(part, name, tag, sizeChange)
local proxyPart = Instance.new("Part")
proxyPart.Name = name
proxyPart.Locked = true
proxyPart.TopSurface = 0
proxyPart.Massless = true
proxyPart.Transparency = 1
proxyPart.BottomSurface = 0
proxyPart.CanCollide = false
proxyPart.CFrame = part.CFrame
local size = part.Size
if sizeChange then
size = size + sizeChange
end
local proxyWeld = Instance.new("Weld")
proxyWeld.Name = "ProxyWeld"
proxyWeld.Part1 = proxyPart
proxyWeld.Part0 = part
if hasBuildTools then
local mesh = Instance.new("SpecialMesh")
mesh.Scale = size * 20
mesh.MeshType = "Brick"
mesh.Offset = part.Size
mesh.Parent = proxyPart
proxyPart.Size = Vector3.new(.05, .05, .05)
proxyWeld.C0 = CFrame.new(-mesh.Offset)
else
proxyPart.Size = part.Size
end
CollectionService:AddTag(proxyPart, tag)
CollectionService:AddTag(proxyPart, "NoBevels")
CollectionService:AddTag(proxyWeld, "GorillaGlue")
proxyWeld.Parent = proxyPart
proxyPart.Parent = part
return proxyPart
end
local function createBevels(part, initializing)
if not canGiveBevels(part) or isPartOfHumanoid(part) then
return
end
local size = part.Size
local sx, sy, sz = size.X, size.Y, size.Z
local bevelKey = bevelHash:format(sx, sy, sz)
local debugBox
if debugMode then
debugBox = Instance.new("BoxHandleAdornment")
debugBox.Color3 = Color3.new(0, 2, 2)
debugBox.AlwaysOnTop = true
debugBox.Name = "DebugBox"
debugBox.Size = size
debugBox.ZIndex = 0
debugBox.Adornee = part
debugBox.Parent = part
end
if not bevelCache:FindFirstChild(bevelKey) then
local halfSize = size / 2
local planeScale = math.max(sx, sy, sz)
local planes = {}
local solverPart = part:Clone()
solverPart.CFrame = CFrame.new()
solverPart.BrickColor = BrickColor.new(-1)
debugPrint("Solving:", bevelKey)
for x = -1, 1 do
local x0 = (x == 0)
for y = -1, 1 do
local y0 = (y == 0)
for z = -1, 1 do
local z0 = (z == 0)
local isCenter = (x0 and y0 and z0)
local isFace = ((x0 and y0) or (y0 and z0) or (z0 and x0))
if not (isCenter or isFace) then
local isCorner = (not x0 and not y0 and not z0)
local depth = isCorner and cornerDepth or edgeDepth
local offset = Vector3.new(x, y, z)
local cornerPos = (halfSize * offset)
local plane = negateBase:Clone()
plane.CFrame = CFrame.new(cornerPos, cornerPos + offset)
plane.Size = Vector3.new(planeScale, planeScale, depth)
plane.Parent = part
table.insert(planes, plane)
end
end
end
end
local success, union = pcall(function ()
return solverPart:SubtractAsync(planes, "Box")
end)
if success then
union.Name = bevelKey
union.UsePartColor = true
union.Parent = bevelCache
CollectionService:AddTag(union, "HasBevels")
if debugBox then
debugBox.Color3 = Color3.new(0, 2, 0)
end
elseif debugBox then
debugBox.Color3 = Color3.new(2, 0, 0)
end
for _,plane in pairs(planes) do
plane:Destroy()
end
overload = 0
else
if debugBox then
debugBox.Color3 = Color3.new(2, 0, 2)
end
overload = overload + 1
if overload % 10 == 0 then
RunService.Heartbeat:Wait()
end
end
local baseUnion = bevelCache:FindFirstChild(bevelKey)
if baseUnion then
local archivable = baseUnion.Archivable
baseUnion.Archivable = true
local union = baseUnion:Clone()
baseUnion.Archivable = archivable
for _,prop in ipairs(mirrorProps) do
union[prop] = part[prop]
end
for _,joint in pairs(part:GetJoints()) do
if joint:IsA("JointInstance") or joint:IsA("WeldConstraint") then
if joint.Part0 == part then
joint.Part0 = union
elseif joint.Part1 == part then
joint.Part1 = union
end
end
end
for _,child in pairs(part:GetChildren()) do
if not child:IsA("TouchTransmitter") and not child:IsA("Texture") then
if child:IsA("BaseScript") then
child.Disabled = true
end
child.Parent = union
if child:IsA("BaseScript") then
child.Disabled = false
end
end
end
if not initializing then
wait()
end
if CollectionService:HasTag(part, "DoUnlock") then
union.Locked = false
end
if part.ClassName ~= "Part" then
local holder = Instance.new("Weld")
holder.Part0 = part
holder.Part1 = union
holder.Parent = part
union.Anchored = false
union.Massless = true
union.Parent = part
part.Transparency = 1
CollectionService:AddTag(holder, "GorillaGlue")
else
local parent = part.Parent
part:Destroy()
union.Parent = parent
end
elseif debugBox then
debugBox.Color3 = Color3.new(2, 0, 0)
end
if debugBox then
debugBox.Transparency = 0.5
Debris:AddItem(debugBox, 2)
end
end
------------------------------------------------------------------------------------------------
do
warn("Solving bevels...")
-- Collect all blocks currently in the workspace.
local initialPass = {}
local debugHint
for _,desc in pairs(workspace:GetDescendants()) do
if canGiveBevels(desc) then
if not desc.Locked then
CollectionService:AddTag(desc, "DoUnlock")
desc.Locked = true
end
table.insert(initialPass, desc)
end
end
if debugMode then
debugHint = Instance.new("Hint")
debugHint.Text = "Generating Bevels..."
debugHint.Parent = workspace
end
-- Run through the initial bevel creation phase.
for _,block in ipairs(initialPass) do
createBevels(block, true)
end
if debugHint then
debugHint:Destroy()
end
end
-- Listen for new parts being added.
workspace.DescendantAdded:Connect(createBevels)
-- Allow regeneration to request bevel solving
local bevelSolver = bevelCache:FindFirstChild("RequestSolve")
if not bevelSolver then
bevelSolver = Instance.new("BindableFunction")
bevelSolver.Name = "RequestSolve"
bevelSolver.Parent = bevelCache
bevelSolver.Archivable = false
end
function bevelSolver.OnInvoke(inst)
for _,desc in pairs(inst:GetDescendants()) do
if desc:IsA("Part") then
createBevels(desc)
end
end
end
if RunService:IsStudio() then
local exportBin = Instance.new("Folder")
exportBin.Name = "ExportBin"
exportBin.Parent = ServerStorage
for _,v in pairs(bevelCache:GetChildren()) do
if v:IsA("TriangleMeshPart") and v.Archivable then
v:Clone().Parent = exportBin
end
end
wait(.1)
for _,v in pairs(exportBin:GetChildren()) do
if v:FindFirstChild("LOD") then
v.LOD:Destroy()
end
end
end
-- Ready!
warn("Bevels ready!")
bevelsReady.Value = true
------------------------------------------------------------------------------------------------