Skip to content

Instantly share code, notes, and snippets.

@howmanysmall
Last active November 29, 2020 04:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save howmanysmall/c2feaeed73dcac18adb6bc679c0ff300 to your computer and use it in GitHub Desktop.
Save howmanysmall/c2feaeed73dcac18adb6bc679c0ff300 to your computer and use it in GitHub Desktop.
-- This goes in ServerStorage.MACRO_PLUGIN.Macros
local Workspace = game:GetService("Workspace")
local CollectionService = game:GetService("CollectionService")
local Selection = game:GetService("Selection")
local BuilderUtility = require(script.BuilderUtility)
local Macro = { }
function Macro:Init()
print("Builder's Assistant initialized")
end
local ALL_MATERIALS = {
wood = 512,
basalt = 788,
sandstone = 912,
brick = 848,
cobblestone = 880,
slate = 800,
woodplanks = 528,
plastic = 256,
salt = 1392,
crackedlava = 804,
grass = 1280,
glass = 1568,
neon = 288,
sand = 1296,
water = 2048,
marble = 784,
corrodedmetal = 1040,
glacier = 1552,
forcefield = 1584,
ground = 1360,
snow = 1328,
air = 1792,
pavement = 836,
limestone = 820,
leafygrass = 1284,
rock = 896,
asphalt = 1376,
smoothplastic = 272,
foil = 1072,
diamondplate = 1056,
mud = 1344,
granite = 832,
metal = 1088,
concrete = 816,
pebble = 864,
fabric = 1312,
ice = 1536
}
local Title = {
Type = "Title";
Text = "Builder's Assistant";
}
local SetToThis = ALL_MATERIALS.smoothplastic
local Material = {
Type = "String";
Text = "Material";
SettingId = "MaterialString";
Value = "SmoothPlastic";
Changed = function(self, NewValue)
local Previous = NewValue
NewValue = NewValue:lower()
NewValue = NewValue:gsub("%s+", "")
if ALL_MATERIALS[NewValue] then
SetToThis = ALL_MATERIALS[NewValue]
else
warn(string.format("The value %q isn't a Material, defaulting to SmoothPlastic.", tostring(Previous)))
SetToThis = ALL_MATERIALS.smoothplastic
end
end;
}
local MaterializeAll = {
Type = "Button";
Text = "Materialize All";
Activate = function(self)
local Descendants = Workspace:GetDescendants()
for Index = 1, #Descendants do
local Child = Descendants[Index]
if Child:IsA("BasePart") then
Child.Material = SetToThis
end
end
end;
}
local MaterializeSelection = {
Type = "Button";
Text = "Materialize Selection";
Activate = function(self)
local Descendants = Selection:Get()
for Index = 1, #Descendants do
local Child = Descendants[Index]
if Child:IsA("BasePart") then
Child.Material = SetToThis
end
end
end;
}
local SearchWoodenParts = BuilderUtility.SearchWoodenParts
local FixGrainDirection = BuilderUtility.FixGrainDirection
local GrainThreshold = {
Type = "Number";
Text = "Threshold";
SettingId = "GrainThreshold";
Value = 0.75;
Changed = function(self, Value)
self.Value = Value > 1 and 1 or Value < 0 and 0 or Value
end;
}
local FixGrainInWorkspace = {
Type = "Button";
Text = "🔨 Fix Wood Grain in Workspace";
Activate = function(self)
local Threshold = GrainThreshold.Value > 1 and GrainThreshold.Value / 100 or GrainThreshold.Value
for _, Child in ipairs(SearchWoodenParts(Workspace:GetDescendants())) do
FixGrainDirection(Child, Threshold)
end
end;
}
local FixGrainInSelection = {
Type = "Button";
Text = "🔨 Fix Wood Grain in Selection";
Activate = function(self)
local Threshold = GrainThreshold.Value > 1 and GrainThreshold.Value / 100 or GrainThreshold.Value
for _, Child in ipairs(SearchWoodenParts(Selection:Get())) do
FixGrainDirection(Child, Threshold)
end
end;
}
local GetColor3 = BuilderUtility.GetColor3
local SetColor = GetColor3("136, 179, 88")
local SetMaterial = ALL_MATERIALS.smoothplastic
local TriangleRGB = {
Type = "String";
Text = "Triangle Color";
SettingId = "TriangleString";
Value = "136, 179, 88";
Changed = function(self, NewValue)
SetColor = GetColor3(NewValue)
end;
}
local TriangleMaterial = {
Type = "String";
Text = "Triangle Material";
SettingId = "TriangleMaterial";
Value = "SmoothPlastic";
Changed = function(self, NewValue)
local Previous = NewValue
NewValue = NewValue:lower()
NewValue = NewValue:gsub("%s+", "")
if ALL_MATERIALS[NewValue] then
SetMaterial = ALL_MATERIALS[NewValue]
else
warn(string.format("The value %q isn't a Material, defaulting to SmoothPlastic.", tostring(Previous)))
SetMaterial = ALL_MATERIALS.smoothplastic
end
end;
}
local NODE_SIZE = Vector3.new(1, 1, 1)
local BuildTriangle = BuilderUtility.BuildTriangle
local TriangleTerrain = {
Type = "Button";
Text = "🔺 Create Triangle";
Activate = function(self)
local CurrentSelection = Selection:Get()
if #CurrentSelection == 3 then
for _, BasePart in ipairs(CurrentSelection) do
assert(BasePart:IsA("BasePart"), ("Selection %s isn't a BasePart!"):format(tostring(BasePart)))
assert(BasePart.Size == NODE_SIZE, ("Selection %s isn't a 1x1x1 square!"):format(tostring(BasePart)))
end
BuildTriangle(CurrentSelection[1].Position, CurrentSelection[2].Position, CurrentSelection[3].Position, SetColor, SetMaterial)
end
end;
}
Macro.Items = {
Title;
Material;
MaterializeAll;
MaterializeSelection;
GrainThreshold;
FixGrainInWorkspace;
FixGrainInSelection;
TriangleRGB;
TriangleMaterial;
TriangleTerrain;
}
return Macro
-- This goes in ServerStorage.MACRO_PLUGIN.Macros.BuilderAssistant
local Workspace = game:GetService("Workspace")
local math_min = math.min
local math_max = math.max
local math_sqrt = math.sqrt
local math_acos = math.acos
local string_split = string.split
local Vector3_new = Vector3.new
local Color3_fromRGB = Color3.fromRGB
local Instance_new = Instance.new
local CFrame_new = CFrame.new
local CFrame_Angles = CFrame.Angles
local tonumber = tonumber
local HALF_PI_X_ANGLES = CFrame_Angles(1.5707963267948965579989817342720925807952880859375, 0, 0)
local HALF_PI_Y_ANGLES = CFrame_Angles(0, 1.5707963267948965579989817342720925807952880859375, 0)
local HALF_PI_Z_ANGLES = CFrame_Angles(0, 0, 1.5707963267948965579989817342720925807952880859375)
local PI_Y_ANGLES = CFrame_Angles(0, 3.141592653589793115997963468544185161590576171875000000000000, 0)
local Wood = Enum.Material.Wood
local WoodPlanks = Enum.Material.WoodPlanks
local Block = Enum.PartType.Block
local BuilderUtility = { }
function BuilderUtility.SearchWoodenParts(Children, Array)
Array = Array or { }
for _, Child in ipairs(Children) do
if (Child:IsA("Part") or Child:IsA("SpawnLocation")) and (Child.Material == Wood or Child.Material == WoodPlanks) and Child.Shape == Block then
local ShouldAdd = true
if Child:FindFirstChildOfClass("CylinderMesh") or Child:FindFirstChildOfClass("FileMesh") then ShouldAdd = false end
if ShouldAdd then Array[#Array + 1] = Child end
else
Array = BuilderUtility.SearchWoodenParts(Child:GetChildren(), Array)
end
end
return Array
end
local function RotateMeshes(Part, RotateCFrame)
for _, Child in ipairs(Part:GetChildren()) do
if Child:IsA("BlockMesh") then
Child.Offset = RotateCFrame:VectorToObjectSpace(Child.Offset)
Child.Scale = RotateCFrame:VectorToWorldSpace(Child.Scale)
end
end
end
function BuilderUtility.FixGrainDirection(Part, Threshold)
local Affected = false
local SizeLow = math_min(Part.Size.X, Part.Size.Y, Part.Size.Z)
local SizeHigh = math_max(Part.Size.X, Part.Size.Y, Part.Size.Z)
if SizeHigh == Part.Size.X then
local NewLow = Part.Size.Y < Part.Size.Z and Part.Size.Y or Part.Size.Z
local NewHigh = Part.Size.Y < Part.Size.Z and Part.Size.Z or Part.Size.Y
if NewHigh == Part.Size.Y then return Affected end
if Threshold > NewLow / NewHigh then
local NewCFrame = Part.CFrame * HALF_PI_X_ANGLES
RotateMeshes(Part, HALF_PI_X_ANGLES)
Part.Size = Vector3_new(Part.Size.X, Part.Size.Z, Part.Size.Y)
Part.CFrame = NewCFrame
Affected = true
end
return Affected
end
if Threshold > SizeLow / SizeHigh then
if SizeHigh == Part.Size.Z then
local NewCFrame = Part.CFrame * HALF_PI_Y_ANGLES
RotateMeshes(Part, HALF_PI_Y_ANGLES)
Part.Size = Vector3_new(Part.Size.Z, Part.Size.Y, Part.Size.X)
Part.CFrame = NewCFrame
Affected = true
elseif SizeHigh == Part.Size.Y then
local NewCFrame = Part.CFrame * HALF_PI_Z_ANGLES
RotateMeshes(Part, HALF_PI_Z_ANGLES)
Part.Size = Vector3_new(Part.Size.Y, Part.Size.X, Part.Size.Z)
Part.CFrame = NewCFrame
Affected = true
end
end
SizeLow = Part.Size.Y < Part.Size.Z and Part.Size.Y or Part.Size.Z
SizeHigh = Part.Size.Y < Part.Size.Z and Part.Size.Z or Part.Size.Y
if SizeHigh == Part.Size.Y then return Affected end
if Threshold > SizeLow / SizeHigh then
local NewCFrame = Part.CFrame * HALF_PI_X_ANGLES
RotateMeshes(Part, HALF_PI_X_ANGLES)
Part.Size = Vector3_new(Part.Size.X, Part.Size.Z, Part.Size.Y)
Part.CFrame = NewCFrame
Affected = true
end
return Affected
end
function BuilderUtility.GetColor3(String)
local Color = string_split(String, ",")
return Color3_fromRGB(tonumber(Color[1]), tonumber(Color[2]), tonumber(Color[3]))
end
local LastTriangle = { Part1 = nil, Part2 = nil }
local TRIANGLE_WIDTH = 0.2
local DEFAULT_SIZE = Vector3_new(1, 7, 7)
local function ParaD(A, B, C)
local BA = B - A
local CA = C - A
return (BA.X * CA.X + BA.Y * CA.Y + BA.Z * CA.Z) / (A - B).Magnitude
end
local function PerpD(A, B, C)
local BA = B - A
local CA = C - A
local Parallel = (BA.X * CA.X + BA.Y * CA.Y + BA.Z * CA.Z) / (A - B).Magnitude
return math_sqrt(CA.Magnitude * CA.Magnitude - Parallel * Parallel)
end
function BuilderUtility.BuildTriangle(NodeA, NodeB, NodeC, SetColor, SetMaterial)
local A, B, C
local Model = Instance_new("Model")
Model.Name = "Triangle"
Model.Parent = Workspace
local P0 = Instance_new("WedgePart")
P0.TopSurface = 0
P0.BottomSurface = 0
P0.Anchored = true
P0.Color = SetColor
P0.Material = SetMaterial
local P1 = Instance_new("WedgePart")
P1.TopSurface = 0
P1.BottomSurface = 0
P1.Anchored = true
P1.Color = SetColor
P1.Material = SetMaterial
local S1 = (NodeA - NodeB).Magnitude
local S2 = (NodeB - NodeC).Magnitude
local S3 = (NodeC - NodeA).Magnitude
local SMax = math_max(S1, S2, S3)
if S1 == SMax then
A, B, C = NodeA, NodeB, NodeC
elseif S2 == SMax then
A, B, C = NodeB, NodeC, NodeA
elseif S3 == SMax then
A, B, C = NodeC, NodeA, NodeB
end
local Perp = PerpD(A, B, C)
local Para = ParaD(A, B, C)
local DiffPara = (A - B).Magnitude - Para
P0.Size = Vector3_new(TRIANGLE_WIDTH, Perp, Para)
P0.CFrame = CFrame_new(B, A)
local TopLook = (P0.CFrame * HALF_PI_X_ANGLES).LookVector
local MidPoint = A + CFrame_new(A, B).LookVector * Para
local NeededLook = CFrame_new(MidPoint, C).LookVector
local Dot = TopLook.X * NeededLook.X + TopLook.Y * NeededLook.Y + TopLook.Z * NeededLook.Z
P0.CFrame = P0.CFrame * CFrame_Angles(0, 0, math_acos(Dot))
if ((P0.CFrame * HALF_PI_X_ANGLES).LookVector - NeededLook).Magnitude > 0.01 then
P0.CFrame = P0.CFrame * CFrame_Angles(0, 0, -2 * math_acos(Dot))
end
P0.CFrame = P0.CFrame * CFrame_new(0, Perp / 2, -(DiffPara + Para / 2))
P0.Parent = Model
P0.Name = "Triangle"
P1.Size = Vector3_new(TRIANGLE_WIDTH, Perp, DiffPara)
P1.CFrame = CFrame_new(B, A) * CFrame_Angles(0, 0, math_acos(Dot)) * PI_Y_ANGLES
if ((P1.CFrame * HALF_PI_X_ANGLES).LookVector - NeededLook).Magnitude > 0.01 then
P1.CFrame = P1.CFrame * CFrame_Angles(0, 0, 2 * math_acos(Dot))
end
P1.CFrame = P1.CFrame * CFrame_new(0, Perp / 2, DiffPara / 2)
P1.Parent = Model
P1.Name = "Triangle"
LastTriangle.Part1 = P0
LastTriangle.Part2 = P1
end
return BuilderUtility
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment