Copy salient and essential scripts and models from Plane Building Roblox

This commit is contained in:
2026-06-10 02:53:40 +01:00
commit 891781366d
76 changed files with 10904 additions and 0 deletions
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
local tween_service = game:GetService("TweenService")
local run_service = game:GetService("RunService")
local player = game.Players.LocalPlayer
local playerGui = player.PlayerGui
local loadingGui = script:WaitForChild("LoadingGui")
loadingGui.Parent = playerGui
local offset = 6
if run_service:IsStudio() then offset = 2 end
local prop_name = {
TextLabel = "TextTransparency",
ImageLabel = "ImageTransparency",
Frame = "BackgroundTransparency"
}
wait(1)
for _,item in pairs(loadingGui:GetChildren()) do
local property = prop_name[item.ClassName]
if not item:IsA("Frame") then
local tween = tween_service:Create(item,TweenInfo.new(2,Enum.EasingStyle.Quad,Enum.EasingDirection.Out,0,false,0),{[property] = 0})
tween:Play()
tween.Completed:Once(function()
local tween2 = tween_service:Create(item,TweenInfo.new(1,Enum.EasingStyle.Quad,Enum.EasingDirection.Out,0,false,1),{[property] = 1})
tween2:Play()
end)
else
local tween2 = tween_service:Create(item,TweenInfo.new(1,Enum.EasingStyle.Quad,Enum.EasingDirection.Out,0,false,offset),{[property] = 1})
tween2:Play()
end
end
+4
View File
@@ -0,0 +1,4 @@
local ready
if game:GetService("RunService"):IsClient() then ready = game.ReplicatedFirst.ReadyClient else ready = game.ReplicatedFirst.ReadyServer end
if not ready.Value then ready.Changed:Wait() end
return function() end
+9
View File
@@ -0,0 +1,9 @@
local run = game:GetService("RunService")
run.RenderStepped:Connect(function()
game.Lighting.Blur.Enabled = false
game.Lighting.WaterCorrection.Enabled = false
if game.Workspace.CurrentCamera.CFrame.Position.Y < 0 then
game.Lighting.Blur.Enabled = true
game.Lighting.WaterCorrection.Enabled = true
end
end)
+9
View File
@@ -0,0 +1,9 @@
# Vehicle // ?!
Command
Create
Delete
Notification
Pick
Place
Save
Saved
+123
View File
@@ -0,0 +1,123 @@
Methods = require(_G.Modules.MethodModule)
Placement = require(_G.Modules.PlacementModule)
Statistics = {}
Configuration = Statistics
function GetValue(part,name,default)
local instance = part:FindFirstChild(name)
if not instance then return default
else return instance.Value end
end
function GetInstance(part,name)
return part:FindFirstChild(name) or error("Missing sub-instance "..name..":"..part:GetFullName(),3)
end
function Print(instance,...)
local text = table.concat({string.format("<b>@%.2f:</b>",tick() - _G.Begin),...}," ")
if _G.PrintUpdate then _G.PrintUpdate(instance,text) end
if _G.IsServer then _G.Remotes.DebugUpdate:FireAllClients(instance,...) end
end
_G.Rules = {}
_G.Rules.Sandbox = {
GenerationVersion = "4",
GenerationFollowPlayer = true,
AutoSpawnIdlers = true,
NeedsExperience = false,
NeedsInventory = false,
NeedsMaterial = false,
NeedsCurrency = false,
}
_G.Rules.Survival = {
GenerationVersion = "5",
GenerationFollowPlayer = true,
AutoSpawnIdlers = false,
AutoSpawnPirates = true,
AutoSpawnFreighters = true,
NeedsExperience = true,
NeedsInventory = true,
NeedsMaterial = true,
NeedsCurrency = false,
}
_G.Rules.War = {
GenerationVersion = "4",
GenerationFollowPlayer = false,
AutoSpawnIdlers = false,
AutoSpawnPirates = false,
AutoSpawnFreighters = false,
NeedsExperience = false,
NeedsInventory = true,
NeedsMaterial = true,
NeedsCurrency = true
}
_G.RuleMode = "Sandbox"
_G.Notify = function(title,content)
game.ReplicatedStorage.Remotes.Notification:FireAllClients(title,content)
end
_G.SaveCompression = true
_G.SaveFormat = "4"
_G.Changelog = [[
DUCK5_0:
Figuring out scripting with Lua and Rust.
Bevy setup.
NEL0:
Parser, interpreter and LI standard library complete.
PB3:
More computer fixes, I forget.
Added steam engines.
Adding balloons.
DCT1:
Scrapped old buildings for 'slice trees'
DCT0:
Implemented working terrain engine.
Loads of sorcery:
Poly terrain (Mesh or WedgePart)
Buildings Beta
PB2:
Fixed ropes and rods scaling.
Fixed ropes and rods.
Fixed motors.
Made some fixes to CreateToolScript and PaintToolScript.
Made DebugWings deactivate correctly.
Did various fixes on ProgramModule.
Added meshes.
Tried to fix propellers.
Fixed wheel resizing.
Fixed rod bug in save system.
Fixed the anchor block bug.
PB1:
Fixed fatal materials paint bug.
Fixed model resizing bug.
Fixed disconnectors bug.
Fixed guns bug.
Fixed notifications looking stupid.
Fixed spring edit updates.
Fixed a poly sticks save bug.
Protected various objects from error.
Fixed microcontroller dragger bug.
Fixed microcontroller deletion bug.
Fixed poly snapping.
Added a changelog.
PB0:
Object system 'Agnostic Ownership' overhaul.
Added Microcontrollers.
Added new engine models.
Aero system implements viscosity (gliding).
Made various changes to object properties.
Poly now supports up to 9 nodes.
Poly now supports thickness changes.
Many unrecorded QOL changes. (like 200 more lines xD)
]]
_G.Union(_G,getfenv()) -- Combine _G with this module
return getfenv()
+161
View File
@@ -0,0 +1,161 @@
function Union(this,other) -- Combine two tables
for index,item in pairs(other) do
this[index] = item
end
return this
end
function Default(this,other)
for index,item in pairs(other) do
if not this[index] then this[index] = item end
end
end
local _string = {["string"] = true}
local _number = {["number"] = true}
local _table = {["table"] = true}
local _boolean = {["boolean"] = true}
local type = type
IsString = function(value) return not not _string[type(value)] end
IsNumber = function(value) return not not _number[type(value)] end
IsTable = function(value) return not not _table[type(value)] end
IsBoolean = function(value) return not not _boolean[type(value)] end
Modules = game.ReplicatedFirst:WaitForChild("Modules")
Storage = game.ReplicatedStorage
Assets = Storage:WaitForChild("Assets")
Sounds = Assets:WaitForChild("Sounds")
Events = game.ReplicatedFirst:WaitForChild("Events")
Remotes = Storage:WaitForChild("Remotes")
Debris = game:GetService("Debris")
Tags = game:GetService("CollectionService")
Tween = game:GetService("TweenService")
Run = game:GetService("RunService")
Input = game:GetService("UserInputService")
Http = game:GetService("HttpService")
IsServer = Run:IsServer()
IsStudio = Run:IsStudio()
IsClient = not IsServer
if IsClient then Player = game.Players.LocalPlayer end
Begin = tick()
if IsServer then
Data = game:GetService("DataStoreService")
else
Player = game.Players.LocalPlayer
UserId = Player.UserId
Gui = game.Players.LocalPlayer.PlayerGui
end
function Iterate(array)
local i = 0
local n = #array
return function()
i = i + 1
if i <= n then return array[i] end
end
end
function WaitFor(tab,item,timeout)
local t = 0
timeout = timeout or 60
repeat wait() t = t + 1 if t > timeout then error("Timeout for "..item,3) end until tab[item]
return tab[item]
end
Errors = {}
function Error(stuff)
local traceback = debug.traceback("\nTraceback:",3)
table.insert(Errors,tostring(stuff)..traceback)
warn(stuff,traceback)
end
function Protect(func)
return function(...)
local success,result = xpcall(func,function(err)
Error(err)
end,...)
return result
end
end
function Protected(func,...)
local success,result = xpcall(func,function(err)
Error(err)
end,...)
if success then return result end
end
String = tostring
Append = table.insert
ClassOf = getmetatable
Percent = function(num)
if not num then error("Number is nil.",2) end
local val = math.ceil(num*3)
local colour = "31"
if val > 2 then
colour = "32"
elseif val > 1 then
colour = "33"
end
local txt = "\27["..colour.."m"..String(math.floor(num*100)).."%".."\27[0m"
return txt
end
Unimplemented = function()
error("Method unimplemented",2)
end
Notify = function(...)
Events.Notification:Fire(...)
end
Class = function(properties)
local template = {} -- Creates a metatable
template.templates = {}
template.Descends = function(other)
return template.templates[getmetatable(other)]
end
template.__index = {}
if properties.Inherits then -- Combine the template of the superclass
template.__index = Union({},properties.Inherits.__index or {})
template.__tostring = properties.Inherits.__tostring
properties.Inherits.templates[template] = true
template.super = properties.Inherits
properties.Inherits = nil
end
properties.Class = template
template.templates[template] = true -- Add this to the table of
if properties.Construct then
template.constructor = properties.Construct or function(_,...) end
properties.Construct = nil
end
for index,item in pairs(properties) do -- The remaining items in properties are data
template.__index[index] = item -- So all constants and methods go here
end
template.create = template.create or function(this,...) -- Call template to make this
local new = {} -- Create a new instance table
template.constructor(new,...) -- Construct it
return setmetatable(new,template) -- Return the newly made instance
end
setmetatable(template,{
__call = template.create,
})
template.__tostring = properties.String or template.__tostring
return template
end
Union(_G,getfenv()) -- Combine _G with this module
require(script.Parent.BuildingGlobals)
if IsServer then
game.ReplicatedFirst.ReadyServer.Value = true
else
game.ReplicatedFirst.ReadyClient.Value = true
end
return _G
+1
View File
@@ -0,0 +1 @@
require(script.Parent.DefaultGlobals)
+1
View File
@@ -0,0 +1 @@
require(script.Parent.DefaultGlobals)
+26
View File
@@ -0,0 +1,26 @@
local defaultPosition = script.Parent.Saves.Position
local defaultSize = script.Parent.Saves.Size
local expandedPosition = UDim2.new(0.5,-defaultSize.X.Offset,0,defaultPosition.Y.Offset)
local expandedSize = UDim2.new(0,defaultSize.X.Offset*2,0,defaultSize.Y.Offset)
local toggle = false
local input = game:GetService("UserInputService")
local function expansion()
toggle = not toggle
local newSize = defaultSize
local newPosition = defaultPosition
if toggle then
newSize = expandedSize
newPosition = expandedPosition
end
script.Parent.Saves:TweenSizeAndPosition(newSize,newPosition,Enum.EasingDirection.InOut,Enum.EasingStyle.Quad,0.5,true)
end
input.InputEnded:Connect(function(input,irrelevant)
if not irrelevant and script.Parent.Enabled then
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.E then
expansion()
end
end
end
end)
script.Parent.MinimizeButton.Activated:Connect(expansion)
+43
View File
@@ -0,0 +1,43 @@
local template = script.Notification
local function Popup(title,message)
local notification = template:Clone()
notification.TitleLabel.Text = title
notification.ContentLabel.Text = message
notification.Parent = script.Parent.Parent.ScreenGui
notification.Position = UDim2.new(0.5,0,0.5,0)
notification.Size = UDim2.new(0,400,0,0)
notification.AnchorPoint = Vector2.one*0.5
notification.UIDragDetector.Enabled = true
notification.TitleLabel.DestroyButton.Activated:Connect(function()
notification:Destroy()
end)
end
local function Notification(title,message)
local notification = template:Clone()
notification.TitleLabel.Text = title or "Untitled"
notification.ContentLabel.Text = tostring(message)
notification.Parent = script.Parent.Left
notification.Size = UDim2.new(0,0,0,0)
notification.TitleLabel.DestroyButton.Visible = false
local tapped = function()
Popup(title,message)
end
notification.TouchTap:Connect(tapped)
notification.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then tapped() end
end)
notification:TweenSize(UDim2.new(0,210,0,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Quad,0.5,true,function()
wait(3)
notification.ContentLabel.Size = UDim2.new(0,notification.ContentLabel.AbsoluteSize.X,0,notification.ContentLabel.AbsoluteSize.Y)
notification:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Quad,0.5,true,function()
notification:Destroy()
end)
end)
end
require(game.ReplicatedFirst.ReadyModule)
_G.Remotes.Notification.OnClientEvent:Connect(function(title,message)
Notification(title,message)
end)
_G.Events.Notification.Event:Connect(function(title,message)
Notification(title,message)
end)
+159
View File
@@ -0,0 +1,159 @@
require(game.ReplicatedFirst.ReadyModule)
local optionFrame = script.Parent:WaitForChild("OptionFrame")
local options = optionFrame:WaitForChild("Options")
function registerToggle(name,callback)
local button = options:WaitForChild(name)
local value = button.TextButton.Text == "On"
button.TextButton.MouseButton1Click:Connect(function()
value = not value
local new = "On"
if not value then
new = "Off"
end
button.TextButton.Text = new
callback(value)
_G.Events.Notification:Fire(name,new)
end)
end
local function registerSlider(name,callback)
local slider = options:WaitForChild(name)
local drag = slider:WaitForChild("Slider"):WaitForChild("Drag")
local etch = slider:WaitForChild("Slider"):WaitForChild("Etch")
local hold
local position
local event
local function changed(object)
if hold then
local delta = (object.Position - hold).X
local final = math.clamp((delta/(etch.AbsoluteSize.X))+position,0,1)
drag.Position = UDim2.new(final,0,0,0)
callback(final)
else
event:Disconnect()
end
end
drag.InputBegan:Connect(function(object)
if object.UserInputType == Enum.UserInputType.MouseButton1 or object.UserInputType == Enum.UserInputType.Touch then
hold = object.Position
position = drag.Position.X.Scale
event = _G.Input.InputChanged:Connect(changed)
end
end)
drag.InputEnded:Connect(function(object)
if object.UserInputType == Enum.UserInputType.MouseButton1 or object.UserInputType == Enum.UserInputType.Touch then
hold = nil
position = nil
end
end)
end
local collaborateButton = options:WaitForChild("CollaborateButton")
collaborateButton.MouseButton1Click:Connect(function()
_G.Remotes.Collaborate:InvokeServer(nil,nil)
end)
registerSlider("Air",function(value)
game.Workspace.CompensateDensity.Value = value * 2
end)
registerToggle("Destruction",function(value)
game.Workspace.Destruction.Value = value
end)
registerToggle("Combat",function(value)
game.Workspace.Combat.Value = value
if value then
_G.Remotes.Combat:InvokeServer()
end
end)
registerToggle("Debug",function(value)
game.Workspace.DebugWings.Value = value
end)
registerToggle("Stats",function(value)
_G.StatisticsFrame.Visible = value
end)
registerSlider("Dead",function(value)
local val = value > 0.5
game.Lighting.ColorCorrection.Enabled = val
game.Lighting.ColorGrading.Enabled = val
end)
local defaultDensity = 0.262
local defaultBlur = 24
local changeFog = function(value)
game.Lighting.Atmosphere.Density = defaultDensity*value
game.Lighting.Blur.Size = defaultBlur*value
end
local changeTime = function(value)
game.Lighting.Time.Value = value * 24
end
registerSlider("Fog",changeFog)
registerSlider("Time",changeTime)
--if _G.Statistics.fog_enabled then changeFog() options:WaitForChild("Fog").Text = "Off" end
local minimized = true
local toggle = _G:WaitFor("OptionsButton")
local extended = optionFrame.Position
local retracted = UDim2.new(UDim.new(0,-optionFrame.AbsoluteSize.X),extended.Y)
optionFrame.Position = retracted
optionFrame.Visible = true
local function toggleMenu()
print("toggle")
minimized = not minimized
optionFrame.Visible = not minimized
--[[local new
if minimized then
new = retracted
else
new = extended
end
optionFrame:TweenPosition(new,Enum.EasingDirection.InOut,Enum.EasingStyle.Quad,0.4,true)]]
end
toggle.Activated:Connect(toggleMenu)
local input = game:GetService("UserInputService")
input.InputBegan:Connect(function(input,irrelevant)
if not irrelevant and input.KeyCode == Enum.KeyCode.Backquote then
if input:IsModifierKeyDown(Enum.ModifierKey.Shift) then
minimized = true
optionFrame.Visible = false
optionFrame.Options.Command.CommandBox:ReleaseFocus()
else
minimized = false
optionFrame.Visible = true
wait() optionFrame.Options.Command.CommandBox:CaptureFocus()
end
end
end)
--game.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Chat,false)
local messageFrame = script.Parent:WaitForChild("MessageFrame")
local messageBox = messageFrame:WaitForChild("TextBox")
local messageContainer = messageFrame:WaitForChild("ScrollingFrame")
_G.Remotes.Chat.OnClientEvent:Connect(function(title,content,color)
local message = messageContainer.Template.Message:Clone()
message.Parent = messageContainer
message.Title.Text = title
message.Content.Text = content
message.Title.TextColor3 = color
message.Visible = true
messageContainer.CanvasPosition = Vector2.new(0, messageContainer.CanvasSize.Y.Offset - messageContainer.AbsoluteSize.Y)
end)
_G.Input.InputBegan:Connect(function(input,irrelevant)
if not irrelevant and input.KeyCode == Enum.KeyCode.Slash then
messageBox:CaptureFocus()
end
end)
_G:WaitFor("ChatHideButton").Activated:Connect(function()
messageFrame.Visible = not messageFrame.Visible
end)
messageBox.FocusLost:Connect(function(enterPressed)
if enterPressed then
local message = messageBox.Text
:gsub("\n","<br/>")
:gsub("<br>","<br/>")
:gsub("<big>(.-)</big>","<font size=\"28\">%1</font>")
:gsub("<small>(.-)</small>","<font size=\"10\">%1</font>")
:gsub("<green>(.-)</green>","<font color=\"#00FF00\">%1</font>")
:gsub("<red>(.-)</red>","<font color=\"#FF0000\">%1</font>")
:gsub("<trans.->(.-)</trans.->","<font transparency=\"0.5\">%1</font>")
:gsub("_(.-)_","<i>%1</i>")
:gsub("*(.-)*","<b>%1</b>")
if #message == 0 then return end
_G.Remotes.Chat:FireServer(message)
messageBox.Text = ""
end
end)
+2
View File
@@ -0,0 +1,2 @@
require(game.ReplicatedFirst.ReadyModule)
require(_G.Modules.PlacementModule).init(script.Parent)
+2
View File
@@ -0,0 +1,2 @@
require(game.ReplicatedFirst.ReadyModule)
require(_G.Modules.SaveModule).InitUI(script)
+183
View File
@@ -0,0 +1,183 @@
require(game.ReplicatedFirst.ReadyModule)
local shopGui = script.Parent
_G.ShopGui = shopGui
local shopFrame = shopGui.Items
local shopMaterial = shopGui.CashLabel
local shopBackButton = shopGui.BackButton
local shopSearch = shopGui.Search
-- Parts
local partsStore = _G.Storage:WaitForChild("Parts")
local namedFrames = {}
local directNamedFrames = {}
local folders = {}
local currentFolder = partsStore
local currentTerm = ""
local function createItemFrame(item)
local defaultFrame = shopFrame.Grid.ItemFrame
defaultFrame.Visible = false
-- Create ViewportFrame and put a camera in
local itemFrame = defaultFrame:Clone()
itemFrame.Parent = shopFrame
itemFrame.ItemLabel.Text = item:GetAttribute("Name") or item.Name
itemFrame.Visible = true
if not item:HasTag("ConnectorObject") then itemFrame.PolyLabel:Destroy() end
itemFrame.NewLabel:Destroy()
itemFrame.Name = item:GetAttribute("Name") or item.Name
local viewportFrame = itemFrame.ViewportFrame
local camera = Instance.new("Camera")
camera.Parent = viewportFrame
viewportFrame.CurrentCamera = camera
item = item:Clone()
item.Parent = viewportFrame
item:PivotTo(item:GetAttribute("Rotation") or CFrame.identity)
local distance
local cameraRotation = 45
if item:IsA("BasePart") then
distance = item.Size.Magnitude
elseif item:IsA("Model") then
if not item.PrimaryPart then
warn(item:GetFullName().." has no PrimaryPart, please set one!")
item.PrimaryPart = item:FindFirstChildWhichIsA("BasePart")
end
distance = item:GetExtentsSize().Magnitude
end
local function rotateCamera()
camera.CFrame = CFrame.fromEulerAnglesYXZ(-0.8,math.rad(cameraRotation),0)
camera.CFrame = camera.CFrame + camera.CFrame.LookVector * -(distance/1.2)
end
rotateCamera()
local mouseInside = false
viewportFrame.MouseEnter:Connect(function()
mouseInside = true
viewportFrame.BackgroundColor3 = Color3.new(0.75,0.75,0.75)
while wait() and mouseInside do
cameraRotation = cameraRotation + 0.33
rotateCamera()
end
end)
viewportFrame.MouseLeave:Connect(function() mouseInside = false viewportFrame.BackgroundColor3 = Color3.new(1,1,1) end)
return itemFrame
end
local function clearItems()
for _,item in pairs(shopFrame:GetChildren()) do
if item:IsA("Frame") then
item.Parent = nil -- Now is this a good idea or not...
end
end
end
local f = string.find
local l = string.lower
local function loadSearch(term)
clearItems()
wait()
term = l(term)
for _,info in pairs(namedFrames) do
if f(l(info[1]),term,1,true) then -- A crude search if anything
info[2].Parent = shopFrame
end
end
for _,info in pairs(directNamedFrames) do
if f(l(info[1]),term,1,true) then -- A crude search if anything
info[2].Parent = shopFrame
end
end
end
shopSearch.FocusLost:Connect(function(enter, inputObject)
if enter then
-- add a quick enter
end
end)
local function loadFolder(folder)
currentFolder = folder
clearItems()
wait()
for _,frame in pairs(folders[folder]) do
frame.Parent = shopFrame
end
end
shopSearch.Changed:Connect(function()
if currentTerm == shopSearch.Text then return end
currentTerm = shopSearch.Text
if #shopSearch.Text > 0 then
loadSearch(shopSearch.Text)
else
loadFolder(currentFolder)
end
end)
local existing = {}
local t = 200
local function registerItem(item)
if existing[item] then return end
existing[item] = true
if item:HasTag("DoNotPlace") then return end
local isFolder = item:IsA("Folder")
local isPart = item.Parent:IsA("Folder") and (item:IsA("Model") or item:IsA("BasePart"))
if isFolder or isPart then
local model = item
if isFolder then
local smallestOrder = t+1
local smallestItem
for _,child in pairs(item:GetDescendants()) do
if not (child.Parent:IsA("Folder") and (child:IsA("BasePart") or child:IsA("Model"))) then continue end
local order = child:GetAttribute("Order") or t
if order < smallestOrder then smallestOrder = order smallestItem = child end
end
model = smallestItem
end
if not model then warn(item:GetFullName().." has no children.") return end
local frame = createItemFrame(model)
frame.LayoutOrder = item:GetAttribute("Order") or t
folders[item.Parent] = folders[item.Parent] or {} -- Make sure the folder exists
table.insert(folders[item.Parent],frame)
if isPart then
if item:GetAttribute("Name") then table.insert(namedFrames,{item:GetAttribute("Name"),frame}) end
table.insert(directNamedFrames,{item.Name,frame})
frame.InputEnded:Connect(function(inputObject)
if inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch then
_G.Events.Pick:Fire(item)
if item.Parent:IsA("Folder") and item.Parent:HasTag("AutoBack") then
loadFolder(item.Parent.Parent)
end
end
end)
else
frame.ItemLabel.Text = item.Name
frame.InputEnded:Connect(function(inputObject)
if inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch then
loadFolder(item)
end
end)
end
end
end
local function loadStore()
for _,item in pairs(partsStore:GetDescendants()) do
registerItem(item)
end
loadFolder(currentFolder)
end
shopBackButton.MouseButton1Click:Connect(function()
local parent = currentFolder.Parent
if currentFolder == partsStore then parent = partsStore end
loadFolder(parent)
end)
loadStore()
+182
View File
@@ -0,0 +1,182 @@
local StarterGui = game:GetService("StarterGui")
StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false)
local frame = script.Parent
frame.Visible = true
local input = game:GetService("UserInputService")
local player = game.Players.LocalPlayer
local backpack = player.Backpack
local stroke = script:WaitForChild("UIStroke")
local currentTool = nil
local tools = {}
local frames = {}
local function isToolRegistered(tool) return tools[tool] end
local function getToolFrame(tool) return frames[tool] end
local function stockTool(tool)
tool.Parent = backpack
stroke.Parent = script
stroke.Transparency = 1
frames[tool].Deselected:Fire()
end
local index = 0
local function setTool(tool)
if currentTool then stockTool(currentTool) end -- If there is already a tool equipped, dequip it
if currentTool == tool then currentTool = nil return end -- If this tool is already equipped, remove it and stop
if tonumber(tool.Name) then index = tonumber(tool.Name) end
frames[tool].Selected:Fire()
tool.Parent = player.Character -- Equip the new tool
stroke.Parent = frames[tool]
stroke.Transparency = 0.2
currentTool = tool
end
local nameToInteger = {
["One"] = 1,
["Two"] = 2,
["Three"] = 3,
["Four"] = 4,
["Five"] = 5,
["Six"] = 6,
["Seven"] = 7,
["Eight"] = 8,
["Nine"] = 9
}
input.InputBegan:Connect(function(input,irrelevant)
if not irrelevant then
local tools = 0
for _,item in pairs(frame.ScrollingFrame:GetChildren()) do
if item:FindFirstChild("ToolReference") then tools = tools + 1 end
end
local inputted = false
if input.UserInputType == Enum.UserInputType.Keyboard then
local number = nameToInteger[input.KeyCode.Name]
if number then inputted = true index = number end
elseif input.KeyCode == Enum.KeyCode.ButtonR1 then
inputted = true
index = index + 1
elseif input.KeyCode == Enum.KeyCode.ButtonL1 then
inputted = true
index = index - 1
end
index = math.clamp(index,0,tools+1)
if not inputted then return end
local toolFrame = frame.ScrollingFrame:FindFirstChild(tostring(index))
if toolFrame then
setTool(toolFrame.ToolReference.Value)
else
if currentTool then stockTool(currentTool) currentTool = nil end
end
end
end)
local function createToolFrame(tool)
local toolFrame = frame.ScrollingFrame.Template.Tool:Clone()
toolFrame.ToolReference.Value = tool
local layout = tool:WaitForChild("LayoutOrderValue",1)
if layout then layout = layout.Value
else layout = #frames end
toolFrame.LayoutOrder = layout
frames[tool] = toolFrame
tools[tool] = tool
local clone = tool:Clone()
for _,child in pairs(clone:GetChildren()) do
if child:IsA("Script") then
child:Destroy()
end
end
toolFrame.Parent = frame.ScrollingFrame
toolFrame.Visible = true
toolFrame.Name = tostring(toolFrame.LayoutOrder)
local viewport = toolFrame.ViewportFrame
viewport.Name = clone.Name
clone.Parent = viewport
local camera = Instance.new("Camera")
viewport.CurrentCamera = camera
camera.Parent = viewport
local label = toolFrame.TextLabel
label.Text = tool.Name
local cframe,size = clone:GetBoundingBox()
local cameraRotation = 45
local distance = size.Magnitude
local function rotateCamera() -- To constantly spin the tool view
camera.CFrame = CFrame.new(cframe.Position) * CFrame.fromEulerAnglesYXZ(-0.3,math.rad(cameraRotation),0)
camera.CFrame = camera.CFrame + camera.CFrame.LookVector * -(distance/1.2)
end
rotateCamera() -- To initialise the camera CFrame once
local mouseInside = 0 -- Rotate camera while the mouse is inside the tool
local mouseEnter = function(no_back)
mouseInside = mouseInside + 1
if not no_back then toolFrame.BackgroundColor3 = Color3.new(0.75,0.75,0.75) end
if mouseInside == 1 then while wait() and mouseInside > 0 do
cameraRotation = cameraRotation + 1.5 * mouseInside
rotateCamera()
end end
end
local mouseLeave = function(no_back)
mouseInside = mouseInside - 1
if not no_back then toolFrame.BackgroundColor3 = Color3.new(1,1,1) end
end
viewport.MouseEnter:Connect(mouseEnter)
viewport.MouseLeave:Connect(mouseLeave)
toolFrame.Selected.Event:Connect(mouseEnter)
toolFrame.Deselected.Event:Connect(mouseLeave)
return toolFrame
end
local function unregisterTool(tool)
--print(tool:GetFullName())
if currentTool == tool then stroke.Parent = script currentTool = nil end
if frames[tool] then frames[tool]:Destroy() end
frames[tool] = nil
tools[tool] = nil
end
local function registerTool(tool)
if isToolRegistered(tool) then return end -- Don't register an existing tool
tool.AncestryChanged:Connect(function(tool,parent) -- Event for when parent changes
if not (parent == player.Character or parent == backpack) then -- If the tool is not a parent of the character or backpack, it is dropped
unregisterTool(tool)
end
end)
createToolFrame(tool)
-- todo: save me
frames[tool].InputEnded:Connect(function(inputObject) -- When the mouse button or touch is clicked (XBOX + VR CONTROL NEEDED)
if inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch then
setTool(tool)
end
end)
--[[frames[tool].TouchTap:Connect(function()
setTool(tool)
end)]]
end
game.Players.LocalPlayer.ChildAdded:Connect(function(child)
if currentTool then unregisterTool(currentTool) end
if child:IsA("Backpack") then backpack = child end
backpack.ChildAdded:Connect(registerTool)
backpack.ChildRemoved:Connect(registerTool)
end)
for _,tool in pairs(backpack:GetChildren()) do
registerTool(tool)
end
+1
View File
@@ -0,0 +1 @@
Most things are not relative to the original hierarchy. I'm just dumping everything here.
+91
View File
@@ -0,0 +1,91 @@
if _G.IsClient then
local module = {}
function module.fire(barrel)
spawn(function()
local bullet = _G.Remotes.Shoot:InvokeServer(barrel)
bullet = game.Workspace:WaitForChild(bullet)
if not bullet then return end
bullet.CFrame = barrel.CFrame * CFrame.Angles(0,math.rad(-90),0)
bullet.Velocity = bullet.CFrame.LookVector * 1000
--bullet.BodyVelocity.Velocity = bullet.Velocity
end)
end
return module
elseif _G.IsServer then
-- SERVER-SIDE
function touched(touched,team,player,bullet,barrel)
if not touched or not touched.Parent then return end
local humanoid = touched.Parent:FindFirstChildWhichIsA("Humanoid")
if humanoid then
if humanoid.Parent:FindFirstChild("Team") and humanoid.Parent.Team.Value == game.Teams.Loon then
humanoid.Health = humanoid.Health - 25
bullet:Destroy()
end
end
local object
local part
if touched.Parent:IsA("Model") and touched.Parent:HasTag("Combat") then
object = touched.Parent
part = object.PrimaryPart
else
part = touched
object = touched
end
if object:HasTag("Combat") then
if not object:HasTag("P_"..player.UserId) then
if not object:GetAttribute("Health") then
local max = part.Size.Magnitude/1.5
if object:IsA("Model") then max = object:GetExtentsSize().Magnitude/1.5 end
object:SetAttribute("Health",max)
object:SetAttribute("MaxHealth",max)
end
local health = object:GetAttribute("Health")
local max = object:GetAttribute("MaxHealth")
health = health - 3
--part.Transparency = 1-(health/max)
if barrel.BlastPressure.Value ~= -1 then
local explosion = Instance.new("Explosion")
explosion.BlastPressure = barrel.BlastPressure.Value
explosion.DestroyJointRadiusPercent = barrel.Destruction.Value
explosion.Parent = part
explosion.Position = part.Position
end
object:SetAttribute("Health",health)
if health < 0 then
_G.Methods.break_part(object,player)
object.Parent = game.Workspace.Hidden -- stop busted mechanics please
end
bullet:Destroy()
end
end
end
function fire(barrel,team,player)
if not barrel then return end
local bullet = _G.Storage[barrel.Parent.Name.."Bullet"]:Clone()
delay(1.8,function() if bullet and bullet.Parent then bullet:Destroy() end end)
bullet.CFrame = CFrame.new(0,1000000000,0)
bullet.Parent = game.Workspace
local connection = bullet.Touched:Connect(function(part)
touched(part,team,player,bullet,barrel)
end)
bullet.CFrame = barrel.CFrame * CFrame.Angles(0,math.rad(-90),0)
bullet.Velocity = bullet.CFrame.LookVector * 1000
bullet.Anchored = false
bullet:SetAttribute("Team",team.Name)
if player then
bullet:SetNetworkOwner(player) --[[delay(0,function() bullet:SetNetworkOwnershipAuto() end)]]
bullet:SetAttribute("Owner",player.UserId)
end
bullet:FindFirstChildWhichIsA("Sound"):Play()
bullet.Name = bullet.Name .. tostring(math.random(1,1000000))
return bullet.Name
end
_G.Remotes.Shoot.OnServerInvoke = function(player,barrel) return fire(barrel,player.Team,player) end
return getfenv()
end
+153
View File
@@ -0,0 +1,153 @@
motors = {}
modes = {}
cframes = {}
targets = {}
c0s = {}
c1s = {}
torso = nil
head = nil
model = nil
humanoid = nil
running = false
cameraOffset = Vector3.new(0,0,0)
run = game:GetService("RunService")
input = game:GetService("UserInputService")
motorsEnabled = true
function getMotor(name)
motors[name] = torso:WaitForChild(name)
cframes[name] = CFrame.new(0,0,0) -- need to change this to a {} for each motor, not globals
targets[name] = CFrame.new(0,0,0)
c0s[name] = motors[name].C0
c1s[name] = motors[name].C1
return motors[name]
end
function check(...)
for _,item in pairs({...}) do
if not item or not item.Parent then return false end
end
return true
end
local alpha = 0.1 * (1/60)
function setArm(name,delta)
if not delta then delta = 1/60 end
if not check(model,head,torso,motors[name]) or motorsEnabled or humanoid.SeatPart then return end
local origin = (game.Workspace.CurrentCamera.CFrame.Rotation + head.Position)
if modes[name] ~= 1 then cframes[name] = origin:ToObjectSpace(motors[name].Part1.CFrame) * CFrame.Angles(-1.57,0,0) end
modes[name] = 1
cframes[name] = cframes[name]:Lerp(targets[name],alpha/delta)
motors[name].Part1.CFrame = origin:ToWorldSpace(cframes[name] * CFrame.Angles(1.57,0,0))
end
function restArm(name,delta)
if not delta then delta = 1/60 end
if not check(model,head,torso,motors[name]) or motorsEnabled or humanoid.SeatPart then return end
local origin = (torso.CFrame)
if modes[name] ~= 2 then cframes[name] = origin:ToObjectSpace(motors[name].Part1.CFrame) * CFrame.Angles(-1.57,0,0) end
modes[name] = 2
cframes[name] = cframes[name]:Lerp(targets[name],alpha/delta)
motors[name].Part1.CFrame = origin:ToWorldSpace(cframes[name] * CFrame.Angles(1.57,0,0))
end
function resetMotors()
for name,motor in pairs(motors) do
if motor and motor.Parent then
motor.C0 = c0s[name]
motor.C1 = c1s[name]
end
end
end
function rotateCharacter()
if not model or not model.Parent or not model.PrimaryPart or not humanoid then return end
local state = humanoid:GetState()
local statetype = Enum.HumanoidStateType
if state == statetype.Ragdoll or state == statetype.FallingDown or state == statetype.GettingUp then return end
model.HumanoidRootPart.CFrame =
CFrame.new(model.HumanoidRootPart.Position,
model.HumanoidRootPart.Position
+ Vector3.new(
game.Workspace.CurrentCamera.CFrame.LookVector.X,0,
game.Workspace.CurrentCamera.CFrame.LookVector.Z))
end
function lookArm(name,position)
end
function rotateHead()
if not motors["Neck"] or not torso then return end
local target = torso.CFrame:ToObjectSpace(game.Workspace.Camera.CFrame).LookVector
motors["Neck"].C0 = CFrame.new(0, 1, 0, -1, 0, 0, 0, 0, 1, 0, 1, -0) * CFrame.Angles(math.acos(target.Y)+math.rad(-105),0,math.acos(target.X)+math.rad(-90))
end
function disableMotors()
if not motorsEnabled then return end
motorsEnabled = false
getMotor("Left Shoulder").Enabled = false
local leftArm = motors["Left Shoulder"].Part1
leftArm.CanCollide = false
leftArm.Anchored = true
getMotor("Right Shoulder").Enabled = false
local rightArm = motors["Right Shoulder"].Part1
rightArm.CanCollide = false
rightArm.Anchored = true
end
function enableMotors()
if motorsEnabled then return end
motorsEnabled = true
motors["Left Shoulder"].Enabled = true
local leftArm = motors["Left Shoulder"].Part1
leftArm.CanCollide = true
leftArm.Anchored = false
motors["Right Shoulder"].Enabled = true
local rightArm = motors["Right Shoulder"].Part1
rightArm.CanCollide = true
rightArm.Anchored = false
end
function setRunning(value)
if value then
running = true
humanoid.WalkSpeed = 30
else
running = false
humanoid.WalkSpeed = 15
end
end
run:BindToRenderStep("CharacterMotorReset",1,function() resetMotors() rotateHead()
game.Players.LocalPlayer.CameraMode = Enum.CameraMode.Classic
if humanoid then humanoid.CameraOffset = cameraOffset end cameraOffset = Vector3.new(0,0,0)
end)
run:BindToRenderStep("CharacterHeadRotate",Enum.RenderPriority.Last.Value+1,function()
rotateHead()
end)
function init(character)
torso = character:WaitForChild("Torso")
head = character:WaitForChild("Head")
humanoid = character:WaitForChild("Humanoid")
humanoid.BreakJointsOnDeath = false
motors = {}
model = character
enableMotors()
getMotor("Neck").Enabled = true
getMotor("Left Shoulder").Enabled = true
getMotor("Right Shoulder").Enabled = true
spawn(function() while wait(.1) do
local event = _G.Remotes.MotorMoveClient
event:FireServer(motors["Neck"],motors["Neck"].C0,motors["Neck"].C1)
event:FireServer(motors["Left Shoulder"],motors["Left Shoulder"].C0,motors["Left Shoulder"].C1)
event:FireServer(motors["Right Shoulder"],motors["Right Shoulder"].C0,motors["Right Shoulder"].C1)
end end)
input.InputBegan:Connect(function(object,irrelevant)
if not irrelevant then
if object.KeyCode == Enum.KeyCode.LeftShift or object.KeyCode == Enum.KeyCode.ButtonL1 then
humanoid.WalkSpeed = 30
running = true
end
end
end)
input.InputEnded:Connect(function(object,irrelevant)
if not irrelevant then
if object.KeyCode == Enum.KeyCode.LeftShift or object.KeyCode == Enum.KeyCode.ButtonL1 then
humanoid.WalkSpeed = 15
running = false
end
end
end)
game.Workspace.CurrentCamera.CameraSubject = humanoid
end
return getfenv()
+54
View File
@@ -0,0 +1,54 @@
require(game.ReplicatedFirst.ReadyModule)("CommandsModule")
local help = ""
local commands = {}
function invoke(input,player)
local found = false
for index,command in ipairs(commands) do
local result = table.pack(input:match(command.pattern))
if result[1] then
if player then
table.insert(result,1,player)
end
found = true
command.callback(table.unpack(result))
end
end
return found
end
function new(pattern,documentation,callback)
help = help .. documentation .. "\n"
table.insert(commands,{
pattern = pattern,
documentation = documentation,
callback = callback
})
if _G.IsServer then _G.Storage:WaitForChild("CommandsHelp").Value = help end
end
_G.Events.Command.Event:Connect(function(command)
local cFound = invoke(command)
local sFound = _G.Remotes.Command:InvokeServer(command)
if not cFound and not sFound then _G.Events.Notification:Fire("Command Not Found",[[
If you're struggling to type in a command, note that they use pattern matching.
"<userid>" means you should literally type "12345678", no letters or dots.
Check your spelling or type 'help' for some commands.
]]) end
end)
if _G.IsServer then
_G.Remotes.Command.OnServerInvoke = function(player,command)
return invoke(command,player)
end
end
local notification
new("help","help: displays this message",function()
local final = "Client:\n"..help
if _G.IsClient then final = final.."\nServer:\n".._G.Storage.CommandsHelp.Value end
_G.Events.Notification:Fire("Command Help",final)
end)
return getfenv()
+41
View File
@@ -0,0 +1,41 @@
local module = {}
local function dragger(object,callback,root)
local finger
object.InputBegan:Connect(function(input)
if finger then return end
if root then
local objects = game.Players.LocalPlayer.PlayerGui:GetGuiObjectsAtPosition(input.Position.X, input.Position.Y)
if #objects == 0 or objects[1] == nil then return end -- Roblox what the hell?
while not objects[1]:IsDescendantOf(root) do table.remove(objects,1) end
if objects[1] ~= object then return end
end
if input.UserInputType == Enum.UserInputType.Touch then finger = input end
if input.UserInputType == Enum.UserInputType.MouseButton1 then finger = true end
if not finger then return end
local options = callback(Vector2.new(input.Position.X,input.Position.Y))
local function drag(input)
if options.drag then options.drag(Vector2.new(input.Position.X,input.Position.Y)) end
end
drag(input)
local changed
changed = _G.Input.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement or input == finger then
drag(input)
end
end)
local ended
ended = _G.Input.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input == finger then
drag(input)
if options.ended then options.ended(Vector2.new(input.Position.X,input.Position.Y)) end
changed:Disconnect()
ended:Disconnect()
finger = nil
end
end)
end)
end
return {dragger = dragger}
+127
View File
@@ -0,0 +1,127 @@
-- this code LOOKS scary, but its actually pretty simple and fast
local items = {}
local is_server = not game.Players.LocalPlayer
local userid
if not is_server then
userid = game.Players.LocalPlayer.UserId
end
-- to stop allocating new variables each time (threading doesn't work anyways)
local position
local velocity
local delta1
local delta2
local delta
local difference
local vp
local this_root
local function round(n)
return math.round(n*1000)/1000
end
local common_delta = 1/120
local common_drag_resistance = 0.967
local common_float_resistance = 0.8
local float_terminal = 1
local decay = 10
local complement = -11.5
local idecay = 1/decay
local anti_gravity = Vector3.new(0,196,0)
local floating_velocity = 10
local run = game:GetService("RunService")
local global_drag_resistance = 0
local global_float_resistance = 0
local global_delta = 0
run.PreSimulation:Connect(function(delta)
global_float_resistance = math.pow(common_float_resistance,delta/common_delta)-1
global_drag_resistance = math.pow(common_drag_resistance,delta/common_delta)-1
global_delta = delta
end)
local water_height = -4
local decay_complement = -water_height+decay+complement
local template_instance = _G.Storage.Idol
local impulse = template_instance.ApplyImpulseAtPosition
local angular_impulse = template_instance.ApplyAngularImpulse
local network_owner_check_success, network_owner
local get_network_owner_real = template_instance.GetNetworkOwner
local get_network_owner = function(instance)
network_owner_check_success,network_owner = pcall(function() get_network_owner_real(instance) end)
return ((not network_owner_check_success) or network_owner)
end
local root_part = template_instance.GetRootPart
local new_vec3 = Vector3.new
local counter = 0
local root_reduction = {}
local function reduce_root_part(root)
root.Velocity = root.Velocity * 0.99
root.AssemblyAngularVelocity = root.AssemblyAngularVelocity * 0.99
end
local math_clamp = math.clamp
local local_disconnect_all = true
local function register_part(instance)
if not instance:IsA("BasePart") then return end
if not instance.Parent then return end
local size = instance.Size
local volume = size.X*size.Y*size.Z
local mass = instance.Mass
local event
local operate = true
local splash = instance:FindFirstChild("Splash")
if splash and not splash:IsA("ParticleEmitter") then splash = nil end
if is_server and get_network_owner(instance) then return end
if instance.Massless then return end
instance.AncestryChanged:Connect(function() if not instance.Parent then operate = false event:Disconnect() end end)
--instance.Destroying:Connect(function() print("disconnect") event:Disconnect() end)
event = run.PreSimulation:Connect(function(delta)
--if not operate then event:Disconnect() end
if is_server and get_network_owner(instance) then wait(0.2) return end
vp = instance.Position
position = vp.Y+decay_complement
if position < 0 then
--if splash then splash.Enabled = true splash.Rate = math_clamp(10*instance.AssemblyLinearVelocity.Magnitude,0,100) end -- PLEASE OPTIMISE
position = math.abs(position*idecay)
--instance.Transparency = 0.5
if position > 1 then position = 1 end
velocity = instance.Velocity
--local angular = instance.AssemblyAngularVelocity -- implement later...
difference = velocity*global_drag_resistance + new_vec3(0,velocity.Y-floating_velocity,0)*global_float_resistance
impulse(instance,difference*volume*position+anti_gravity*delta,vp)
else
--if splash then splash.Enabled = false end -- PLEASE OPTIMISE
--velocity = instance.Velocity
wait(0.1) -- to stop checking too many times in air (saving frames)
end
end)
return event
end
if is_server then
game.Workspace.Objects.DescendantAdded:Connect(function(instance)
local owner = instance:GetAttribute("NetworkOwner")
if not owner and is_server then
--register_part(instance)
for _,child in pairs(instance:GetChildren()) do
--register_part(child)
end
end
end)
else
game.Workspace.Objects.DescendantAdded:Connect(function(instance)
local owner = instance:GetAttribute("NetworkOwner")
if owner == userid then
--register_part(instance)
for _,child in pairs(instance:GetChildren()) do
--register_part(child)
end
end
end)
end
local module = {}
module.register_part = register_part
return module
+36
View File
@@ -0,0 +1,36 @@
function init(this,mode)
if mode == "Survival" then
this.Parent.Triggered:Connect(function(player)
for _,tool in pairs(player.Character:GetChildren()) do
if tool:IsA("Tool") then
tool:Destroy()
end
end
for _,tool in pairs(player.Backpack:GetChildren()) do
if tool:IsA("Tool") then
tool:Destroy()
end
end
for _,tool in pairs(_G.Storage.Tools:GetChildren()) do
tool:Clone().Parent = player.Backpack
end
player.Character:SetPrimaryPartCFrame(game.Workspace.Heaven.AngelSpawn.CFrame+Vector3.new(0,4,0))
end)
end
if mode == "Heaven" then
this.Parent.Triggered:Connect(function(player)
for _,tool in pairs(player.Character:GetChildren()) do
if tool:IsA("Tool") then
tool:Destroy()
end
end
for _,tool in pairs(player.Backpack:GetChildren()) do
if tool:IsA("Tool") then
tool:Destroy()
end
end
player.Character:SetPrimaryPartCFrame(game.Workspace.GoonSpawn.CFrame+Vector3.new(0,4,0))
end)
end
end
return getfenv()
+200
View File
@@ -0,0 +1,200 @@
c = require(_G.Modules.CharacterModule)
l = "Left Shoulder"
r = "Right Shoulder"
local lastShot = tick()
local input = game:GetService("UserInputService")
local bulletModule = nil
if script.Parent:FindFirstChild("BulletModule") then bulletModule = require(_G.Modules.BulletModule) end
function init(gun)
local fov = 60
local returnTable = {}
local la = {} -- states for the arm in different animations
local ra = {}
returnTable.la = la
returnTable.ra = ra
returnTable.maxRounds = 10
returnTable.shouldRest = true
returnTable.clock = 0.1
returnTable.rounds = returnTable.maxRounds
local aiming = false
local shootEvent
local cameraLast = game.Workspace.CurrentCamera.CFrame.Rotation
ra.aiming = CFrame.new(0,-1,-2)
ra.idling = CFrame.new(1,-1,-2) * CFrame.Angles(0,0,0.5)
ra.recoil = CFrame.new(0,0,1.5)
ra.sheath = CFrame.new(1.5,-4,0)*CFrame.Angles(-1.57,0,0)
ra.reload = {}
ra.reload.beforeSmashMagazine = CFrame.new(1,-0.5,-2)*CFrame.Angles(0.8,0.5,0)
ra.reload.smashingMagazine = CFrame.new(0.7,-0.9,-2)*CFrame.Angles(0.4,0.3,0)
ra.reload.beforeLoadingMagazine = CFrame.new(1,-0.5,-2)*CFrame.Angles(0.8,-0.5,0)
ra.reload.loadingMagazine = CFrame.new(1,-0.5,-2)*CFrame.Angles(0.8,-0.5,0)
ra.reload.beforePullBack = CFrame.new(2.2,-1,-2.5)*CFrame.Angles(-0.3,1.2,0.1)
ra.reload.pullBack = CFrame.new(1.7,-1,-2.5)*CFrame.Angles(-0.3,1.2,0.1)
la.idling = CFrame.new(-1.5,0,0)*CFrame.Angles(-1.57,0,0)
la.resting = CFrame.new(-1.5,0,0)*CFrame.Angles(-1.57,0,0)
la.aiming = CFrame.new(-0.5,-1.3,-2) * CFrame.Angles(0.3,-1,0)
la.recoil = CFrame.new(0.5,0,0.75)
la.sheath = CFrame.new(-1.5,-1.3,0)*CFrame.Angles(-1.57,0,0)
la.reload = {}
la.reload.beforeSmashMagazine = CFrame.new(-1.1,-1.5,0)*CFrame.Angles(-1.57,0,0)
la.reload.smashingMagazine = CFrame.new(-1.1,-1.5,0)*CFrame.Angles(-1.57,0,0)
la.reload.beforeLoadingMagazine = CFrame.new(0.3,-1.1,-2.5) * CFrame.Angles(0.4,-1,0)
la.reload.loadingMagazine = CFrame.new(0.6,-0.7,-2)*CFrame.Angles(0.6,-1,0)
la.reload.beforePullBack = CFrame.new(-1,-1,-2.6)*CFrame.Angles(0.4,0,0)
la.reload.pullBack = CFrame.new(0.5,-1,-2.4)*CFrame.Angles(0.4,0,0)
for name,cframe in pairs(la.reload) do
la.reload[name] = cframe + Vector3.new(-0.4,0,0)
end
for name,cframe in pairs(ra.reload) do
ra.reload[name] = cframe + Vector3.new(-0.4,0,0)
end
gun.Equipped:Connect(function()
if c.humanoid.SeatPart then return end
local rc = {} -- state for the arm
local lc = {}
local gc = {} -- state for gun pully thing
c.disableMotors()
rc.offset = CFrame.new(0,0,0)
rc.target = ra.idling
lc.offset = CFrame.new(0,0,0)
lc.target = la.resting
rc.janky = CFrame.new()
lc.janky = CFrame.new()
if not returnTable.shouldRest then lc.target = la.idling end
gc.target = CFrame.new(0,0,0)
c.cframes[r] = ra.sheath c.setArm(r)
if not returnTable.shouldRest then c.setArm(l) c.cframes[l] = la.sheath c.setArm(l)
else c.restArm(l) c.cframes[l] = la.resting c.restArm(l) end
c.run:UnbindFromRenderStep("GunMove")
c.run:UnbindFromRenderStep("Transparency")
local modes = {["idle"]=1,["reload"]=function()
local scale = game.Workspace.Scale.Value
rc.target = ra.reload.beforeSmashMagazine
lc.target = la.reload.beforeSmashMagazine
wait(0.3*scale)
gun.MagOut:Play()
rc.target = ra.reload.smashingMagazine
lc.target = la.reload.smashingMagazine
wait(0.4*scale)
rc.target = ra.reload.beforeLoadingMagazine
lc.target = la.reload.beforeLoadingMagazine
wait(0.5*scale)
rc.target = ra.reload.loadingMagazine
lc.target = la.reload.loadingMagazine
gun.MagIn:Play()
wait(0.3*scale)
rc.target = ra.reload.beforePullBack
lc.target = la.reload.beforePullBack
wait(0.4*scale)
gun.Reload:Play()
if gun:FindFirstChild("Slide") then gc.target = gun.Slide.Value end
rc.target = ra.reload.pullBack
lc.target = la.reload.pullBack
wait(0.3*scale)
gc.target = CFrame.new(0,0,0)
end,}
local mode = modes.idle
local reloading = false
if shootEvent then shootEvent:Disconnect() end
shootEvent = gun.Activated:Connect(function()
if not reloading and not c.running then
if returnTable.rounds == 0 and returnTable.maxRounds > 0 then
reloading = true
modes.reload()
reloading = false
returnTable.rounds = returnTable.maxRounds
return
end
if tick()-lastShot < returnTable.clock then return end
gun.Fired:Play()
lastShot = tick()
returnTable.rounds-=1
local rot = Vector3.new(0,0,math.random(-10,10)/10)
rc.offset = ra.recoil * CFrame.Angles(rot.x,rot.y,rot.z)
if aiming or not returnTable.shouldRest then lc.offset = la.recoil end
if gun.Gun:FindFirstChild("GunShot") then
gun.Gun.GunShot.ParticleEmitter.Transparency = NumberSequence.new(0)
end
if gun:FindFirstChild("Slide") then gc.target = gun.Slide.Value end
if bulletModule and gun.Gun:FindFirstChild("Barrel") then bulletModule.fire(gun.Gun.Barrel) end
wait()
if gun.Gun:FindFirstChild("GunShot") then
gun.Gun.GunShot.ParticleEmitter.Transparency = NumberSequence.new(1)
end
rc.offset = CFrame.new(0,0,0)
lc.offset = CFrame.new(0,0,0)
wait(returnTable.clock/2)
gc.target = CFrame.new(0,0,0)
end
end)
c.run:BindToRenderStep("GunMove",Enum.RenderPriority.Last.Value,function(delta)
if gun.Parent ~= c.model then c.enableMotors() return end
--game.Players.LocalPlayer.CameraMode = Enum.CameraMode.LockFirstPerson
if not reloading then
if input:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) or input:IsKeyDown(Enum.KeyCode.Q) then
aiming = true
c.setRunning(false)
rc.target = ra.aiming
lc.target = la.aiming
fov = (fov+60)/2
else
aiming = false
fov = (fov+70)/2
rc.target = ra.idling
rc.janky = CFrame.new():Lerp(game.Workspace.CurrentCamera.CFrame.Rotation:ToObjectSpace(cameraLast),0.3)
cameraLast = game.Workspace.CurrentCamera.CFrame.Rotation
--if lc.target ~= la.resting then c.setArm(l) end
if returnTable.shouldRest then lc.target = la.resting
else lc.target = la.idling end
end
game.Workspace.CurrentCamera.FieldOfView = fov
if c.running then
rc.target = CFrame.new(1.5,-1,-1.5) * CFrame.Angles(1,0,0)
lc.target = la.resting
end
if not reloading and input:IsKeyDown(Enum.KeyCode.R) then
mode = modes.reload
reloading = true
modes.reload()
mode = modes.idle
reloading = false
returnTable.rounds = returnTable.maxRounds
end
end
if not c.humanoid.SeatPart then c.rotateCharacter() c.disableMotors() else --[[print("Enable")]] c.enableMotors() end
c.targets[r] = rc.target * rc.offset * rc.janky
c.targets[l] = lc.target * lc.offset * lc.janky
if gun.Gun:FindFirstChild("Slide") and gun.Gun.Slide:FindFirstChild("Motor6D") then
gun.Gun.Slide.Motor6D.C1 = gun.Gun.Slide.Motor6D.C1:Lerp(gc.target,0.5)
end
c.setArm(r,delta)
if lc.target == la.resting then
c.restArm(l,delta)
else
c.setArm(l,delta)
end
c.motors[r].Part1.LocalTransparencyModifier = 0
c.motors[l].Part1.LocalTransparencyModifier = 0
c.motors[r].Part1.CastShadow = false
c.motors[l].Part1.CastShadow = false
if math.abs(game.Workspace.CurrentCamera.CFrame:ToObjectSpace(c.head.CFrame).Position.Z) > 1 then c.cameraOffset = Vector3.new(1,0,0) end
end)
c.run:BindToRenderStep("Transparency",2,function()
--game.StarterPlayer.CameraMode = Enum.CameraMode.LockFirstPerson
end)
end)
return returnTable
end
return getfenv()
+69
View File
@@ -0,0 +1,69 @@
local extract = bit32.extract
local replace = bit32.replace
local gmatch = string.gmatch
local char = string.char
local pck = string.pack
local unpck = string.unpack
local lshift = bit32.lshift
local rshift = bit32.rshift
local format = string.format
local byte = string.byte
local tostring = tostring
local tonumber = tonumber
local gsub = string.gsub
local rep = string.rep
local sub = string.sub
local r = 33
@native
local function encode(num)
local str = pck("d",num)
local f = unpck("I",str:sub(1,4))
local s = unpck("I",str:sub(5,8))
return format("%s%s%s%s%s%s%s%s%s%s%s",
char(extract(f,0,6)+r),
char(extract(f,6,6)+r),
char(extract(f,12,6)+r),
char(extract(f,18,6)+r),
char(extract(f,24,6)+r),
char(extract(s,0,6)+r),
char(extract(s,6,6)+r),
char(extract(s,12,6)+r),
char(extract(s,18,6)+r),
char(extract(s,24,6)+r),
char(replace(extract(f,30,2),extract(s,30,2),2,2)+r)
)
end
@native
local function decode(str)
local a,b,c,d,e,f,g,h,i,j,k = byte(str,1,11)
k = k - r
local s = replace(replace(replace(replace(replace(f-r,g-r,6,6),h-r,12,6),i-r,18,6),j-r,24,6),extract(k,2,2),30,2)
local f = replace(replace(replace(replace(replace(a-r,b-r,6,6),c-r,12,6),d-r,18,6),e-r,24,6),extract(k,0,2),30,2)
local a = unpck("d",pck("I",f)..pck("I",s))
return a
end
@native
local function rle_encode(str)
--print("nuttin",str)
str = gsub(str,"%$","$$")
--print("dollars sanitised",str)
str = gsub(str,"(!!!!?!?!?!?!?!?)",function(rep)
return format("$%i",#rep)
end)
--print("repeats replaced",str)
return str
end
@native
local function rle_decode(str)
str = gsub(str,"(%$+)(%d)",function(first,num)
if (#first % 2 == 0) then return first..num end
return rep("$",#first-1)..rep("!",tonumber(num))
end)
--print("repeats removed",str)
str = gsub(str,"%$%$","$")
--print("dollars desanities",str)
return str
end
return {decode = decode, encode = encode, rle_encode = rle_encode, rle_decode = rle_decode}
+182
View File
@@ -0,0 +1,182 @@
-- This module can be required and used by other scripts to get information about where the mouse is in the 3D world
local module = {}
module.tags = _G.Tags
-- table of parts in game by index, each index is the object's id, each object has an ID tag associated to it
--[[module.objects = {} -- this should only be regarded for the server
function module.getId(object) -- CLIENT/SERVER
for _,tag in pairs(module.tags:GetTags(object)) do -- remove object from the objects table by its ID
if string.sub(tag,1,3) == "ID_" then
local id = tonumber(string.sub(tag,4,#tag))
return id
end
end
return false
end
function module.removeId(object) -- SERVER ONLY
module.objects[module.getId(object)] = nil
end
function module.newId(object) -- SERVER ONLY
local currentIdIndex = 1
for i=1,#module.objects do
if not module.objects[i] then
module.objects[i] = object
return i
end
end
module.objects[#module.objects+1] = object
return #module.objects+1
end]]
local function getConnectedParts(part)
local connected = part:GetConnectedParts(false)
for _,joint in pairs(part:GetJoints()) do
if joint:IsA("Constraint") then
if joint.Attachment0 and joint.Attachment1 then
if joint.Attachment0.Parent == part then
table.insert(connected,joint.Attachment1.Parent)
else
table.insert(connected,joint.Attachment0.Parent)
end
end
end
end
return connected
end
function module.forEachConnectedObject(start,callback,show,parent,ignore)
if not start then return end
if start:IsA("Model") then start = start.PrimaryPart end
if not parent then parent = game.Workspace.Objects end
local parts = {}
local objects = {}
local function checkPart(part)
local object
if part.Parent:IsA("Model") and part.Parent.Parent == parent then
object = part.Parent
elseif part.Parent == parent then
object = part
end
if not object or objects[object] then return end
objects[object] = object
callback(object)
end
local function tryConnected(part)
if parts[part] then return else parts[part] = part checkPart(part) end
local connected = getConnectedParts(part)
for _,part in pairs(connected) do
tryConnected(part)
end
end
tryConnected(start)
return objects
end
--[[function module.forEachConnectedObjectOld2(start,callback,show,parent,ignore)
if not start then return end
if start:IsA("Model") then start = start.PrimaryPart end
if not parent then parent = game.Workspace.Objects end
local checkedTag = "checked"..tostring(tick())
local objects = {}
local function checkPart(part)
local object
if part.Parent:IsA("Model") and part.Parent.Parent == parent then
object = part.Parent
elseif part.Parent == parent then
object = part
end
if object and not object:HasTag(checkedTag) then
object:AddTag(checkedTag)
table.insert(objects,object)
end
return object
end
local function getConnected(part)
if not part:HasTag(checkedTag) then
checkPart(part)
part:AddTag(checkedTag)
for _,part in pairs(getConnectedParts(part)) do
if part.Name ~= "Separator" or not ignore then getConnected(part) end
end
end
end
for _,part in pairs(start:GetConnectedParts(false)) do
getConnected(part)
end
for _,object in pairs(objects) do
callback(object)
end
for _,part in pairs(module.tags:GetTagged(checkedTag)) do
module.tags:RemoveTag(part,checkedTag)
end
end
function module.forEachConnectedObjectOld(start,callback,debugShow,parent)
local tagId = "checked"..tostring(tick())
if start:IsA("Model") then start = start.PrimaryPart end
local parts = start:GetConnectedParts(true)
module.tags:AddTag(start,tagId)
module.tags:AddTag(start.Parent,tagId)
local function checkPart(part,depth)
local count = 0
if not module.tags:HasTag(part,tagId) then
if depth < 2 then
for _,joint in pairs(part:GetJoints()) do
if joint:IsA("Constraint") then
if joint.Attachment1 and not module.tags:HasTag(joint.Attachment1.Parent,tagId) then
for _,part in pairs(joint.Attachment1.Parent:GetConnectedParts(true)) do
checkPart(part,depth+1)
end
end
if joint.Attachment0 and not module.tags:HasTag(joint.Attachment0.Parent,tagId) then
for _,part in pairs(joint.Attachment0.Parent:GetConnectedParts(true)) do
checkPart(part,depth+1)
end
end
end
end
end
local newObject -- Note, we are walking over the newObject function. this is ok, we're not gonna use it. a neat feature of lua's scope
if part.Parent == game.Workspace.Objects then -- we found a part! lets add it
newObject = part
elseif part.Parent:IsA("Model") and part.Parent.Parent == game.Workspace.Objects then -- we found a model instead from this part (its a child of the model)
newObject = part.Parent
end
print(part,depth)
if newObject and not module.tags:HasTag(newObject,tagId) then
module.tags:AddTag(newObject,tagId)
callback(newObject)
if debugShow then
local previousTransparency = part.Transparency -- fancy bits here show for debug
part.LocalTransparencyModifier = 0.9 - (depth * 0.2)
if count == 6 then count = 0 wait() else count = count + 1 end
task.delay(0.25,function() part.LocalTransparencyModifier = previousTransparency end) -- fancy bits here show for debug
end
end
end
end
for key,part in pairs(parts) do
checkPart(part,0)
end
for _,part in pairs(module.tags:GetTagged(tagId)) do
module.tags:RemoveTag(part,tagId)
end
end]]
return module
+213
View File
@@ -0,0 +1,213 @@
require(game.ReplicatedFirst.ReadyModule)
local module = {}
local input = game:GetService("UserInputService")
local mouse = require(_G.Modules.MouseModule)
local gamepad = Enum.UserInputType.Gamepad1
local function KeyBinding(keycode,value)
return function()
if input:IsKeyDown(keycode) then return value else return nil end
end
end
local function ButtonBinding(keycode,value)
return function()
if input:IsGamepadButtonDown(gamepad,keycode) then return value else return nil end
end
end
local function MouseBinding(inputtype,value)
return function()
if input:IsMouseButtonPressed(inputtype) then return value else return nil end
end
end
local function SequenceBinding(iterations,predicate)
local value = 0
input.InputEnded:Connect(function(input,irrelevant)
if not irrelevant then
if predicate(input) then
value = value + 1/iterations
if value > 1 then value = 0 end
return value
end
end
end)
return function() return value end
end
local function SequenceKeyBinding(keycode,iterations)
return SequenceBinding(iterations,function(input)
return input.UserInputType == Enum.UserInputType.Keyboard and input.KeyCode == keycode
end)
end
local function SequenceButtonBinding(keycode,iterations)
return SequenceBinding(iterations,function(input)
return input.UserInputType == gamepad and input.KeyCode == keycode
end)
end
local function SequenceMouseBinding(inputtype,iterations)
return SequenceBinding(iterations,function(input)
return input.UserInputType == inputtype
end)
end
local function HoldBinding()
return function(old,new)
if not new then return old end
end
end
local function DeltaBinding(velocity,predicate)
return function(value,new,delta)
if predicate() then return (value or 0) + velocity * delta end
end
end
local function DeltaKeyBinding(keycode,velocity)
return DeltaBinding(velocity,function()
return input:IsKeyDown(keycode)
end)
end
local function DeltaButtonBinding(keycode,velocity)
return DeltaBinding(velocity,function()
return input:IsGamepadButtonDown(gamepad,keycode)
end)
end
local function DeltaMouseBinding(inputtype,velocity)
return DeltaBinding(velocity,function()
return input:IsMouseButtonPressed(inputtype)
end)
end
local function MouseMovement(keycode)
return function()
input:IsKeyDown(keycode)
end
end
local controlBindings = {
throttle = {
DeltaKeyBinding(Enum.KeyCode.LeftShift,1),
DeltaKeyBinding(Enum.KeyCode.LeftControl,-1),
HoldBinding()
},
pitch = {
KeyBinding(Enum.KeyCode.W,-1),
KeyBinding(Enum.KeyCode.S,1)
},
yaw = {
KeyBinding(Enum.KeyCode.Q,1),
KeyBinding(Enum.KeyCode.E,-1)
},
roll = {
KeyBinding(Enum.KeyCode.A,1),
KeyBinding(Enum.KeyCode.D,-1)
},
fire = {
MouseBinding(Enum.UserInputType.MouseButton1,1),
KeyBinding(Enum.KeyCode.F,-1)
},
bomb = {
KeyBinding(Enum.KeyCode.B,1)
},
action1 = {
ButtonBinding(Enum.KeyCode.L,1)
},
action2 = {
MouseBinding(Enum.UserInputType.MouseButton3,1)
},
flaps = {
SequenceKeyBinding(Enum.KeyCode.F,4)
},
gear = {
SequenceKeyBinding(Enum.KeyCode.G,1)
},
}
module.values = {}
module.cursor = nil
function module.aimTowards(root,position,values)
local aim = root:PointToObjectSpace(position).Unit
local _,_,z = root:ToEulerAnglesYXZ()
values.pitch += aim.Y*2 + values.pitch
values.yaw += aim.X*-2 + values.yaw
values.roll += values.roll + (-z/3) + values.yaw/5
end
-- Bindings
local shouldAim = true
module.seat = nil
if _G.IsClient then
spawn(function()
local dragger = require(_G.Modules.DraggingModule).dragger
_G:WaitFor("ControlMouseButton")
local function aimOptionCallback()
shouldAim = not shouldAim
if shouldAim then
_G.ControlMouseButton.Text = "Mouse"
else
_G.ControlMouseButton.Text = "WASD"
end
end
_G.ControlMouseButton.Activated:Connect(aimOptionCallback)
input.InputEnded:Connect(function(input,irrelevant)
if not irrelevant then
if
input.UserInputType == Enum.UserInputType.Keyboard and
input.KeyCode == Enum.KeyCode.LeftAlt
then
aimOptionCallback()
end
end
end)
local function processThrottle(throttle)
throttle = math.clamp(throttle,-1,1)
_G.ThrottleFrame.ThrottleBar.Value.Size = UDim2.new(1, 0, 0.5 * math.abs(throttle),0)
_G.ThrottleFrame.ThrottleBar.Value.Position = UDim2.new(0, 0, 0.5 - (0.5 * math.clamp(throttle,0,1)),0)
_G.ThrottleFrame.TextValue.Text = tostring(math.ceil(throttle * 100)).."%"
if throttle > 0 then
_G.ThrottleFrame.ThrottleBar.Value.BackgroundColor3 = Color3.new(0, 1, 0)
end
if throttle < 0 then
_G.ThrottleFrame.ThrottleBar.Value.BackgroundColor3 = Color3.new(1, 0, 0)
end
module.values.throttle = throttle
end
dragger(_G.ThrottleFrame.ThrottleBar,function(begin)
return {
drag = function(changed)
local down = (changed.Y - (_G.ThrottleFrame.ThrottleBar.AbsolutePosition.Y))/_G.ThrottleFrame.ThrottleBar.AbsoluteSize.Y
processThrottle(-(math.clamp(down,0,1) * 2 - 1))
end,
}
end)
repeat
local delta = task.wait()
_G.ThrottleFrame.Visible = not not module.seat
if not module.seat then continue end
for name,bindings in pairs(controlBindings) do
local value = nil
for _,binding in pairs(bindings) do
local new = binding(module.values[name],value,delta)
if new then
--print(name,module.values[name],value,delta)
if math.abs(new) > (value or 0) then value = new end
end
end
module.values[name] = value or 0
end
local instance,ray = mouse.getMouseHit()
module.cursor = ray.Origin + ray.Direction * 300
if module.seat and shouldAim then
local position = ray.Origin + ray.Direction * 300 + Vector3.new(0,25,0)
module.aimTowards(module.seat:GetPivot(),position,module.values)
end
processThrottle(module.values.throttle)
for index,value in pairs(module.values) do
module.values[index] = math.clamp(value or 0,-1,1)
end
until false
end)
end
return module
+176
View File
@@ -0,0 +1,176 @@
params = {}
params.cs = 512
params.cliff = 4
params.offset = Vector3.new(0,-300,0)
params.thresh = 0.18
params.voxels = false
local p = params
exclude = {}
chunks = {}
function part(x,y,z,mat,s)
local stone = Instance.new("Part")
stone.Parent = game.Workspace.Generation
stone.Transparency = 0
stone.Material = Enum.Material.Slate
stone.Position = Vector3.new(x,y,z)*params.s + p.offset
stone.Size = Vector3.new(p.s,p.s,p.s)
if s then stone.Size = s end
stone.Anchored = true
local grass = stone:Clone()
grass.CanCollide = true
grass.Transparency = 0
grass.Material = Enum.Material.Grass
grass.Color = Color3.fromRGB(255, 255, 255)
grass.Parent = stone
grass.Size = Vector3.new(stone.Size.X+1,20,stone.Size.Z+1)
grass.Position = stone.Position + Vector3.new(0,stone.Size.Y/2 - grass.Size.Y/2 + 1,0)
return stone
end
local mod = 1
local n = function(x,y,z,scale)
return math.noise(x*scale,y*scale,z*scale)
end
local function mulv(a,b) return Vector3.new(a.x*b.x,a.y*b.y,a.z*b.z) end
local structures = _G.Storage.Structures:GetChildren()
local foliage = _G.Storage.Foliage:GetChildren()
function islandChunk(ox,oz) -- i had to think there lol
local chunk = chunks[tostring(ox)..","..tostring(oz)]
local cn = n(ox,.5,oz,0.05)
if cn < 0.3 then return end
local offset = math.abs(n(ox,124.5432,oz,0.015)*2)
ox = ox + 20
for oy = 0,3 do
mod = math.clamp(math.pow(Vector2.new(ox,oz).Magnitude/6,2),0,1)
local height = {}
p.thresh = 0.5
p.s = 10
local lx = 1/8
local ly = 1/8
local lz = 1/8
local values = {}
local xi = 0
for x=ox,ox+1,lx do
xi = xi + 1
table.insert(values,{})
local yi = 0
for y=oy,oy+1,ly do
yi = yi + 1
local hshift = math.abs((y/2)-1)/4
table.insert(values[xi],{})
local zi = 0
for z=oz,oz+1,lz do
zi = zi + 1
local v = (n(x,y,z,0.05))*((n(x,y,z,0.1)+n(x,y,z,1)+n(x,y,z,0.5)))-0.2-hshift-math.abs(n(x,0.123,y,0.1))
table.insert(values[xi][yi],v)
if xi > 1 and yi > 1 and zi > 1 then
local temp = values[xi]
local a = temp[yi][zi-1] > 0
local b = temp[yi-1][zi] > 0
local c = values[xi-1][yi][zi] > 0
local d = v > 0
if ((not(a and b and c) and d) or ((a or b or c) and not d)) and math.random(1,4) == 1 then
local part = _G.Storage.Stone:Clone()
part.Position = Vector3.new(x,y+offset,z)*params.cs
part.Size = Vector3.new(lx,ly,lz)*params.cs
part.Parent = chunk.folder
local m = Vector3.new(math.random(1,16)/4,math.random(1,16)/4,math.random(1,16)/4)+Vector3.new(1.2,1.2,1.2)
part.Size = mulv(part.Size,m)
local grass = _G.Storage.Grass:Clone()
grass.Size = Vector3.new(part.Size.X+1,params.cs/math.random(5,30),part.Size.Z+1)
grass.CFrame = part.CFrame + Vector3.new(0,part.Size.Y/2,0)
grass.Parent = chunk.folder
end
end
end
end
end
end
end
function chunk(ox,oz)
math.randomseed(ox+oz)
local chunk = {}
chunk.folder = Instance.new("Folder")
chunk.folder.Name = tostring(ox)..", "..tostring(oz)
chunk.folder.Parent = game.Workspace.Generation
chunk.position = Vector3.new(ox,0,oz) -- ? my hands are cold again
chunk.needed = true
if ox%2==0 and oz%2==0 then
for i=18,2,-4 do
local water = _G.Storage.Water:Clone()
water.Size = Vector3.new(params.cs,2,params.cs)*2
water.Position = Vector3.new(ox,0,oz)*params.cs + Vector3.new(0,i+-41.5,0)
water.Parent = chunk.folder
end
end
chunks[tostring(ox)..","..tostring(oz)] = chunk -- again abusing Lua's hashtables
--[[islandChunk(ox,oz) -- oh lord have mercy on my clockspeed
local cn = n(ox,.5,oz,0.05)
if cn < 0.4 then return end
mod = math.clamp(math.pow(Vector2.new(ox,oz).Magnitude/6,2),0,1)
local height = {}
local lx = 1/8
local ly = 1/8
local lz = 1/8
local values = {}
local xi = 0
for x=ox-lx,ox+1,lx do
xi = xi + 1
table.insert(values,{})
local yi = 1
local y=-0.05
local hshift = y/2
table.insert(values[xi],{})
local zi = 0
for z=oz-lz,oz+1,lz do
zi = zi + 1
local v = (n(x,y,z,1)+n(x,y,z,1))*n(x,y,z,0.05)-0.5-hshift
table.insert(values[xi][yi],v)
if xi > 1 and zi > 1 then
if v > 0 then
local r = CFrame.new()--CFrame.Angles(0,math.random(-10,10)/300,0)
local part = _G.Storage.Sand:Clone()
part.CFrame = CFrame.new(Vector3.new(x,y,z)*params.cs)*r
part.Size = Vector3.new(lx,ly*0.2,lz)*params.cs
part.Parent = chunk.folder
local grass = _G.Storage.Grass:Clone()
grass.CFrame = CFrame.new(Vector3.new(x,y,z)*params.cs)*r
grass.Size = Vector3.new(lx,ly*0.2,lz)*params.cs
grass.Parent = chunk.folder
local m = Vector3.new(math.random(1,10),math.random(1,10)/10,math.random(1,10))/5+Vector3.new(1,1,1)
part.Size = mulv(part.Size,m)
grass.Size = mulv(grass.Size,(m+Vector3.new(0,0.3,0))*0.9)
m = Vector3.new(math.random(1,4)/4+1,1,math.random(1,4)/4+1)
part.Size = mulv(part.Size,m)
local random = Random.new(60391)
for sx=x,x+lx,0.125 do
for sz=z,z+lz,0.125 do
local fv = n(x+1/sx,0,z+1/sz,4.123)
if fv > 0.3 then
local sub = foliage[random:NextInteger(1,#foliage)]:Clone()
sub:SetPrimaryPartCFrame(CFrame.new(Vector3.new(sx,y,sz)*params.cs)*CFrame.Angles(0,math.random(0,3)*math.pi*0.5,0))
sub.Parent = chunk.folder
sub:ScaleTo(math.random(1,10)/10 + 1)
end
if fv < -0.4 then
local sub = structures[random:NextInteger(1,#structures)]:Clone()
sub:SetPrimaryPartCFrame(CFrame.new(Vector3.new(sx-0.125,y+0.015+0.00390625,sz)*params.cs)*CFrame.Angles(0,math.random(0,3)*math.pi*0.5,0))
sub.Parent = chunk.folder
end
end
end
end
end
end
end]]
end
-- ok now more!
return getfenv()
+975
View File
@@ -0,0 +1,975 @@
local module = {}
_G.Methods = module
local tags = _G.Tags
local saveToPartTranslator = {
Barrel = "$B",
Sheet = "$S",
Stick = "$s"
}
local partToSaveTranslator = {
}
local ids = require(_G.Modules.IdModule)
module.Ids = ids
local mouse = require(_G.Modules.MouseModule)
module.Mouse = mouse
local buildParts = {}
local idParts = {}
local idNodes = {}
local polyParts = {}
module.canLoad = false
module.polyParts = polyParts
module.buildParts = buildParts
module.idParts = idParts
task.spawn(function()
require(game.ReplicatedFirst.ReadyModule)("Parts")
_G.Storage:WaitForChild("Parts")
for _,object in pairs(_G.Storage.Parts:GetDescendants()) do
if object.Parent:IsA("Folder") then
local id = object:GetAttribute("PartId")
if not id then continue end
if not object:HasTag("ConnectorObject") then
buildParts[object.Name] = object
if saveToPartTranslator[object.Name] then buildParts[saveToPartTranslator[object.Name]] = object end
idParts[id] = object
else
if object:GetAttribute("Nodes") == 3 then
if not object:FindFirstChild("A") then
warn(tostring(object).." missing sub-wedge 'A'")
end
if not object:FindFirstChild("B") then
warn(tostring(object).." missing sub-wedge 'B'")
end
end
idParts[id] = object
polyParts[id] = object
polyParts[object.Name] = object
--[[if object:FindFirstChild("A") and object:FindFirstChild("B") then
polyParts[object.Name] = object
polyParts[id] = object
elseif object:FindFirstChild("1") and object:FindFirstChild("2") then
idParts[id] = object
else
polyParts[object.Name] = object
polyParts[id] = object
idParts[id] = object
end]]
end
end
end
-- To fix old compatibility issues
local polyCompat = {
Pane = 17,
Wedge = 6,
Stick = 5,
Paper = 20,
HeavySheet = 10,
Bar = 13,
LightSheet = 14,
Rod = 13,
Rope = 22
}
for index,item in pairs(polyCompat) do
--[[local part = idParts[item]:Clone()
part.Parent = idParts[item].Parent
if part:FindFirstChild("1") then part["1"].Name = "A" end
if part:FindFirstChild("2") then part["2"].Name = "B" end]]
polyParts[index] = polyParts[item]
end
module.canLoad = true
end)
for id,part in pairs(idParts) do
local nodeId = part:GetAttribute("NodeId")
if nodeId then
idNodes[id] = idParts[nodeId]
end
end
function module.getPartByName(name)
return buildParts[name]
end
function module.getPolyByName(name)
return polyParts[name]
end
function module.getPartById(id)
return idParts[id]
end
function module.getNodeById(id)
return idNodes[id]
end
function module.setItemCFrame(item,cframe)
if item:IsA("BasePart") then
item.CFrame = cframe
elseif item:IsA("Model") then
if item.PrimaryPart then
local mcframe,size = item:GetBoundingBox()
item:SetPrimaryPartCFrame(cframe-cframe.Position+cframe:PointToWorldSpace(mcframe:PointToObjectSpace(item.PrimaryPart.Position)))
end
end
return cframe
end
function module.getItemSize(item)
if item:IsA("BasePart") then
return item.Size
elseif item:IsA("Model") then
if item.PrimaryPart then
return item:GetExtentsSize()
end
end
end
function module.getItemCFrame(item)
if item:IsA("BasePart") then
return item.CFrame
elseif item:IsA("Model") then
return item:GetBoundingBox()
end
end
function module.colorPart(part,color)
if part:IsA("Model") then
for _,subpart in pairs(part:GetDescendants()) do
if subpart:IsA("BasePart") then
subpart.Color = color
end
end
else
part.Color = color
end
part:SetAttribute("Color",color)
end
function module.shapeTriangle(p1,p2,p3,r1,r2) -- simple function, might work?
local s = 0 --0.01
local bottom = {
b1 = nil,
b2 = nil,
dist = 0,
other = nil
}
local function check(point1,point2,other)
local dist = (point1 - point2).Magnitude
if dist > bottom.dist then
bottom.b1 = point1
bottom.b2 = point2
bottom.other = other
bottom.dist = dist
end
end
check(p1,p2,p3) -- check all of the different options for bottom and get the right one?
check(p1,p3,p2)
check(p2,p3,p1)
local view = CFrame.lookAt(bottom.b1,bottom.b2,(bottom.other-bottom.b1).Unit)
local object = view:PointToObjectSpace(bottom.other)
local length = object.Z
local height = object.Y
r1.Size = Vector3.new(r1.Size.X,height-s,-length-s)
r1.CFrame = (view * CFrame.Angles(0,math.rad(180),0)) * CFrame.new(0,height/2+s/2,-length/2+s/2)
r2.Size = Vector3.new(r2.Size.X,height-s,-(-bottom.dist-length)-s)
r2.CFrame = view * CFrame.new(0,height/2+s/2,-(bottom.dist-length)/2+s/2)
return r1,r2
end
function module.positionPart(part,nodes,s)
local d = (nodes[1].Position - nodes[2].Position).Magnitude -- distance
local a = nodes[1].Position -- first node
local b = nodes[2].Position -- second node
if #nodes == 2 then -- stick on Z (XY thick)
part.CFrame = CFrame.lookAt(a,b) * CFrame.Angles(0,math.pi*0.5,0)
part.CFrame = part.CFrame + part.CFrame.RightVector * d * 0.5
part.Size = Vector3.new(d,part.Size.Y,part.Size.Z)
end
if #nodes == 3 then -- triangle on YZ (X thick)
local c = nodes[3].Position -- third node
local p2 = part["B"]
local p1 = part["A"]
module.shapeTriangle(a,b,c,p1,p2)
end
end
function module.shiftPart(part,shift)
shift = shift - (part:GetAttribute("Shift") or 0)
for _,part in pairs(part:GetChildren()) do
if part:IsA("BasePart") then
part.Position = part.Position + part.CFrame.RightVector * shift
end
end
end
function module.getNodePositions(part)
end
function module.form_rod(pa,p1,p2)
local d = (p1 - p2).Magnitude -- distance
pa.CFrame = CFrame.lookAt(p1,p2) * CFrame.Angles(0,math.pi*0.5,0)
pa.CFrame = pa.CFrame + pa.CFrame.RightVector * d * 0.5
pa.Size = Vector3.new(d,pa.Size.Y,pa.Size.Z)
end
module.saves = require(script.Parent:WaitForChild("SerialisationModule"))
spawn(function()
require(game.ReplicatedFirst.ReadyModule)("MethodCommands")
if _G.IsClient then
local commands = require(_G.Modules.CommandsModule)
commands.new("save format (%d)","set save format <number:string>: Sets current save version. Use '4'",function(v)
_G.Events.Notification:Fire("Set Save Format","Set save format to: "..v)
_G.SaveFormat = v
end)
commands.new("save comp[^%s]- (.+)","set save compress <on:bool>: Enables or disables save compression for debugging. A bad idea.",function(v)
v = not not v:match("t")
_G.Events.Notification:Fire("Set Save Compress","Set save compress to: "..tostring(v))
_G.SaveCompression = v
end)
end
end)
function module.saves.modelToData(root)
if root then
if module.saves[_G.SaveFormat] then
local success,result = xpcall(module.saves[_G.SaveFormat].encode,function(err)
print("before")
_G.Error(string.format("Save V%s Error: %s",_G.SaveFormat,err))
end,root,{
compress = _G.SaveCompression
})
if not success then return nil end
if #result.errors > 0 then print(result.errors) end
return result.data
else
return _G.Methods.dataToString(_G.Methods.modelToTable(root))
end
else
error("Root passed is nil!",2)
end
end
function module.saves.dataToModel(raw,physics,mirror)
if _G.IsString(raw) then
local v,r = raw:match("v(%d)save(.+)")
if v then
local success,result = xpcall(module.saves[v].decode,function(err)
warn(debug.traceback(err))
end,r,{
physics = physics,
mirror = mirror,
})
if not success then return nil end
if #result.errors > 0 then print(result.errors) end
if result.model then result.model:SetAttribute("Version","V"..v) end
return result.model
else
local tabular = _G.Protected(module.universalConvertToTable,raw)
if not tabular or not tabular[1] then return nil end
local model = _G.Protected(module.convertToModel,tabular,physics)
if not model then return nil end
if not tabular[1]:match("V%dSAVE") then
tabular.Name = tabular[1]
--model:AddTag("Old")
end
model:AddTag("Old")
return model
end
else
return nil
end
end
function module.tableToModel(data,physics)
end
function module.stringToData(str)
if type(str) ~= "string" then error("Not a string",2) end
local insert = table.insert
if str:sub(1,1) ~= "{" then return end
local function parse(start)
local data = {}
local function register(str)
if #str < 1 then return end
local index,part = str:match("(.*)=(.*)")
if not index then
insert(data,str)
else
data[index] = str
end
end
while start do
local first,last = str:find("[{},]",start+1,false)
local c = str:sub(last,last)
if c == "{" then
local sub_data
local index = str:sub(start+1,last-2)
sub_data,start = parse(last)
if #index > 0 then
data[index] = sub_data
else
insert(data,sub_data)
end
elseif c == "}" then
register(str:sub(start+1,last-1))
return data,last
else
if c == "," then
register(str:sub(start+1,last-1))
end
start = last
end
end
wait()
end
return parse(1)
end
function module.can_destroy(part,player)
return part:HasTag("Combat") or part.Parent:HasTag("Combat")
end
local built = {BuiltWeldConstraint = true}
function module.break_part(part,player)
if part:IsA("Model") then
for _,item in pairs(part:GetDescendants()) do
if built[item.Name] then
item:Destroy()
end
end
elseif part:IsA("BasePart") then
part:BreakJoints()
end
end
function module.wash_part(part,colour,material)
if part:IsA("Model") then
for _,item in pairs(part:GetDescendants()) do
if part:IsA("BasePart") then
part.Color = colour
part.Material = material
end
end
elseif part:IsA("BasePart") then
part.Color = colour
part.Material = material
end
end
function module.fire(root,player)
if module.can_destroy(root,player) then
if root:HasTag("Engine") then
local fire = _G.Storage.Fire:Clone()
fire.Parent = root.PrimaryPart
end
module.wash_part(root,Color3.new(1, 0.831617, 0.298161),Enum.Material.Neon)
wait(math.random(1,10)*0.333)
module.break_part(root,player)
for _,part in pairs(root:GetTouchingParts()) do
module.fire(part,player)
end
wait(math.random(1,20)*0.333)
module.wash_part(root,Color3.new(0,0,0),Enum.Material.Sand)
end
end
function module.explode(root,player,power)
print("explode")
if root:HasTag("Exploded") then return end
root:AddTag("Exploded")
local debris = game:GetService("Debris")
if not power then power = 10 end
local explosion = _G.Sounds.Explosion:Clone()
explosion.Parent = root
explosion:Destroy()
local burn = _G.Storage.Burn:Clone()
burn.Parent = root
debris:AddItem(burn,1)
local params = OverlapParams.new()
params.MaxParts = 40
params.RespectCanCollide = true
params.FilterType = Enum.RaycastFilterType.Include
params.FilterDescendantsInstances = {game.Workspace.Objects}
local result = game.Workspace:GetPartBoundsInRadius(root.Position,10,params)
wait()
for _,hit in pairs(result) do
if module.can_destroy(hit,player) then
module.break_part(hit,player)
if math.random(1,3) == 1 then module.fire(hit,player) end
end
end
wait(0.1)
for _,hit in pairs(result) do
hit:ApplyImpulse((hit.Position-root.Position)*-100)
end
end
function module.playerOwnsPart(player,part) return part:HasTag("P_"..player.UserId) or part:HasTag("P_ublic") end
function module.dataToString(data)
local insert = table.insert
local isString = {["string"] = true}
local isNumber = {["number"] = true}
local isTable = {["table"] = true}
local function converts(t)
local result = {"{"}
local first = true
for index,item in pairs(t) do
if not first then insert(result,",") else first = false end
local value
if isTable[type(item)] then
value = converts(item)
else
value = tostring(item)
end
if isNumber[type(index)] then
insert(result,value)
elseif isString[type(index)] then
insert(result,index.."="..value)
end
end
insert(result,"}")
return table.concat(result)
end
return converts(data)
end
function module.convertToData(data)
if not data:match("{.*}") then return end
data = data:gsub("%s","")
local index = 2
local function parseTable(t)
local this = {} -- The table we're working with now
local idx -- Special named table index
local _,last
local char
local value
while true do
_,last = data:find("[{,=}]",index)
if not last then return end
char = data:sub(last,last) -- The next significant character
if index ~= last then value = data:sub(index,last-1) end -- Current value in table
index = last + 1
--print(t,value,char)
local function terminate()
if idx then this[idx] = value -- If there is an index, add it by index
else table.insert(this,value) end -- Otherwise just plainly insert it (by order)
end
if char == "=" then -- When finding an '=', take the existing piece and make it an index
idx = value
elseif char == "," then -- When finding a ',', the entry has terminated so add it
terminate()
elseif char == "{" then -- When finding a '{', there is a new table whose value needs to be computed
value = parseTable(t+1)
else-- char == "}" then -- When finding a '}', this table has terminated so return
terminate()
return this
end
end
end
return parseTable(1)
end
function module.universalConvertToTable(data)
if not (data:match("{V%dSAVE,objects=")) then return module.stringToData(data)
else return module.convertToData(data) end
end
function module.removeSpaces(data)
local new = ""
for index = 1, #data do
local char = string.sub(data,index,index)
if char ~= " " then new = new .. char end
end
return new
end
function module.round(number,decimals)
local pow = math.pow(10,decimals)
return math.round(number*pow)/pow
end
function module.makeSaveString(data)
local saveDataString = "{"..data.saveName..","
local saveObjectsString = "{"
for index,item in ipairs(data.saveObjects) do
local editString = ""
local cframeString = module.removeSpaces(tostring(item.CFrame))
if item.Edit then
for _,edit in pairs(item.Edit) do
editString = editString .. "{"..edit[1]..","..edit[2].."},"
end
end
if #editString ~= 0 then editString = "{"..editString.."}," end
saveObjectsString = saveObjectsString .. "{{"
.. cframeString
.. "}" .. ","
.. item.Name .. ","..editString.."},"
end
saveObjectsString = saveObjectsString .. "}"
saveDataString = saveDataString .. saveObjectsString .. ","
local saveWeldsString = "{"
for index,item in pairs(data.saveWelds) do
saveWeldsString = saveWeldsString .. "{{"
.. tostring(item.Part0[1])
if item.Part0[2] then
saveWeldsString = saveWeldsString .. "," .. item.Part0[2]
end
saveWeldsString = saveWeldsString .. "},{"
.. tostring(item.Part1[1])
if item.Part1[2] then
saveWeldsString = saveWeldsString .. "," .. item.Part1[2]
end
saveWeldsString = saveWeldsString .. "}},"
end
saveWeldsString = saveWeldsString .. "}"
saveDataString = saveDataString .. saveWeldsString
saveDataString = saveDataString .. "}"
--print(saveDataString)
return saveDataString
end
function module.convertValue(value) -- BRUH
if value == "true" then return true
elseif value == "false" then return false
elseif tonumber(value) then return tonumber(value)
else return value end
end
function module.tableToCFrame(c)
local n = tonumber
return CFrame.new(
n(c[1]),n(c[2]),n(c[3]),
n(c[4]),n(c[5]),n(c[6]),
n(c[7]),n(c[8]),n(c[9]),
n(c[10]),n(c[11]),n(c[12])
)
end
local function weld(part1,part2)
local weld = Instance.new("WeldConstraint")
weld.Name = "BuiltWeldConstraint"
weld.Parent = part1
weld.Part0 = part1
weld.Part1 = part2
return weld
end
local function weldNoPersist(part1,part2)
weld(part1,part2).Name = "WeldConstraint"
end
local CFrameNew = CFrame.new
local CFrameFromAxisAngle = CFrame.fromAxisAngle
local tableUnpack = table.unpack
local Vector3New = Vector3.new
local n = tonumber
function module.cframeToData(cframe)
local p = cframe.Position
local r,w = cframe:ToAxisAngle()
return {{cframe.Position},{r},w}
end
local tp = table.pack
function module.cframeToDataNoAxis(cframe)
return tp(cframe:GetComponents())
end
local nc = CFrame.new
local tu = table.unpack
function module.dataToCFrameNoAxis(data)
return nc(tu(data))
end
function module.dataToCFrame(data)
if not (data[1] and data[2] and data[3]) then return CFrame.new() end
local p1,p2,p3 = tableUnpack(data[1])
local r1,r2,r3 = tableUnpack(data[2])
local w = data[3]
return CFrameNew(n(p1),n(p2),n(p3))*CFrameFromAxisAngle(Vector3New(n(r1),n(r2),n(r3)),n(w))
end
local editCompress = {Color="C",HingeCFrame="HC",Size="S"}
local editDepress = {HC="HingeCFrame",C="Color",S="Size"}
function module.modelToTable(root)
local getItemCFrame = function(object) if object:IsA("BasePart") then return object.CFrame elseif object:IsA("Model") then return object.PrimaryPart.CFrame end end
local origin = getItemCFrame(root)
local insert = table.insert
local saveData = {"V3SAVE"}
saveData.objects = {}
saveData.welds = {}
local objects = {}
--[[Agreed object structure:
{
1: name
2: {node1,node2,node3} cframes (nodes included)
3: {colour={a,b,c},size={a,b,c},speed=120} edits (size included)
}
]]
local isBuiltWeld = {["BuiltWeldConstraint"] = true}
local index = 1
ids.forEachConnectedObject(root,function(object)
insert(objects,object)
objects[object] = index
index = index + 1
local cframes = {}
local edits = {}
local nodes = object:GetAttribute("Nodes")
if nodes then
for i=1,nodes do
insert(cframes,module.cframeToDataNoAxis(origin:ToObjectSpace(object[i].CFrame)))
end
else
insert(cframes,module.cframeToDataNoAxis(origin:ToObjectSpace(getItemCFrame(object))))
end
-- edits
for index,edit in pairs(object:GetChildren()) do
if tags:HasTag(edit,"Edit") and
(edit:GetAttribute("Default") ~= edit.Value)
then
edits[editCompress[edit.Name] or edit.Name] = tostring(edit.Value)
end
end
local id = object:GetAttribute("PartId") or object.Name
--local original = module.getPartById(id) or module.getPartByName(id)
-- psuedo edits
local color = object:GetAttribute("Color")
if color then edits[editCompress["Color"]] = {color.R,color.G,color.B} end
local hinge = object:FindFirstChild("Hinge")
if hinge then edits[editCompress["HingeCFrame"]] = module.cframeToDataNoAxis(origin:ToObjectSpace(hinge.CFrame)) end
local size = object:GetAttribute("Size")
if size then size = size * math.sqrt(0.333); edits[editCompress["Size"]] = {size.X,size.Y,size.Z} end
-- final object
local final = {id,cframes}
if next(edits) then insert(final,edits) end
insert(saveData.objects,final)
end)
for index,object in ipairs(objects) do
for _,weld in pairs(object:GetDescendants()) do
if isBuiltWeld[weld.Name] then
if not weld.Part0 or not weld.Part1 then continue end
local first = objects[weld.Part0]
if not first then first = {objects[weld.Part0.Parent],weld.Part0.Name}
else first = {first} end
local second = objects[weld.Part1]
if not second then second = {objects[weld.Part1.Parent],weld.Part1.Name}
else second = {second} end
insert(saveData.welds,{first,second})
end
end
end
return saveData
end
function module.convertToModel(data,physics)
if not data then return end
local v = data[1]:match("V(%d)SAVE")
if not v or not data.objects then return module.oldConvertToModel(data,physics) end
local insert = table.insert
local objects = {}
local model = Instance.new("Model")
local toCFrame = module.dataToCFrameNoAxis
local setCFrame = function(part,cframe) if part:IsA("BasePart") then part.CFrame = cframe else part:SetPrimaryPartCFrame(cframe) end end
model:SetAttribute("Version","V3")
if v == "2" then setCFrame = module.setItemCFrame toCFrame = module.dataToCFrame model:SetAttribute("Version","V2") end
local editActions = {
Color = function(edit,part)
module.colorPart(part,Color3.new(n(edit[1]),n(edit[2]),n(edit[3])))
end,
HingeCFrame = function(edit,part)
local hinge = part:FindFirstChild("Hinge")
if hinge then hinge.CFrame = toCFrame(edit) end
end,
MaterialAttribute = function()
end,
Size = function(edit,part)
local size = Vector3.new(n(edit[1]),n(edit[2]),n(edit[3]))
if part:IsA("Model") then
part:ScaleTo(size.Magnitude)
size = (size/math.sqrt(0.333)) -- For parity with the new stronger system
else
part.Size = size
end
part:SetAttribute("Size",size)
end,
}
repeat wait() until module.canLoad
for index,object in ipairs(data.objects) do
local name = object[1]
name = n(name) or name
local isPoly = #object[2] > 1
local part = idParts[name]
local node
if not part then
if isPoly then
part = module.getPolyByName(name)
if not part then print(name) end
node = part.Parent:FindFirstChild("Node") or idParts[-1]
else part = module.getPartByName(name) end
else
if isPoly then
node = idNodes[name] or part.Parent:FindFirstChild("Node") or idParts[-1]
end
end
if not part then warn(name,isPoly,idParts,buildParts,polyParts) end
part = part:Clone()
local isModel = not part:IsA("BasePart")
local primary = part
if isModel then primary = part.PrimaryPart end
if isPoly then
local nodes = {}
for index,cframe in pairs(object[2]) do
local newNode = node:Clone()
newNode.CFrame = toCFrame(cframe)
newNode.Parent = part
newNode.Name = tostring(index)
newNode.Transparency = 1
nodes[index] = newNode
end
module.positionPart(part,nodes)
for _,subpart in pairs(part:GetChildren()) do if subpart ~= primary and subpart:IsA("BasePart") then weldNoPersist(subpart,primary) end end
else
setCFrame(part,toCFrame(object[2][1]))
end
local cnv = module.convertValue
if object[3] then
for index,edit in pairs(object[3]) do
if editDepress[index] then index = editDepress[index] end
local value = part:FindFirstChild(index)
if not value then
if editActions[index] then
editActions[index](edit,part)
end
else
value.Value = cnv(edit)
end
end
end
if not physics then
if isModel then
for _,object in pairs(part:GetChildren()) do
if object:IsA("BasePart") then
object.Anchored = true
object.CanCollide = false
end
end
else
part.CanCollide = false
part.Anchored = true
end
end
if index == 1 then if part:IsA("Model") then model.PrimaryPart = part.PrimaryPart else model.PrimaryPart = part end end
if part.Name == "VehicleSeat" then model.PrimaryPart = part.PrimaryPart end
part.Parent = model
insert(objects,part)
end
if physics then
for index,weld in pairs(data.welds) do
local weld11 = n(weld[1][1])
local weld12 = weld[1][2]
local weld21 = n(weld[2][1])
local weld22 = weld[2][2]
local part1 = objects[weld11]
local part2 = objects[weld21]
if weld12 then part1 = part1:FindFirstChild(weld12) or part1 end
if weld22 then part2 = part2:FindFirstChild(weld22) or part2 end
if not (part1 and part2) then print("Weld Missing",objects[weld11],objects[weld21],weld21,weld22) continue end
local new = Instance.new("WeldConstraint")
if part1:IsA("Model") then part1 = part1.PrimaryPart end
if part2:IsA("Model") then part2 = part2.PrimaryPart end
new.Part0 = part1
new.Part1 = part2
new.Parent = part1
new.Name = "BuiltWeldConstraint"
end
end
return model
end
function module.oldConvertToModel(data,physics)
if not data[1] then return end
local parts = {}
local n = tonumber
local currentModel = Instance.new("Model")
currentModel:SetAttribute("Version","V1")
local internalSuccess = true
local internalValue = {}
local success,value = xpcall(function()
for index,part in pairs(data[2]) do
local success,value = pcall(function()
local isPoly = #part[1] == 0
local new = module.getPartByName(part[2])
if new then new = new:Clone() end
if isPoly then
new = module.getPolyByName(part[2])
if not new then warn("Missing Poly: "..part[2]) end
local old = new
new = new:Clone()
local realNodes = {}
local attach = new
if new:IsA("Model") then attach = new.PrimaryPart end
for _,edit in pairs(part[3]) do
if #edit[1] == 1 and tonumber(edit[1]) then
local realNode = (old.Parent:FindFirstChild("Node") or _G.Storage.Parts.DefaultNode):Clone()
realNode.CFrame = module.tableToCFrame(edit[2])
realNode.Name = edit[1]
realNode.Parent = new
realNode.Transparency = 1
table.insert(realNodes,realNode)
end
end
module.positionPart(new,realNodes)
for _,node in pairs(realNodes) do
weldNoPersist(node,attach)
end
for _,part in pairs(new:GetChildren()) do
if part:IsA("BasePart") and part ~= attach then weldNoPersist(part,attach) end
end
end
if new then
table.insert(parts,new)
new.Parent = currentModel
if index == 1 then
if new:IsA("Model") then
currentModel.PrimaryPart = new.PrimaryPart else currentModel.PrimaryPart = new
end
end
if part[3] then
local cnv = module.convertValue
if typeof(part[3]) == "string" then print(part[3]) end
for _,edit in pairs(part[3]) do
if #edit ~= 1 and not tonumber(edit[1]) and not (edit[1] == "C") and new:FindFirstChild(edit[1]) then
new[edit[1]].Value = module.convertValue(edit[2])
elseif (edit[1] == "C") then
module.colorPart(new,Color3.new(cnv(edit[2][1]),cnv(edit[2][2]),cnv(edit[2][3])))
end
end
end
if not physics then
if new:IsA("Model") then
new.PrimaryPart.Anchored = true
for _,part in pairs(new:GetDescendants()) do
if part:IsA("BasePart") then part.CanCollide = false part.CanTouch = false end
end
else
new.Anchored = true
new.CanCollide = false
new.CanTouch = false
end
end
if not isPoly then
module.setItemCFrame(new,module.tableToCFrame(part[1]))
end
end
end)
if not success then internalSuccess = false table.insert(internalValue,value) end
end
if not physics then return currentModel,data end
for index,weld in pairs(data[3]) do
local new = Instance.new("WeldConstraint")
local part0 = parts[n(weld[1][1])]
if not part0 then warn("Weld missing.") continue end
if weld[1][2] and part0:FindFirstChild(weld[1][2]) then part0 = part0[weld[1][2]] end
local part1 = parts[n(weld[2][1])]
if not part1 then continue end
if weld[2][2] and part1:FindFirstChild(weld[2][2]) then part1 = part1[weld[2][2]] end
if part0:IsA("Model") then part0 = part0.PrimaryPart end
if part1:IsA("Model") then part1 = part1.PrimaryPart end
new.Part0 = part0
new.Part1 = part1
new.Parent = part0
new.Name = "BuiltWeldConstraint"
end
end,print)
if not internalSuccess then data[1] = "CORRUPTED" for _,item in pairs(internalValue) do print(data) print(item) debug.traceback(item,2) end end
--print(data)
return currentModel,data
end
local function getConnectedParts(part)
local connected = part:GetConnectedParts(false)
for _,joint in pairs(part:GetJoints()) do
if joint:IsA("Constraint") then
if joint.Attachment0 and joint.Attachment1 then
if joint.Attachment0.Parent == part then
table.insert(connected,joint.Attachment1.Parent)
else
table.insert(connected,joint.Attachment0.Parent)
end
end
end
end
return connected
end
function module.forEachConnectedObject(start,callback,show,parent)
if not start then return end
if start:IsA("Model") then start = start.PrimaryPart end
if not parent then parent = game.Workspace.Objects end
local parts = {}
local objects = {}
local function checkPart(part)
local object
if part.Parent:IsA("Model") and part.Parent.Parent == parent then
object = part.Parent
elseif part.Parent == parent then
object = part
end
if not object or objects[object] then return end
objects[object] = object
if callback then callback(object) end
end
local function tryConnected(part)
if parts[part] then return else parts[part] = part checkPart(part) end
local connected = getConnectedParts(part)
for _,part in pairs(connected) do
tryConnected(part)
end
end
tryConnected(start)
return objects
end
function module.setNetworkOwner(part,player)
for _,part in pairs(part:GetConnectedParts(true)) do
if part.Anchored then
return
end
end
part:SetNetworkOwner(player)
end
function module.getOwnedOrNil(player,part)
if module.playerOwnsPart(player,part) then
return part
elseif module.playerOwnsPart(player,part.Parent) then
return part
end
return nil
end
function module.getObjectFromPart(part)
if part:GetAttribute("PartId") then
return part
elseif part.Parent and part.Parent:GetAttribute("PartId") then
return part.Parent
end
end
module.getMouseHit = mouse.getMouseHit
return module
+56
View File
@@ -0,0 +1,56 @@
-- This module can be required and used by other scripts to get information about where the mouse is in the 3D world
local module = {}
local input = game:GetService("UserInputService")
module.mouseDistance = 500
module.mouseRaycastParameters = RaycastParams.new()
local dominantFinger
local usingMouse = true
local lastMouseInstance
local lastMouseRay
game:GetService("UserInputService").TouchStarted:Connect(function(touch,irrelevant)
if not irrelevant and not dominantFinger then
dominantFinger = touch
usingMouse = false
end
end)
game:GetService("UserInputService").TouchEnded:Connect(function(touch,irrelevant)
if touch == dominantFinger then dominantFinger = nil end
end)
local uit = Enum.UserInputType
local mouseInputs = {uit.MouseButton1,uit.MouseWheel,uit.MouseMovement,uit.MouseButton2}
for _,item in ipairs(mouseInputs) do mouseInputs[item] = true end
game:GetService("UserInputService").InputBegan:Connect(function(object)
if mouseInputs[object.UserInputType] then usingMouse = true end
end)
function module.getMouseHit(location)
local mousePosition = location or input:GetMouseLocation()
local mouseRay = game.Workspace.CurrentCamera:ViewportPointToRay(mousePosition.X,mousePosition.Y)
if dominantFinger then
local position = dominantFinger.Position
mouseRay = game.Workspace.CurrentCamera:ScreenPointToRay(position.X,position.Y)
end
local mouseInstance = game.Workspace:Raycast(mouseRay.Origin,mouseRay.Direction*module.mouseDistance,module.mouseRaycastParameters)
return mouseInstance,mouseRay,(dominantFinger or usingMouse)
end
function module.isMouseDown()
return input:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) or dominantFinger
end
function mouse()
local pos = input:GetMouseLocation()
local vec = game.Workspace.CurrentCamera:ViewportPointToRay(pos.x,pos.y,0)
return game.Workspace.CurrentCamera.CFrame:PointToObjectSpace(vec.Origin+vec.Direction*300).Unit
end
function module.initialise()
module.mouseRaycastParameters.FilterDescendantsInstances = {game.Workspace.Hidden,game.Workspace.Players}
module.mouseRaycastParameters.FilterType = Enum.RaycastFilterType.Exclude
module.mouseRaycastParameters.IgnoreWater = true
end
return module
+34
View File
@@ -0,0 +1,34 @@
NAME_COLORS =
{
Color3.new(253/255, 41/255, 67/255), -- BrickColor.new("Bright red").Color,
Color3.new(1/255, 162/255, 255/255), -- BrickColor.new("Bright blue").Color,
Color3.new(2/255, 184/255, 87/255), -- BrickColor.new("Earth green").Color,
BrickColor.new("Bright violet").Color,
BrickColor.new("Bright orange").Color,
BrickColor.new("Bright yellow").Color,
BrickColor.new("Light reddish violet").Color,
BrickColor.new("Brick yellow").Color,
}
local function GetNameValue(pName)
local value = 0
for index = 1, #pName do
local cValue = string.byte(string.sub(pName, index, index))
local reverseIndex = #pName - index + 1
if #pName%2 == 1 then
reverseIndex = reverseIndex - 1
end
if reverseIndex%4 >= 2 then
cValue = -cValue
end
value = value + cValue
end
return value
end
local color_offset = 0
function ComputeNameColor(pName)
return NAME_COLORS[((GetNameValue(pName) + color_offset) % #NAME_COLORS) + 1]
end
return getfenv()
+435
View File
@@ -0,0 +1,435 @@
-- Module for handling all game objects
require(game.ReplicatedFirst.ReadyModule)
local input = require(_G.Modules.InputModule)
-- Note that closures absorb upvalues. I'm trying this as a programming paradigm here.
local modulesByName = {}
local modules = {} -- [Name] = {...module}
local predicates = {}
local objects = {} -- Table of globally considered parts (responsible for considering) (consider = input and background tasks)
local owning = {} -- Table of locally owned parts (responsible for all processing)
-- Get the object from a part and do something to it
local function asObject(part,callback)
if part.Parent:GetAttribute("PartId") then return callback(part.Parent) -- Since parts can be INSIDE of objects
elseif part:GetAttribute("PartId") then return callback(part) end
end
local processOwnership
local context = {
controls = {},
root = nil -- Where the system is controlling from (Seat)
}
context.message = function(instance,name,...)
local stuff = {...}
asObject(instance,function(object)
local data = objects[object]
if not data then return end
if name == "" then name = "default" end
name = name or "default"
for _,module in pairs(modules[object.Name] or {}) do
if module.messages then
if module.messages[name] then
if objects[object] then
module.messages[name](data,instance,context,table.unpack(stuff))
end
end
end
end
end)
end
context.connected = function(wire_tag,end_tag,root,callback)
local searched = {}
local search = {root}
while #search > 0 do
local new_search = {}
for _,search_part in pairs(search) do
local connected = search_part:GetConnectedParts()
for _,part in pairs(connected) do
if not searched[part] then
searched[part] = true
if part:HasTag(wire_tag) then
table.insert(new_search,part)
if part:GetAttribute("PartId") and part:HasTag("Glow") then part.Material = Enum.Material.Neon end
end
if (not end_tag) or part:HasTag(end_tag) or part.Parent:HasTag(end_tag) then
asObject(part,function(object)
callback(part)
end)
end
end
end
end
search = new_search
end
end
local queues = {}
context.queue = function(this,tag,delta,valid)
-- Init
valid = valid or owning
local queue = queues[tag]
if queue then queue = {last = tick()} queues[tag] = queue end
if not queue[this] then queue[this] = this table.insert(queue,1,this) end
-- Get next in line
local first = queue[1]
while not valid[first] do table.remove(queue,1) first = queue[1] end -- Remove removed objects (lol)
-- Are you ready!?
if tick() - queue.last > delta then -- Aye aye captain!!
queue.last = tick() -- Oooo
table.remove(queue,1)
table.insert(queue,this)
return true -- hhhh
else
return false
end
end
context.frequency = function(this,tag,delta)
if not this[tag] then this[tag] = tick() end
if tick() - this[tag] > delta then
this[tag] = tick()
return true
else
return false
end
end
context.asObject = asObject
if _G.IsServer then
context.replicate = function(this,object,module,...)
module.replicate(this,object,context,...)
end
elseif _G.IsClient then
context.replicate = function(this,object,module,...)
if module.replicate then module.replicate(this,object,context,...) end
if tick() > module.next_replication then
module.next_replication = tick() + module.replication_delta
_G.Remotes.ObjectReplicate:FireServer(object,module.name,...)
end
end
end
local thisPlayer
if _G.IsServer then
thisPlayer = nil
elseif _G.IsClient then
thisPlayer = game.Players.LocalPlayer
end
-- Make object data from an instance (it's shared, so modules can communicate easily)
local function new(object)
local this = {}
for module,predicate in pairs(predicates) do
if predicate(object) then
local tab = modules[object.Name]
if not tab then tab = {} modules[object.Name] = tab end
tab[module] = module
end
end
for _,module in pairs(modules[object.Name] or {}) do
_G.Protected(module.new,this,object,context)
end
objects[object] = this
return this
end
local function remove(object)
local this = objects[object]
if not this then return end
for _,module in pairs(modules[object.Name] or {}) do
if module.remove then _G.Protected(module.remove,this,object,context) end
end
objects[object] = nil
owning[object] = nil
end
local own
-- Add an object to be considered by this client or server
local function add(instance,owner)
-- If this is not an instance or we already considered it then ignore
local data = objects[instance]
if data then return data end
if not instance:GetAttribute("PartId") then return end
data = new(instance)
objects[instance] = data
instance.Destroying:Connect(function()
remove(instance)
end)
if _G.IsServer then
if owner then
data.owner = owner
_G.Protected(function()
if instance:IsA("Model") then
instance.PrimaryPart:SetNetworkOwner(owner)
if instance:FindFirstChild("Hinge") then
instance.Hinge:SetNetworkOwner(owner)
end
else
instance:SetNetworkOwner(owner)
end
end)
else
own(instance)
end
else
if owner then own(instance) end
end
return data
end
local function update(instance)
local data = objects[instance]
if data then
for _,module in pairs(modules[instance.Name] or {}) do
if module.update then _G.Protected(module.update,data,instance,context) end
end
end
end
-- Make the client drop ownership and continue to run idle tasks
local function sleep(object)
if add(object) then owning[object] = nil end
end
-- Make the client own the object and take responsibility for processing
own = function(object)
if add(object) then owning[object] = objects[object] end
end
-- Get all parts connected to an instance, determine if the player is still connected in any way
local hrp = {HumanoidRootPart = true}
local function playerInSeat(seat)
if (seat:IsA("Seat") or seat:IsA("VehicleSeat")) and seat.Occupant then
return game.Players:GetPlayerFromCharacter(seat.Occupant.Parent)
else
local seat = seat:FindFirstChildWhichIsA("Seat") or seat:FindFirstChildWhichIsA("VehicleSeat")
if seat and seat.Occupant then
return game.Players:GetPlayerFromCharacter(seat.Occupant.Parent)
end
end
end
local vs = {VehicleSeat = true}
local function maxRankSeat(seat1,seat2)
if not seat1 then return seat2 end
if not seat2 then return seat1 end
local priority1 = seat1:GetAttribute("Priority") or 0
local priority2 = seat2:GetAttribute("Priority") or 0
-- A seat has priority over the other
if priority1 == priority2 then
local class1 = vs[seat1.Name]
local class2 = vs[seat2.Name]
-- One seat is a VehicleSeat and the other isn't
if class1 and not class2 then return seat1
elseif class2 and not class1 then return seat2
else
local owner1 = game.Players:GetPlayerByUserId(seat1:GetAttribute("Owner") or 0) == playerInSeat(seat1)
local owner2 = game.Players:GetPlayerByUserId(seat2:GetAttribute("Owner") or 0) == playerInSeat(seat2)
-- One player is seating in a seat that they placed themselves
if owner1 and not owner2 then return seat1
elseif owner2 and not owner1 then return seat2
else return seat1 -- Since it was probably closest
end
end
elseif priority1 > priority2 then return seat1 else return seat2 end
end
local function getParts(instance)
local searchedParts = {} -- Searched connected instance parts
local objectsFound = {} -- Found connected objects
local assemblyRoots = {} -- Found AssemblyRootParts
local playersSeated = {} -- Players that are connected to this vehicle in some way and their associated seat
-- Recursive function to look through all the parts
local function search(part)
-- DRY
local function add(instance)
searchedParts[instance] = true
local root = instance.AssemblyRootPart
if not assemblyRoots[root] then assemblyRoots[root] = root end
asObject(instance,function(object)
if instance:FindFirstChild("SeatWeld") then
local hrp = instance.SeatWeld.Part1
local player = game.Players:GetPlayerFromCharacter(hrp.Parent)
if player then
-- Players could be in multiple seats so just pick the highest one
playersSeated[player] = maxRankSeat(playersSeated[player],object)
end
end
objectsFound[object] = object
end)
end
-- If not found, add it, otherwise return
if not searchedParts[part] then
add(part)
else return end
-- For all the connected parts do the same thing, but not 'search' since it causes a bunch of excess checking
for _,part in pairs(part:GetConnectedParts(true)) do
-- If not found, add it, otherwise continue
if not searchedParts[part] then
add(part)
else continue end
-- If the part is connected to a Character
local joints = part:GetJoints()
for _,joint in pairs(joints) do
if joint:IsA("HingeConstraint") or joint:IsA("PrismaticConstraint") then
-- Also check joints since their network ownership is funky otherwise
if joint.Attachment0 and joint.Attachment1 then
search(joint.Attachment0.Parent)
search(joint.Attachment1.Parent)
end
end
end
end
end
search(instance)
return objectsFound,playersSeated,assemblyRoots
end
-- Out of a list of player-seats get the highest priority player seated (nil == server)
local function maxPlayerSeated(playersSeated)
local maxPlayer,maxSeat = nil
for player,seat in pairs(playersSeated) do
if maxRankSeat(maxSeat,seat) then maxPlayer = player maxSeat = seat end
end
return maxPlayer
end
local function mark(object)
if object:FindFirstChild("Mark") then object.Mark:Destroy() end
local box = Instance.new("SelectionBox")
box.Parent = object
box.Adornee = object
box.Name = "Mark"
return box
end
-- This sort of runs on every client, so maybe it's far too slow?
processOwnership = function(seat)
local objectsFound,playersSeated,assemblyRoots = getParts(seat)
local ownerPlayer = maxPlayerSeated(playersSeated)
-- The server needs to change the network owner in this case
if _G.IsServer then
_G.Remotes.OwnershipReplicate:FireAllClients(seat)
for _,root in pairs(assemblyRoots) do
local success,result = pcall(function()
root:SetNetworkOwner(ownerPlayer)
end)
if not success then warn(result) end
end
for object in pairs(objectsFound) do
local data = objects[object]
if data then data.owner = ownerPlayer end
end
if ownerPlayer then
for object in pairs(objectsFound) do
sleep(object)
end
end
end
if ownerPlayer == thisPlayer then
for _,object in pairs(objectsFound) do -- Connected and Owning
--if _G.IsClient then mark(object).Color3 = Color3.fromRGB(0,255,0) end
own(object)
end
elseif playersSeated[thisPlayer] then -- Connected and not Owning
for _,object in pairs(objects) do
if objectsFound[object] then
--if _G.IsClient then mark(object).Color3 = Color3.fromRGB(255,0,0) end
sleep(object)
end
end
elseif _G.IsClient then -- Disconnected and not Owning
for object,data in pairs(objects) do
if objectsFound[object] then
--if _G.IsClient then mark(object).Color3 = Color3.fromRGB(0,0,255) end
remove(object)
end
end
end
if _G.IsClient then
local ourSeat = playersSeated[thisPlayer]
if ourSeat then
input.seat = ourSeat
context.seat = ourSeat
elseif input.seat == asObject(seat,function(...) return ... end) then
input.seat = nil
context.seat = nil
end
end
end
local function objectsProcess(delta)
context.delta = delta
context.cursor = input.cursor
for object,data in pairs(owning) do
for _,module in pairs(modules[object.Name] or {}) do
if module.run then if not module.run(data,object,context) then remove(object) end end
end
end
for object,data in pairs(objects) do
for _,module in pairs(modules[object.Name] or {}) do
if module.idle then if not module.idle(data,object,context) then remove(object) end end
end
end
for _,module in pairs(modulesByName) do
if module.loop then
module.loop(context)
end
end
end
if _G.IsServer then
-- Clients and servers will send this to update information between each other (should be handled by values)
_G.Remotes.ObjectReplicate.OnServerEvent:Connect(function(player,object,name,...)
local data = objects[object]
if data and data.owner == player then
modulesByName[name].replicate(data,object,context,...)
end
end)
game.Players.PlayerRemoving:Connect(function(player)
for object,data in pairs(objects) do
if data.owner == player then
own(object)
end
end
end)
elseif _G.IsClient then
-- Respond to processing events from the server
_G.Remotes.OwnershipReplicate.OnClientEvent:Connect(function(seat)
processOwnership(seat)
end)
end
for _,instance in pairs(script.Parent.Objects:GetChildren()) do
local module = require(instance)
modulesByName[instance.Name] = module
module.name = instance.Name
module.next_replication = tick()
module.replication_delta = module.replication_delta or 0.5
if module.manifest.predicate then
predicates[module] = module.manifest.predicate
else
for _,name in pairs(module.manifest) do
modules[name] = modules[name] or {}
table.insert(modules[name],module)
end
end
end
if _G.IsServer then
for _,instance in pairs(game.Workspace.Objects:GetChildren()) do
add(instance)
end
end
spawn(function()
_G.Run.Heartbeat:Connect(function(delta)
if _G.IsClient then context.controls = input.values end
objectsProcess(delta)
end)
end)
return {processOwnership = processOwnership, add = add, update = update, own = own}
+242
View File
@@ -0,0 +1,242 @@
require(game.ReplicatedFirst.ReadyModule)
-- This is a script that handles a tool for creating objects
local player = game.Players.LocalPlayer
-- Gui Objects
local paintFrame = _G:WaitFor("PaintFrame")
--local paintSelection = paintFrame:WaitForChild("ScrollingFrame")
--local paintDefault = paintSelection:WaitForChild("Default")
local userInputEvent = nil
local renderEvent = nil
local mouseModule = require(_G.Modules.MouseModule)
local currentColor = Color3.new(1, 1, 1)
local lastPainted = nil
local input = game:GetService("UserInputService")
local partStateModule = require(_G.Modules.PartStateModule)
local partState = partStateModule.new()
local paintFrame = _G:WaitFor("PaintFrame")
local colorPickerFrame = paintFrame.ColorPickerFrame
local colorSelectorFrame = paintFrame.ColorSelectorFrame
local colorLabel = paintFrame.ColorLabel
local materialSelectorFrame = paintFrame.MaterialSelectorFrame
local materialLabel = paintFrame.MaterialLabel
local polyFrame = paintFrame.PolyFrame
local polyLabel = paintFrame.PolyLabel
-- local label = game:GetService("Selection"):Get()[1] for name,id in ([[DiamondPlate7546654401Metal7547178395Wood7547190453WoodPlanks7547301709SmoothPlastic Rubber14108673018Neon Glass7547304577]]):gmatch("([a-zA-Z]+)([0-9]*)") do local l = label:Clone() l.Parent = label.Parent l.Image = "rbxassetid://"..id l.Name = name end
local hues = {}
local hue = Color3.new(0,0,1)
local finalColor = nil
local function makeColorGradient()
local n = tonumber
local t = {}
for r,g,b in ("100101001011010110100"):gmatch("(.)(.)(.)") do
local color = Color3.new(n(r),n(g),n(b))
--table.insert(t,ColorSequenceKeypoint.new(#t/7,color))
table.insert(hues,color)
end
--return ColorSequence.new(t)
end
makeColorGradient()
local function getHue(value)
local index1 = math.clamp(math.floor(value*6)+1,1,7)
local index2 = math.clamp(math.floor(value*6)+2,2,7)
local a = hues[index1]
local b = hues[index2]
local alpha = value*6 - math.floor(value*6)
print(value,index1,index2,alpha)
return a:Lerp(b,alpha)
end
local function pickerFactory(object,callback,use_x,use_y)
local held
local inputs = {[Enum.UserInputType.MouseButton1]=true,[Enum.UserInputType.Touch]=true,[Enum.UserInputType.MouseMovement]=true}
local function changed(input)
if input == held or (held and input.UserInputType == Enum.UserInputType.MouseMovement) then
local p = object.Pointer.Position
local s = object.Pointer.AbsoluteSize
local x_scale,y_scale,x_offset,y_offset,x_value,y_value
if use_x then
x_scale = math.clamp((input.Position.X - object.AbsolutePosition.X) / (object.AbsoluteSize.X + s.X),0,object.AbsoluteSize.X/(object.AbsoluteSize.X + s.X))
x_value = math.clamp((input.Position.X - object.AbsolutePosition.X) / (object.AbsoluteSize.X),0,1)
x_offset = s.X/2
else
x_scale = p.X.Scale
x_offset = p.X.Offset
end
if use_y then
y_scale = math.clamp((input.Position.Y - object.AbsolutePosition.Y) / (object.AbsoluteSize.Y + s.Y),0,object.AbsoluteSize.Y/(object.AbsoluteSize.Y + s.Y))
y_value = math.clamp((input.Position.Y - object.AbsolutePosition.Y) / (object.AbsoluteSize.Y),0,1)
y_offset = s.Y/2
else
y_scale = p.Y.Scale
y_offset = p.Y.Offset
end
object.Pointer.Position = UDim2.new(x_scale,x_offset,y_scale,y_offset)
callback(x_value,y_value)
end
end
object.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
held = input
changed(input)
end
end)
object.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
held = nil
end
end)
input.InputChanged:Connect(changed)
end
local saturation = 0
local brightness = 0
local function updateColor()
finalColor = hue:Lerp(Color3.new(1,1,1),saturation):Lerp(Color3.new(0,0,0),brightness)
colorSelectorFrame.Preview.BackgroundColor3 = finalColor
colorLabel.CancelButton.Visible = true
end
pickerFactory(colorPickerFrame.Hue,function(_,y)
local color = getHue(y)
hue = color
colorPickerFrame.Picker.BackgroundColor3 = color
updateColor()
end,false,true)
pickerFactory(colorPickerFrame.Picker,function(x,y)
saturation = 1-x
brightness = y
updateColor()
end,true,true)
local colorPicking = false
colorSelectorFrame.PickButton.Activated:Connect(function()
colorPicking = true
colorSelectorFrame.PickButton.BackgroundColor3 = Color3.new(0.5,1,0.5)
end)
colorLabel.CancelButton.Activated:Connect(function()
colorLabel.CancelButton.Visible = false
finalColor = nil
end)
local finalMaterial = nil
local materialStroke = materialSelectorFrame:WaitForChild("UIStroke")
materialStroke.Enabled = true
materialStroke.Parent = nil
for _,item in pairs(materialSelectorFrame:GetChildren()) do
if item:IsA("ImageButton") then
item.Activated:Connect(function()
finalMaterial = Enum.Material[item.Name]
materialLabel.CancelButton.Visible = true
materialStroke.Parent = item
end)
end
end
materialLabel.CancelButton.Activated:Connect(function()
materialLabel.CancelButton.Visible = false
materialStroke.Parent = nil
finalMaterial = nil
end)
local finalShift = nil
local finalThickness = nil
polyFrame.ShiftButton.Activated:Connect(function()
if not finalShift then finalShift = 1
elseif finalShift == 1 then finalShift = -1
elseif finalShift == -1 then finalShift = nil end
if not finalShift then polyFrame.ShiftButton.Text = "Shift Off"
elseif finalShift == -1 then polyFrame.ShiftButton.Text = "Shift Out"
elseif finalShift == 1 then polyFrame.ShiftButton.Text = "Shift In" end
polyLabel.CancelButton.Visible = true
end)
polyFrame.ThicknessBox.FocusLost:Connect(function(enter)
if enter then
finalThickness = tonumber(polyFrame.ThicknessBox.Text) or nil
if finalThickness < 0.05 then finalThickness = 0.05 end
local text = ""
if finalThickness then text = tostring(finalThickness).." studs" end
polyFrame.ThicknessBox.Text = text
end
polyLabel.CancelButton.Visible = true
end)
polyLabel.CancelButton.Activated:Connect(function()
polyLabel.CancelButton.Visible = false
finalShift = nil
finalThickness = nil
polyFrame.ShiftButton.Text = "Shift Off"
polyFrame.ThicknessBox.Text = ""
end)
polyLabel.CancelButton.Visible = false
colorLabel.CancelButton.Visible = false
materialLabel.CancelButton.Visible = false
local function render(delta)
local result = mouseModule.getMouseHit()
if result and result.Instance then
local object = _G.Methods.getObjectFromPart(result.Instance,player)
if object then script.SelectionBox.Adornee = object else script.SelectionBox.Adornee = nil end
script.SelectionBox.FillTransparency = 1
if not mouseModule.isMouseDown() then return end
script.SelectionBox.FillTransparency = 0.5
if colorPicking then
finalColor = result.Instance.Color
colorSelectorFrame.Preview.BackgroundColor3 = finalColor
colorPicking = false
colorSelectorFrame.PickButton.BackgroundColor3 = Color3.new(1,1,1)
if object then lastPainted = object end
return
end
if lastPainted == object then return end
lastPainted = object
if not lastPainted then return end
local state = partStateModule.new()
if finalColor then state.color = finalColor end
if finalShift then state.look = game.Workspace.CurrentCamera.CFrame.LookVector state.shift = finalShift end
if finalThickness then state.size = Vector3.new(finalThickness,0,0) end
if finalMaterial then state.material = finalMaterial end
_G.Remotes.Apply:FireServer(lastPainted,state:serialise())
else
lastPainted = nil
script.SelectionBox.Adornee = nil
end
end
paintFrame.Visible = false
local function init(s)
if renderEvent then renderEvent:Disconnect() end
s.Parent.Equipped:Connect(function()
if renderEvent then renderEvent:Disconnect() end
renderEvent = game:GetService("RunService").RenderStepped:Connect(render)
paintFrame.Visible = true
end)
s.Parent.Unequipped:Connect(function()
if renderEvent then renderEvent:Disconnect() end
paintFrame.Visible = false
script.SelectionBox.Adornee = nil
end)
end
return {init = init}
+14
View File
@@ -0,0 +1,14 @@
-- module which serves the purpose of providing a universal function so that I can change how checks for whether a player is allowed to activate something works (such as collaborative building)
local module = {}
module.tagService = _G.Tags
function module.activator(realScript,callback)
local clickDetector = realScript.Parent:FindFirstChildWhichIsA("ClickDetector")
clickDetector.MouseClick:Connect(function(player)
if module.tagService:HasTag(realScript.Parent,"P_"..player.UserId) or module.tagService:HasTag(realScript.Parent.Parent,"P_"..player.UserId) then
callback(player)
end
end)
end
return module
+508
View File
@@ -0,0 +1,508 @@
local module = {}
-- PARTS TABLE
local parts = {}
_G.Storage:WaitForChild("Parts")
local function added(object)
if object.Parent:IsA("Folder") then
local id = object:GetAttribute("PartId")
if id then parts[id] = object end
end
end
for _,object in pairs(_G.Storage.Parts:GetDescendants()) do
added(object)
end
_G.Storage.Parts.DescendantAdded:Connect(function(object)
if object:GetAttribute("PartId") then
warn(string.format("%i:%s, you are late!",object:GetAttribute("PartId"),object:GetFullName()))
end
added(object)
end)
function module.get_template_part(part)
local id = part:GetAttribute("PartId")
if not id then return end
local template = parts[id]
return template
end
function module.get_part_by_id(id)
return parts[id]
end
function module.change_material(part,material)
if part.Material == Enum.Material.Glass then
part.Transparency = part:GetAttribute("OriginalTransparency",part.Transparency)
end
if material == Enum.Material.Glass then
part:SetAttribute("OriginalTransparency",part.Transparency)
part.Transparency = 0.5
end
part.Material = material
end
-- PART METHODS
function module.apply_material(part,material)
if material then
part:SetAttribute("Material",material.Name)
if part:IsA("Model") then
for _,subpart in pairs(part:GetDescendants()) do
if subpart:IsA("BasePart") then
module.change_material(subpart,material)
end
end
else
module.change_material(part,material)
end
end
end
function module.apply_edit(part,index,value)
part[index].Value = value
end
function module.get_size(part)
local nodes = module.get_nodes_class(part)
if nodes == 1 then
return part:GetAttribute("Size")
elseif nodes == 2 then
return Vector3.new(part.Size.Z,0,0) -- Yeah the bloody irony I know right?
elseif nodes >= 3 then
return Vector3.new(part:FindFirstChildWhichIsA("Part").Size.X,0,0)
end
end
function module.get_thickness(part)
return module.get_size(part).X -- WHAT?!
end
function module.apply_size(part,size)
part:SetAttribute("Size",size)
local nodes = module.get_nodes_class(part)
if nodes == 1 then
if part:IsA("Model") then
part:ScaleTo(size.X)
elseif part:IsA("BasePart") then
if size.Y == 0 or size.Z == 0 then return end -- Since this is not for normal parts it's a poly size
part.Size = size
end
elseif nodes == 2 then
part.Size = Vector3.new(part.Size.X,size.X,size.X) -- Yeah, this is serious
elseif nodes >= 3 then
for _,item in pairs(part:GetChildren()) do
item.Size = Vector3.new(size.X,item.Size.Y,item.Size.Z)
end
end
end
function module.apply_color(part,color)
part:SetAttribute("Color",color)
if part:IsA("Model") then
for _,subpart in pairs(part:GetDescendants()) do
if subpart:IsA("BasePart") then
subpart.Color = color
end
end
else
part.Color = color
end
end
local function Weld(part1,part2,temporary) -- Make a BUILDING weld
local weld = Instance.new("WeldConstraint")
weld.Name = "BuiltWeldConstraint"
weld.Parent = part1
weld.Part0 = part1
weld.Part1 = part2
if temporary then weld.Name = "WeldConstraint" end -- Or not.
return weld
end
function module.weld(part,connection,temporary)
local attach = part
if part:IsA("Model") then attach = attach.PrimaryPart end
local new_weld = Weld(attach,connection,temporary)
new_weld.Parent = part
return new_weld
end
function module.form_triangle(pa,pb,p1,p2,p3,s2)
local s1 = pa.Size.X
s2 = s2 or s1*0.5
s2 = 0
local p12 = p2-p1
local p23 = p3-p2
local p13 = p3-p1
local p12l = p12.Magnitude
local p23l = p23.Magnitude
local p13l = p13.Magnitude
local bottom
local bot_len
local totop
local root
local tip
local far
local t
if p12l > p23l and p12l > p13l then -- TRUE: p12 largest FALSE: p12 smaller than p23l or p13l
tip = p3
root = p1
far = p2
bottom = p12
bot_len = p12l
totop = p13
--[[pa.Color = Color3.new(1,0,0)
pb.Color = Color3.new(1,0,0)]]
elseif p23l > p13l then -- TRUE: p23 must be largest FALSE: p13 largest
tip = p1
root = p2
far = p3
bottom = p23
bot_len = p23l
totop = -p12
--[[pa.Color = Color3.new(0,1,0)
pb.Color = Color3.new(0,1,0)]]
else -- p13 largest
tip = p2
root = p1
far = p3
bottom = p13
bot_len = p13l
t = pa
pa = pb
pb = t
--[[pa.Color = Color3.new(0,0,1)
pb.Color = Color3.new(0,0,1)]]
totop = p12
end
bottom = bottom.Unit
local coeff = totop:Dot(bottom)
local mid = root+coeff*bottom
local cross = totop:Cross(bottom).Unit
local top = (mid-tip)
pa.Size = Vector3.new(s1,top.Magnitude,coeff)
pb.Size = Vector3.new(s1,top.Magnitude,bot_len - coeff)
top = top.Unit
pa.CFrame = CFrame.fromMatrix(s2*cross+(root+tip)*0.5,cross,-top,bottom)
pb.CFrame = CFrame.fromMatrix(s2*cross+(tip+far)*0.5,-cross,-top,-bottom)
end
function module.get_tri_positions1(pa,pb)
local ca = pa.CFrame
local ra = ca.Rotation
local cb = pb.CFrame.Position
local sa = pa.Size
local lb = pb.Size.Z
local lh = sa.Y
local fb = Vector3.new(0,lh,-lb)
return (cb - ra * (fb * 0.5)) -- FOR THIRD RETURN VALUE OF BELOW
end
function module.get_tri_positions2(pa,pb)
local ca = pa.CFrame
local ra = ca.Rotation
ca = ca.Position
local sa = pa.Size
local la = sa.Z
local lh = sa.Y
local fa = Vector3.new(0,lh,la)
return (ca + ra * (fa * -0.5)),(ca + ra * (fa * 0.5))
end
-- apply data positions of parts
local form_triangle = module.form_triangle
local number_to_string = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}
local string_to_number = {}
for num,str in pairs(number_to_string) do
string_to_number[str] = num
end
--; for i = 1,10 do number_to_string[i] = tostring(i) end
local mod = CFrame.Angles(0,0,0)
function module.apply_nodes(part,nodes)
if #nodes < module.get_nodes_class(part) then return true end
if #nodes == 1 then
part:PivotTo(nodes[1])
elseif #nodes == 2 then
part.CFrame = nodes[1]*mod
part.Size = Vector3.new(nodes[2],part.Size.Y,part.Size.Z)
local first = part:FindFirstChild("1")
local second = part:FindFirstChild("2")
local p1,p2 = module.get_stick_ends(part)
local offset = Vector3.new(nodes[2]*0.5,0,0)
if first then
first.Position = nodes[1]:PointToWorldSpace(offset)
if first:FindFirstChild("WeldConstraint") then
first.WeldConstraint.Enabled = true
end
end
if second then
second.Position = nodes[1]:PointToWorldSpace(-offset)
if second:FindFirstChild("WeldConstraint") then
second.WeldConstraint.Enabled = true
end
end
elseif #nodes >= 3 then
local template = part["A"]
for i=1,(#nodes-2)*2 do
local name = number_to_string[i]
if not part:FindFirstChild(name) then
local piece = template:Clone()
piece.Parent = part
piece.Name = name
end
end
for i=1,(#nodes-2) do
form_triangle(part[number_to_string[i*2-1]],part[number_to_string[i*2]],nodes[1],nodes[i+1],nodes[i+2])
end
end
end
-- get the poly 'type' of a part
function module.get_nodes_class(part)
return part:GetAttribute("Nodes") or 1
end
-- get data positions of parts
function module.get_nodes(part)
local nodes = part:GetAttribute("Nodes")
if not nodes then
return {part:GetPivot()}
elseif nodes == 2 then
local first = part:FindFirstChild("1")
local second = part:FindFirstChild("2")
if first and second then
local offset = second.Position - first.Position
return {CFrame.lookAt(first.Position,second.Position,first.CFrame.UpVector)*CFrame.Angles(0,math.pi*0.5,0) + offset * 0.5,offset.Magnitude}
else
return {part.CFrame,part.Size.X}
end
elseif nodes >= 3 then
local i = -1
local p1 = part["A"]
local p2 = part["B"]
local t = {module.get_tri_positions2(p1,p2)}
while true do
i = i + 2
local pn1 = part:FindFirstChild(number_to_string[i])
if not pn1 then return t end
local pn2 = part:FindFirstChild(number_to_string[i+1])
if not pn2 then return t end
table.insert(t,module.get_tri_positions1(p1,p2))
end
return t
end
end
-- shift a poly part along its normal axis while keeping static node positions
function module.apply_shift(part,shift)
if module.get_nodes_class(part) < 3 then return end
local previous = part:GetAttribute("Shift") or 0
if shift == 0 and previous == 0 then part:SetAttribute("Shift",nil) return end
part:SetAttribute("Shift",shift)
shift = shift - previous
for _,item in pairs(part:GetChildren()) do
if item:IsA("BasePart") and not item:HasTag("Node") then
local neg = (string_to_number[item.Name] % 2 == 0)
if neg then neg = -1 else neg = 1 end
item.Position = item.Position + item.CFrame.RightVector * item.Size.X * 0.5 * shift * neg
end
end
end
-- stupid name, it just tries to shift in the direction based on how we're looking
function module.attempt_shift(part,shift,look)
local template = part:FindFirstChild("A")
if not template then return end
module.apply_shift(part,math.sign(template.CFrame.RightVector:Dot(look)) * shift)
end
-- get the cframe of a single connection
function module.eval_connection(connection)
local connected = connection.connected
local relative = CFrame.identity
if connected then relative = connected:GetPivot() end
return relative:ToWorldSpace(connection.CFrame)
end
function module.get_stick_ends(part)
local nodes = module.get_nodes(part)
if #nodes ~= 2 then error("Part is not a stick",2) end
local cframe = nodes[1]
local offset = Vector3.new(nodes[2]*0.5,0,0)
return cframe:PointToWorldSpace(offset),cframe:PointToWorldSpace(-offset)
end
function module.get_points(part)
local class = module.get_nodes_class(part)
if class == 1 then return {}
elseif class == 2 then return {module.get_stick_ends(part)}
else return module.get_nodes(part) end
end
-- apply driven connections by attachments in the placement engine
function module.eval_connections(part,connections)
local class = module.get_nodes_class(part)
local cframes = {}
for _,connection in ipairs(connections) do
table.insert(cframes,module.eval_connection(connection))
end
local nodes = {}
if class == 1 then
nodes = cframes
elseif class == 2 then
if #cframes == 2 then
local to = cframes[2].Position - cframes[1].Position
nodes = {CFrame.lookAt(cframes[1].Position,cframes[2].Position,cframes[1].UpVector)*CFrame.Angles(0,math.pi*0.5,0) + (to * 0.5), to.Magnitude}
end
elseif class == 3 then
for _,cframe in ipairs(cframes) do table.insert(nodes,cframe.Position) end
end
return nodes
end
function module.apply_internal_welds(part)
if part:IsA("Model") then
local main = part.PrimaryPart
for _,item in pairs(part:GetChildren()) do
if item:IsA("BasePart") and item ~= main then Weld(item,main,true) end
end
end
end
-- PART STATE METHODS
local _state = {}
_state.__index = _state
function _state.apply_material(this)
if this.material then module.apply_material(this.part,this.material) end
end
function _state.apply_size(this)
if this.size then module.apply_size(this.part,this.size) end
end
function _state.apply_color(this)
if this.color then module.apply_color(this.part,this.color) end
end
function _state.apply_edit(this,index,value)
local edit = this.part:FindFirstChild(index)
if not edit then error("Could not find edit "..index,2) end
module.apply_edit(this.part,index,value)
end
function _state.apply_edits(this)
for index,value in pairs(this.edits) do
this:apply_edit(index,value)
end
end
function _state.attempt_shift(this,...)
module.attempt_shift(this.part,...)
end
function _state.apply_internal_welds(this)
if module.get_nodes_class(this.part) > 1 then -- Weld together parts inside of poly
module.apply_internal_welds(this.part)
end
end
function _state.apply_welds(this)
this:apply_internal_welds()
for index,connection in pairs(this.welds) do
local target = this.part
if connection.name then target = this.part[connection.name] end
if target:IsA("Model") then target = target.PrimaryPart end
if (not this.owner) or (_G.Methods.getOwnedOrNil(this.owner,connection.part)) then
module.weld(target,connection.part)
end
end
end
function _state.apply_connections(this)
this.nodes = module.eval_connections(this.part,this.connections)
end
function _state.apply_nodes(this)
module.apply_nodes(this.part,this.nodes)
end
function _state.apply_shift(this)
if this.look then this:attempt_shift(this.shift or 0,this.look)
else module.apply_shift(this.part,this.shift or 0) end
end
function _state.apply(this,part,options)
options = options or {}
this.part = part or this.part
if this.color then this:apply_color() end
if this.size then this:apply_size() end
if this.material then this:apply_material() end
if this.edits and not options.no_edit then this:apply_edits() end
if this.connections then this:apply_connections() end
if this.nodes then this:apply_nodes() end
if this.welds and not options.no_weld then this:apply_welds() else this:apply_internal_welds() end
if this.shift then this:apply_shift() end
end
function _state.create(this)
if not this.id then error("No id",2) end
local template = parts[this.id]:Clone()
template.Parent = game.Workspace.Objects
this:apply(template)
return template
end
function _state.get_nodes_class(this)
return module.get_nodes_class(this.part)
end
function _state.take(this,existing)
this.color = existing:GetAttribute("Color")
this.material = existing:GetAttribute("Material")
this.size = existing:GetAttribute("Size")
this.shift = existing:GetAttribute("Shift")
this.edits = {}
for _,edit in existing:GetChildren() do
if edit:HasTag("Edit") then
if edit.Value ~= (edit:GetAttribute("Default") or nil) then
this.edits[edit.Name] = edit.Value
end
end
end
this.nodes = module.get_nodes(existing)
return this
end
function _state.update(this)
this:take(this.part)
return this
end
function _state.copy(this,existing)
this:take(existing)
this.id = existing:GetAttribute("PartId")
this.part = parts[this.id]:Clone()
this:apply(nil,{no_weld = true})
return this
end
function module.copy(existing)
return module.new():copy(existing)
end
function module.new(original)
return setmetatable(_G.Union({
material = nil, -- Enum.Material SERIAL
color = nil, -- Color3.new() SERIAL
size = nil, -- Vector3.new() SERIAL
shift = nil, -- 1 -> 0 -> -1 SERIAL
connections = {}, -- {[n]={cframe=,connection=}} for placement engine etc
welds = {}, -- {{name=,touched=},...},... SERIAL
edits = {}, -- ? SERIAL
owner = nil, -- player
part = nil, -- part being worked with (applied)
id = nil -- part id SERIAL
},original or {}),_state)
end
-- SERIALISATION
--[[local function serialisation_factory(match_string)
local serialisation_table = {}
for index in match_string:gmatch("[^,]+") do
table.insert(serialisation_table,index)
end
return {
serialise = function(this)
local t = {}
for _,key in pairs(serialisation_table) do
table.insert(t,this[key])
end
return table.unpack(t)
end,
deserialise = function(this,...)
local t = {...}
for index,key in pairs(serialisation_table) do
this[key] = t[index]
end
end,
}
end
local state_serialiser = serialisation_factory("id,connections,color,size,shift,material,edits,welds")]]
_state.serialise = function(this) --state_serialiser.serialise
local color
if this.color then color = {this.color.R,this.color.G,this.color.B} end
return this.id,this.connections,color,this.size,this.shift,this.material,this.edits,this.welds,this.look
end
_state.deserialise = function(this,id,connections,color,size,shift,material,edits,welds,look) --state_serialiser.deserialise
if color then this.color = Color3.new(table.unpack(color)) end
this.id = id
this.connections = connections
this.size = size
this.shift = shift
this.material = material
this.edits = edits
this.welds = welds
this.look = look
end
return module
+627
View File
@@ -0,0 +1,627 @@
local run = game:GetService("RunService")
local inputService = game:GetService("UserInputService")
local collectionService = _G.Tags
local mouseModule = require(_G.Modules.MouseModule)
local methodModule = require(_G.Modules.MethodModule)
local partStateModule = require(_G.Modules.PartStateModule)
local selectionBox = script.SelectionBox
local adjacentBox = script.AdjacentBox
local statGui
if game.Players.LocalPlayer then
statGui = game.Players.LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("StatGui")
end
local gui
local current = nil
local mode = "place"
local realMode = "place"
local rotSnap = 15
local rotSnapKey = 45.
local moveSnap = 0.5
local modelScaleSnap = 0.1
local touchModeConnect = true
local touchingBoxes = {}
local controlModifier = false -- embed
local shiftModifier = false -- lower snap
local ModeButtons = {
clone = nil,
resize = nil
}
local pointer = nil
local function temp()
local a = Instance.new("Part")
a.Parent = game.Workspace
a.Size = Vector3.new(1,1,1)
a.Anchored = true
a.CanCollide = false
a.Transparency = 0.5
a.Material = Enum.Material.Neon
return a
end
local a = temp()
local b = temp()
local modeEnd = function()
if current.axes then current.axes:Destroy() end
end
local focusAxes = function()
current.axes:ScaleTo(current.size.Magnitude*1.3)
methodModule.setItemCFrame(current.axes,current.cframe)
for _,part in pairs(current.axes:GetChildren()) do
if not current.axis then
part.Transparency = 0.5
else
part.Transparency = 1
end
end
if current.axis then current.axis.Transparency = 0 end
end
local createAxes = function(name)
current.axes = _G.Storage.Assets[name]:Clone()
current.axes.Parent = game.Workspace
current.axes.PrimaryPart.Shape = Enum.PartType.Ball
focusAxes()
end
local getBounds = function()
local cframe,size
if current.model:IsA("Model") then
cframe,size = current.model:GetBoundingBox()
else
cframe = current.model.CFrame
size = current.model.size
end
current.cframe = cframe
current.size = size
end
local styleModel = function()
local function style(part)
part.Anchored = true
part.CanCollide = false
end
if current.model:IsA("BasePart") then style(current.model) end
for _,part in pairs(current.model:GetChildren()) do
if part:IsA("BasePart") then
style(part)
end
end
end
local distanceAlong = function(o : Vector3,n : Vector3,p : Vector3) -- origin, normal, point
local k = (n.X*(p.X-o.X)+n.Y*(p.Y-o.Y)+n.Z*(p.Z-o.Z))--/(n.x*n.x+n.y*n.y+n.z*n.z) -- is 1
return (n*k+o-p).Magnitude
end
local processPointer = function(inputObject)
local position = inputObject.Position
local result,ray = mouseModule.getMouseHit() -- get whatever we're hovering
current.location = position
current.ray = ray
current.result = result
end
local function axisClick(inputObject) -- For whenever we click in a mode supporting axis
if current.axes then -- if there are axes (there should be)
processPointer(inputObject)
if current.result then
if current.result.Instance.Parent == current.axes then -- if it's an axis
current.axis = current.result.Instance -- set it
pointer = inputObject -- set the inputObject for current selection
end
end
end
end
local function round(v : Vector3,snap) -- just round a vector based on the snapping
snap = snap or moveSnap
return Vector3.new(math.round(v.X/snap)*snap,math.round(v.Y/snap)*snap,math.round(v.Z/snap)*snap)
end
local function mask(a : Vector3,b : Vector3) -- multiply each axis together
return Vector3.new(a.X*b.X,a.Y*b.Y,a.Z*b.Z)
end
local function clamp(a : Vector3, min : Vector3, max : Vector3)
return Vector3.new(math.clamp(a.X,min.X,max.X),math.clamp(a.Y,min.Y,max.Y),math.clamp(a.Z,min.Z,max.Z))
end
local function rayProjectToRay(ray,axis,root)
local n2 = ray.Direction
local o2 = ray.Origin
local n1 = (axis.Position - axis.Parent.PrimaryPart.Position).Unit
local o1 = root or current.axis.Position
-- using more vector magic to find the closest point on the axis line to the ray of the pointer
local s = n2:Dot(n1)
return o1+n1*((n1:Dot(o2-o1)+s*(n2:Dot(o1-o2)))/(1-s*s))
end
local function axisDragClick(inputObject)
axisClick(inputObject)
if current.axis then
local p = rayProjectToRay(current.ray,current.axis)
current.modelOrigin = current.touchingCFrame
current.dragOrigin = current.axes.PrimaryPart.CFrame:PointToObjectSpace(p)
current.axisOrigin = current.touching.CFrame:ToObjectSpace(current.axis.CFrame)
end
end
local function rayProjectToPlane(ray,axis)
-- using some vector magic to find the point of intersection between the ray of the pointer and the plane of rotation (arc handle plane)
return ray.Origin+(axis.CFrame.RightVector:Dot(axis.Position-ray.Origin))/(axis.CFrame.RightVector:Dot(ray.Direction))*ray.Direction
end
local function axisRotateClick(inputObject)
axisClick(inputObject)
if current.axis then
local p = rayProjectToPlane(current.ray,current.axis)
current.dragOrigin = current.axis.CFrame:PointToObjectSpace(p)
current.modelOrigin = current.touchingCFrame
current.axisOrigin = current.touching.CFrame:ToObjectSpace(current.axis.CFrame)
current.initialRotation = current.rotation
end
end
local modeStart = {}
modeStart.clone = function()
modeEnd()
end
modeStart.rotate = function()
modeEnd()
createAxes("RotateAxes")
end
modeStart.resize = function()
modeEnd()
createAxes("ResizeAxes")
for _,item in pairs(current.axes:GetChildren()) do
local axis = item.Name:match("Part(.)")
if axis then
if (current.scaleTerms or Vector3.one)[axis] == 0 then axis:Destroy() end -- if the scale terms say that this axis should not be scaled
if current.model:IsA("Model") then item.Color = Color3.fromRGB(13, 105, 172) end
end
end
end
modeStart.move = function()
modeEnd()
createAxes("MoveAxes")
end
modeStart.place = function()
modeEnd()
end
local function setMode(newMode)
for _,other_button in pairs(_G.PlaceModeFrame:GetChildren()) do
if other_button:IsA("TextButton") then
other_button.BackgroundColor3 = Color3.new(1,1,1)
if (current.disabled or {})[other_button.Name] then other_button.Visible = false else other_button.Visible = true end
end
end
realMode = newMode
mode = newMode
gui.Confirm.Text = "Place"
if not current.touching and newMode ~= "place" then
mode = "place"
gui.Confirm.Text = "Confirm"
end
_G.PlaceModeFrame[realMode].BackgroundColor3 = Color3.new(1, 0.956863, 0.345098)
_G.PlaceModeFrame[mode].BackgroundColor3 = Color3.new(0.6, 1, 0.333333)
modeStart[mode]()
end
local function confirmPlace(inputObject,forced)
if not current then return end
if realMode ~= mode then setMode(realMode) return end
if (inputObject and inputObject.UserInputType == Enum.UserInputType.MouseButton1) or not inputObject then
current:confirm(forced)
if not current then return end
methodModule.setItemCFrame(current.model,CFrame.new())
current.touching = nil
current.touchingCFrame = CFrame.new()
setMode(realMode)
end
end
local modeSnap = {
}
local modeClick = {}
modeClick.place = function(inputObject)
confirmPlace(inputObject,false)
end
modeClick.clone = function(inputObject)
setMode("place")
end
modeClick.move = function(inputObject)
axisDragClick(inputObject)
current.moveOffset = current.newMoveOffset or Vector3.zero
end
modeClick.rotate = axisRotateClick
modeClick.resize = function(inputObject)
axisDragClick(inputObject)
current.startSize = current.size
if current.model:IsA("Model") then current.startScale = current.model:GetScale() end
end
local modeLoop = {}
modeLoop.default = function()
if current.touching and current.touching:IsA("BasePart") then
methodModule.setItemCFrame(current.model,current.touching.CFrame:ToWorldSpace(current.touchingCFrame or CFrame.identity))
end
statGui.Enabled = not not statGui.Adornee
getBounds()
end
modeLoop.clone = function()
current:clone(current.touching)
modeLoop.default()
end
modeLoop.rotate = function()
modeLoop.default()
focusAxes()
if pointer then
processPointer(pointer)
local p = rayProjectToPlane(current.ray,current.axis)
local plane = current.touching.CFrame:ToWorldSpace(current.axisOrigin)
local toDrag = plane.Rotation:PointToWorldSpace(current.dragOrigin)
local toPointer = p-plane.Position
local angle = math.round(toDrag:Angle(toPointer,plane.RightVector)/math.rad(rotSnap))*math.rad(rotSnap)
local modelCFrame = current.touching.CFrame:ToWorldSpace(current.modelOrigin) -- cframe of axes/object in the world
local newCFrame = plane.Rotation:ToWorldSpace(CFrame.Angles(angle,0,0)*plane.Rotation:ToObjectSpace(modelCFrame.Rotation)) + modelCFrame.Position -- put it back into object space
current.rotation = current.initialRotation * newCFrame:ToObjectSpace(modelCFrame):Inverse()
current.touchingCFrame = current.touching.CFrame:ToObjectSpace(newCFrame) -- need to add rotation
statGui.TextLabel.Text = tonumber(math.round(math.deg(angle)*1000)/1000)
statGui.Adornee = current.model
else
statGui.Adornee = nil
end
end
local function psumag(v)
return v.X + v.Y + v.Z
end
modeLoop.resize = function()
modeLoop.default()
focusAxes()
if pointer then
processPointer(pointer)
local origin = current.touching.CFrame:ToWorldSpace(current.modelOrigin)
local start = origin:PointToWorldSpace(current.dragOrigin) -- the start of the scale drag
local diff = (rayProjectToRay(current.ray,current.axis)-start) -- difference between start and end
local scaleChange = origin.Rotation:PointToObjectSpace(diff)
if current.model:IsA("Model") then
local before = mask(current.startSize,current.dragOrigin.Unit) -- the size in this axis before scaling
local after = psumag(mask(scaleChange,current.dragOrigin.Unit)) -- the size in this axis after scaling
local scale = math.round(((((after*math.sign(psumag(before)))/psumag(before))+1)/modelScaleSnap)*current.startScale)*modelScaleSnap
scale = math.clamp(scale,0.5,current.model:GetAttribute("MaxSize") or 3)
current.model:ScaleTo(scale)
current.model:SetAttribute("Size",Vector3.new(scale,0,0))
current.touchingCFrame = current.modelOrigin + current.modelOrigin.Rotation:PointToWorldSpace(((scale/current.startScale)-1)*before*0.5)
statGui.TextLabel.Text = tonumber(math.round(scale*1000)/1000)
else
local change = round(mask(scaleChange,current.scaleTerms or Vector3.one))
current.model.Size = clamp(current.startSize + mask(change,current.dragOrigin.Unit),Vector3.one*0.1,current.model:GetAttribute("MaxSize") or Vector3.one*20)
current.model:SetAttribute("Size",current.model.Size)
current.touchingCFrame = current.modelOrigin + current.modelOrigin.Rotation:PointToWorldSpace(change/2)
statGui.TextLabel.Text = tonumber(math.round(mask(current.model.Size,current.dragOrigin.Unit).Magnitude*1000)/1000)
end
statGui.Adornee = current.model
else
statGui.Adornee = nil
end
modeLoop.default() -- the resize system has this weird lag, i cannot be asked fixing it unfortunately
end
modeLoop.move = function()
modeLoop.default()
focusAxes()
if pointer then
processPointer(pointer)
local origin = current.touching.CFrame:ToWorldSpace(current.modelOrigin)
local start = origin:PointToWorldSpace(current.dragOrigin)
local diff = (rayProjectToRay(current.ray,current.axis,start)-start) -- difference between start and end
diff = current.touching.CFrame.Rotation:PointToObjectSpace(diff)
local offset = psumag(mask(current.moveOffset,diff.Unit))
local len = math.abs(psumag(mask(current.size,origin:ToObjectSpace(current.axis.CFrame).RightVector)))
diff = diff.Unit * math.round(math.clamp(diff.Magnitude,-len*0.5-offset,len*0.5-offset)/moveSnap) * moveSnap
current.touchingCFrame = current.modelOrigin + diff
current.newMoveOffset = current.moveOffset + diff
statGui.TextLabel.Text = tonumber(math.round(diff.Magnitude*1000)/1000)
statGui.Adornee = current.model
else
statGui.Adornee = nil
end
end
modeLoop.place = function()
local result,ray,update = mouseModule.getMouseHit()
if result and result.Instance and update then
current.touching = result.Instance
if current.touching.Locked then adjacentBox.Adornee = nil end
if not current.touching then return end
if not current.touching.Position then print(current.touching) return end
local rotation = (current.touching.CFrame - current.touching.Position)
rotation = rotation * current.rotation
local s = current.size*0.5 -- So half the size of the part, rotated correctly
local max = 0
local points = {Vector3.new(s.X,s.Y,s.Z),Vector3.new(s.X,-s.Y,s.Z),Vector3.new(-s.X,s.Y,s.Z),Vector3.new(-s.X,-s.Y,s.Z),Vector3.new(s.X,s.Y,-s.Z),Vector3.new(s.X,-s.Y,-s.Z),Vector3.new(-s.X,s.Y,-s.Z),Vector3.new(-s.X,-s.Y,-s.Z)}
for _,point in pairs(points) do
max = math.max(max,result.Normal:Dot(rotation:PointToWorldSpace(point)))
end
local push = max
local snap = moveSnap
if shiftModifier then snap = moveSnap * 0.5 end
local depth = (Ray.new(result.Position,-result.Normal):ClosestPoint(current.touching.Position)-result.Position).Magnitude
local flat = current.touching.CFrame:PointToWorldSpace(round(current.touching.CFrame:PointToObjectSpace(result.Position - result.Normal * depth),snap))
if controlModifier or current.model:HasTag("Node") then push = 0 end
local cframe = rotation + flat + result.Normal * (depth + push) -- Add the rotation, Snapped movement perpendicular to normal and the Offset/Pushback
if current.model:HasTag("Node") then
local part
if current.touching:GetAttribute("Nodes") then
part = current.touching
elseif current.touching.Parent:GetAttribute("Nodes") then
part = current.touching.Parent
end
if part then
local ideal
local current_distance = 1
for _,node in ipairs(partStateModule.get_points(part)) do
local distance = (node - result.Position).Magnitude
if distance < current_distance then
current_distance = distance
if part:IsA("Model") then
ideal = part.PrimaryPart
else
ideal = part
end
if current.model:IsA("Model") then
cframe = rotation + node
else
cframe = current.model.CFrame.Rotation + node
end
current.touching = ideal
end
end
end
end
if current.touching:HasTag("Node") then
if current.model:IsA("Model") then
cframe = rotation + current.touching.Position
else
cframe = current.model.CFrame.Rotation + current.touching.Position
end
end
methodModule.setItemCFrame(current.model,cframe)
current.touchingCFrame = current.touching.CFrame:ToObjectSpace(cframe)
current.newMoveOffset = Vector3.zero
adjacentBox.Adornee = current.touching
end
getBounds()
end
local function render()
if not current then return end
controlModifier = inputService:IsKeyDown(Enum.KeyCode.LeftControl) or inputService:IsKeyDown(Enum.KeyCode.LeftMeta)
shiftModifier = inputService:IsKeyDown(Enum.KeyCode.LeftShift)
modeLoop[mode]()
end
--[[
config:
- Can Resize
- Confirm Callback
]]
local placetouchbuttoncolor
local function toggleTouching()
touchModeConnect = not touchModeConnect
if touchModeConnect then
_G.PlaceTouchButton.BackgroundColor3 = placetouchbuttoncolor
else
_G.PlaceTouchButton.BackgroundColor3 = Color3.new(1,1,1)
end
end
function begin(model,config)
if current then current:stop() end
gui.Visible = true
current = config or {}
current.stop = function(config)
if not config then warn("No config passed to PlacementModule; current:stop(). Using current.") config = current end
if not config then return end
if config.axes then config.axes:Destroy() end
if config.stopped then config:stopped() end
current = nil
selectionBox.Adornee = nil
adjacentBox.Adornee = nil
for _,box in pairs(touchingBoxes) do
box:Destroy()
end
touchingBoxes = {}
gui.Visible = false
end
model.Parent = game.Workspace.Hidden
if model:IsA("Model") then
model.PrimaryPart.Anchored = true
else
model.Anchored = true
end
ModeButtons.resize.Visible = config.resize
if not config.resize and mode == "resize" then mode = "place" end
if not config.rotate and mode == "rotate" then mode = "place" end
_G.PlaceTouchButton.Visible = config.touch
selectionBox.Adornee = model
current.model = model
if not current.model then error("urmum") end
current.connections = current.connections or {}
current.rotation = current.model:GetAttribute("Rotation") or CFrame.identity
setMode(realMode)
styleModel()
getBounds()
modeStart[mode]()
return config
end
function init(_gui)
gui = _gui
_G:WaitFor("PlaceModeFrame") _G:WaitFor("PlaceSnapFrame") _G:WaitFor("PlaceCancelButton")
statGui.Enabled = false
placetouchbuttoncolor = _G.PlaceTouchButton.BackgroundColor3
gui.Visible = false
run:BindToRenderStep("placementmodule",Enum.RenderPriority.Last.Value,render)
local touchOverlapParams = OverlapParams.new()
touchOverlapParams.MaxParts = 3
touchOverlapParams.FilterType = Enum.RaycastFilterType.Include
touchOverlapParams.FilterDescendantsInstances = {game.Workspace.Objects}
touchOverlapParams.RespectCanCollide = true
touchOverlapParams.Tolerance = 0.05
run.Heartbeat:Connect(function() -- Touching parts loop
if current and current.model and current.touch then -- in placement
local loop_current = current
local connections = {}
if touchModeConnect then
local items
if current.model:IsA("Model") then -- Check all potential subparts
items = current.model:GetChildren()
elseif current.model:IsA("BasePart") then
items = {current.model}
end
local count = 0
for _,item in pairs(items) do
if item:IsA("BasePart") then
for _,part in pairs(workspace:GetPartsInPart(item,touchOverlapParams)) do -- Get touching parts
if current.connections[part] then connections[part] = part continue end
local object = _G.Methods.getOwnedOrNil(_G.Player,part) -- Check if this is owned
if object then
connections[object] = true -- Add to the current touching ones
current.connections[object] = { -- Add to the final list
part = object
}
if current.model ~= item then current.connections[object].name = item.Name end
--if part == current.touching then continue end
local box = object:FindFirstChild("TouchingBox")
if not box then box = script.TouchingBox:Clone() box.Parent = object end
touchingBoxes[object] = box
end
end
end
if count > 3 then wait() count = 0 else count = count + 1 end -- Dangerous! Things can change after this (wait)
if current ~= loop_current then return end -- Current is invalid now so stop!
end
elseif current.touching then
local connection = {
part = current.touching
}
current.connections[current.touching] = connection
connections[current.touching] = connection
end
for index,connection in pairs(current.connections) do -- Remove all invalid connections too
if not connections[index] then
if touchingBoxes[index] then
touchingBoxes[index]:Destroy()
touchingBoxes[index] = nil
end
current.connections[index] = nil
end
end
end
end)
_G.PlaceTouchButton.Activated:Connect(toggleTouching)
inputService.InputBegan:Connect(function(inputObject,irrelevant)
if not current then return end
if irrelevant then return end
local sign = 1
if shiftModifier then sign = -1 end
if inputObject.UserInputType == Enum.UserInputType.Keyboard then
if inputObject.KeyCode == Enum.KeyCode.R then
if controlModifier then
_G.PlaceSnapInputFrame.RotSnap:CaptureFocus()
else
current.rotation = current.rotation * CFrame.Angles(math.rad(rotSnapKey*sign),0,0)
end
elseif inputObject.KeyCode == Enum.KeyCode.T then
if controlModifier then
_G.PlaceSnapInputFrame.MoveSnap:CaptureFocus()
else
current.rotation = current.rotation * CFrame.Angles(0,math.rad(rotSnapKey*sign),0)
end
elseif inputObject.KeyCode == Enum.KeyCode.Y then
if controlModifier then
_G.PlacePolyInputFrame.PolyThicknessInputBox:CaptureFocus()
else
current.rotation = current.rotation * CFrame.Angles(0,0,math.rad(rotSnapKey*sign))
end
elseif inputObject.KeyCode == Enum.KeyCode.G and not controlModifier then
current.rotation = CFrame.identity
elseif inputObject.KeyCode == Enum.KeyCode.E then
setMode("place")
elseif inputObject.KeyCode == Enum.KeyCode.Q and current.rotate then
setMode("rotate")
elseif inputObject.KeyCode == Enum.KeyCode.Z and current.resize then
setMode("resize")
elseif inputObject.KeyCode == Enum.KeyCode.X then
setMode("move")
elseif inputObject.KeyCode == Enum.KeyCode.U and current.touch then
toggleTouching()
elseif inputObject.KeyCode == Enum.KeyCode.Backspace then
if current and current.cancel then current:cancel() end
end
elseif inputObject.UserInputType == Enum.UserInputType.MouseButton1 then
if not shiftModifier then modeClick[mode](inputObject) else confirmPlace(inputObject,true) end
end
end)
inputService.TouchStarted:Connect(function(object)
modeClick[mode](object)
end)
inputService.InputEnded:Connect(function(inputObject,irrelevant)
if current and inputObject == pointer then
pointer = nil
current.axis = nil
end
end)
gui.Confirm.Activated:Connect(function()
confirmPlace(nil,true)
end)
_G.PlaceCancelButton.Activated:Connect(function()
if current and current.cancel then current:cancel() end
end)
for _,button in pairs(_G.PlaceModeFrame:GetChildren()) do
if button:IsA("TextButton") then
ModeButtons[button.Name] = button
if mode == button.Name then
button.BackgroundColor3 = Color3.new(0.6, 1, 0.333333)
end
button.Activated:Connect(function()
setMode(button.Name)
modeStart[mode]()
end)
end
end
local moveSnapInput = _G.PlaceSnapInputFrame.MoveSnap
local function evaluateSnapInput(text)
return text:match("([%d%.]+).-")
end
_G.PlaceSnapInputFrame.MoveSnap.FocusLost:Connect(function()
local num = evaluateSnapInput(moveSnapInput.Text)
if num then moveSnap = tonumber(num) end
moveSnapInput.Text = tostring(moveSnap).." stud"
end)
local rotSnapInput = _G.PlaceSnapInputFrame.RotSnap
_G.PlaceSnapInputFrame.RotSnap.FocusLost:Connect(function()
local num = evaluateSnapInput(rotSnapInput.Text)
if num then rotSnap = tonumber(num) end
rotSnapInput.Text = tostring(rotSnap).." deg"
end)
moveSnapInput.Text = tostring(moveSnap).." stud"
rotSnapInput.Text = tostring(rotSnap).." deg"
local snap_buttons = {}
for _,button in pairs(_G.PlaceModeFrame:GetChildren()) do
if button:IsA("TextButton") then
table.insert(snap_buttons,button)
if mode == button.Name then
button.BackgroundColor3 = Color3.new(0.6, 1, 0.333333)
end
button.Activated:Connect(function()
setMode(button.Name)
modeStart[mode]()
end)
end
end
end
return getfenv()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
local module = {}
local rules = {}
module.rules = rules
local listeners = {}
function module.listenRule(name,callback)
if rules[name] then task.spawn(callback,rules[name]) end
if not listeners[name] then listeners[name] = {} end
table.insert(listeners[name],callback)
end
function module.setRule(name,value)
_G.Remotes.Rule:FireAllClients(name,value)
if not rules[name] then rules[name] = {} end
if not listeners[name] then listeners[name] = {} end
rules[name] = value
for _,callback in pairs(listeners[name]) do
task.spawn(callback,value)
end
end
if _G.IsClient then
_G.Remotes.Rule.OnClientEvent:Connect(module.setRule)
end
return module
+503
View File
@@ -0,0 +1,503 @@
-- 41458171
require(game.ReplicatedFirst.ReadyModule)("SaveModule")
local Commands = require(_G.Modules.CommandsModule)
Placement = require(_G.Modules.PlacementModule)
Debris = _G.Debris
SaveFrame = nil
Stroke = nil
SaveData = nil
SaveTabs = nil
SaveLists = nil
Templates = nil
PlayerSaveList = nil
SelectionBox = nil
parts = _G.Storage:WaitForChild("Parts")
SubassemblyData = ""
SubassemblyModel = nil
CurrentSaveData = ""
CurrentPlacementConfig = nil
CurrentSaveRoot = nil
LastInteractedPart = nil
SelectionBoxes = {}
local function OffloadSelection()
if CurrentPlacementConfig then CurrentPlacementConfig:stop() end
CurrentSaveRoot = nil
CurrentSaveData = nil
SelectionBox.Adornee = nil
for _,object in pairs(SelectionBoxes) do
object:Destroy()
end
SelectionBoxes = {}
end
local function LoadModel(key,group)
wait()
local raw = _G.Remotes.Load:InvokeServer(group,key)
local success,result = pcall(function()
return _G.Methods.saves.dataToModel(raw,false)
end)
local model
if success then
model = result
else
_G.Error(string.format("Load Model Error (%s:%s): %s",key,group,result))
end
if not model then
return _G.Storage.Missing:Clone()
end
local primary = model:FindFirstChildWhichIsA("VehicleSeat",true)
if primary then
model.PrimaryPart = primary
end
return model
end
local _tab = {}
_tab.__index = _tab
local _slot = {}
_slot.__index = _slot
_slot.__newindex_operations = {
name = function(this,name)
this.frame.NameBox.Text = name
end,
order = function(this,index)
this.frame.LayoutOrder = tonumber(index) or 0
this.frame.IdLabel.Text = tostring(index):upper()
if index == "autosave" then
this.frame.IdLabel.BackgroundColor3 = Color3.new(1, 0.768627, 0.223529)
elseif index == "subassembly" then
this.frame.IdLabel.BackgroundColor3 = Color3.new(0.623529, 0.0941176, 1)
end
end,
on_mirror_clicked = function(this,callback)
_G.Error("Mirror function unimplemented")
end,
on_name_change = function(this,callback)
this.frame.NameBox.TextEditable = true
this.frame.NameBox.FocusLost:Connect(function(enter)
if enter then callback(this.frame.NameBox.Text) end
end)
end,
on_save_clicked = function(this,callback)
this.frame.SaveButton.Visible = true
this.frame.SaveButton.Activated:Connect(function()
callback(CurrentSaveData)
end)
end,
on_load_clicked = function(this,callback)
this.frame.LoadButton.Activated:Connect(function()
callback(this)
end)
end,
model = function(this,model)
local old = this.viewport:FindFirstChildWhichIsA("Model")
if old then old:Destroy() end
if not model then
this.frame.OldLabel.Visible = true
this.frame.OldLabel.Text = "CORRUPT"
return
else
if not (model.Name == "Model") then this.frame.NameBox.Text = model.Name end
end
model.Parent = this.viewport
this.frame.OldLabel.Visible = model:HasTag("Old")
this.frame.OldLabel.Text = model:GetAttribute("Version") or "Err"
local cframe,size = model:GetBoundingBox()
this.camera.CFrame = cframe * CFrame.fromEulerAnglesYXZ(-0.5,-2.5,0)
this.camera.CFrame = this.camera.CFrame + this.camera.CFrame.LookVector * -(size.Magnitude/2)
end
}
_slot.__newindex = function(this,index,value)
if not _slot.__newindex_operations[index] then error("No index to set: "..index,2) end
_slot.__newindex_operations[index](this,value)
end
_slot.show = function(this)
this.frame.Visible = true
_G.Tween:Create(this.frame.UIPadding,TweenInfo.new(0.5,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{PaddingLeft = UDim.new(0,0)}):Play()
end
_tab.CreateSlot = function(this)
local new = {}
new.frame = Templates.Frame:Clone()
new.frame.Parent = this.list
new.raw = ""
_G.Union(new.frame,{
Parent = this.list,
Visible = true,
})
new.frame.NameBox.TextEditable = false
new.frame.SaveButton.Visible = false
new.angle = 45
new.viewport = new.frame.ViewportFrame
new.undo = new.frame.UndoButton
new.refresh = new.frame.RefreshButton
new.camera = Instance.new("Camera")
new.camera.Parent = new.viewport
new.camera.CFrame = CFrame.Angles(0,math.rad(new.angle),0)
new.viewport.CurrentCamera = new.camera
local hover = false
new.viewport.MouseEnter:Connect(function()
hover = true
new.viewport.BackgroundColor3 = Color3.new(0.75,0.75,0.75)
while wait() and hover do
new.camera.CFrame = CFrame.Angles(0,math.rad(2),0) * new.camera.CFrame
end
end)
new.viewport.MouseLeave:Connect(function()
hover = false
new.viewport.BackgroundColor3 = Color3.new(1,1,1)
end)
new.frame.Visible = false
new.frame.OldLabel.Visible = false
return setmetatable(new,_slot)
end
_tab.FocusSection = function(this)
for _,list in pairs(SaveLists:GetChildren()) do
if list:IsA("ScrollingFrame") then list.Visible = false end
end
this.list.Visible = true
Stroke.Parent = this.tab
end
local function CreateSection(title)
local new = setmetatable({},_tab)
new.list = Templates.List:Clone()
_G.Union(new.list,{
Parent = SaveLists,
Visible = false
})
new.tab = Templates.Tab:Clone()
_G.Union(new.tab,{
Parent = SaveTabs,
Visible = true,
Text = title
})
new.slots = {}
new.tab.CloseButton.Activated:Connect(function()
Stroke.Parent = PlayerSaveList
new.tab:Destroy()
new.list:Destroy()
PlayerSaveList:FocusSection()
end)
new.tab.Activated:Connect(function() new:FocusSection() end)
return new
end
_G.SaveIndices = {}
local function CreateUserSection(userid,modify,close)
userid = tostring(userid or _G.UserId)
local title = (game.Players:GetPlayerByUserId(tonumber(userid) or 0) or {DisplayName = userid}).DisplayName
local section = CreateSection(title)
if userid == tostring(_G.UserId) then section:FocusSection() end
local save_info = {}
local save_info_locked = false
local saves = {}
local slots = {}
if not userid:match(":") and not close then section.tab.CloseButton.Visible = false end
local function update_save_info(callback)
repeat
wait()
until not save_info_locked
save_info_locked = true
if callback then callback(save_info) end
_G.Remotes.SetSaveInfo:InvokeServer(save_info,userid)
save_info = _G.Remotes.GetSaveInfo:InvokeServer(userid)
for index,name in pairs(save_info) do
local slot = slots[tostring(index)] or slots[index]
if slot then
slot.name = name
end
end
save_info_locked = false
end
spawn(function()
save_info = _G.Remotes.GetSaveInfo:InvokeServer(userid)
local indices = {}
if not save_info then return end
for index,_ in pairs(save_info) do
table.insert(indices,index)
end
_G.SaveIndices[userid] = indices
if not userid:match(":") then
table.sort(indices,function(before,after)
before = tonumber(before)
after = tonumber(after)
if not before then return true end
if not after then return false end
return before < after
end)
end
for _,index in pairs(indices) do
spawn(function()
local name = save_info[index]
index = tostring(index)
local slot = section:CreateSlot()
slots[index] = slot
local model = LoadModel(index,userid)
saves[index] = model
_G.Union(slot,{
name = name,
order = index,
on_name_change = function(text)
update_save_info(function()
save_info[tonumber(index) or index] = text
end)
end,
on_load_clicked = function()
if not saves[index] or not saves[index].PrimaryPart then _G.Events.Notification:Fire("Vehicle Load Failed","There is no vehicle.") return end
OffloadSelection()
CurrentPlacementConfig = Placement.begin(saves[index]:Clone(),{
confirm = function(config)
_G.Remotes.Place:InvokeServer(config.model.PrimaryPart.CFrame,index,userid)
config:stop()
end,
stopped = function(config)
config.model:Destroy()
CurrentPlacementConfig = nil
end,
disabled = {
resize = true
}
})
end,
model = model
})
if modify then
slot.on_save_clicked = function()
if (not CurrentSaveData) or (#CurrentSaveData == 0) then
_G.Events.Notification:Fire("Vehicle Save Failed","Please select a vehicle to save")
return
end
_G.Remotes.Save:InvokeServer(CurrentSaveData,index,userid)
model = LoadModel(index,userid)
saves[index] = model
slot.model = model
OffloadSelection()
end
slot.viewport.MouseEnter:Connect(function()
slot.undo.Visible = true
slot.refresh.Visible = true
end)
slot.viewport.MouseLeave:Connect(function()
slot.undo.Visible = false
slot.refresh.Visible = false
end)
end
slot.undo.Activated:Connect(function()
CreateUserSection(userid..":"..index,false)
end)
slot.refresh.Activated:Connect(function()
model = LoadModel(index,userid)
saves[index] = model
slot.model = model
OffloadSelection()
end)
slot:show()
end)
wait(0.2)
end
if tostring(_G.UserId) == userid then -- autosave
spawn(function()
wait(5)
repeat
if not LastInteractedPart then continue end
local part = LastInteractedPart.part
if part and part.Parent and _G.Methods.playerOwnsPart(game.Players.LocalPlayer,part) then
_G.Events.Notification:Fire("Auto Saving","Attempting Autosave")
CurrentSaveData = _G.Methods.saves.modelToData(part)
_G.Remotes.Save:InvokeServer(CurrentSaveData,"autosave",userid)
_G.Events.Save:Fire(CurrentSaveData)
local selection_template = Instance.new("SelectionBox")
for index,item in pairs({
SurfaceTransparency = 0.75,
SurfaceColor3 = Color3.fromRGB(231, 72, 183),
LineThickness = 0,
}) do
selection_template[index] = item
end
_G.Methods.forEachConnectedObject(part,function(model)
wait()
local selection = selection_template:Clone()
selection.Adornee = model
selection.Parent = model
Debris:AddItem(selection,0.1)
end)
update_save_info(function()
save_info["autosave"] = tostring(os.date("%c",os.time()))
end)
local model = LoadModel("autosave",userid)
slots["autosave"].model = model
saves["autosave"] = model
end
until not wait(60*2.5)
end)
end
end)
return section,saves
end
_G.Events.Create.Event:Connect(function(part)
LastInteractedPart = part
end)
local function RenderStepped(delta) -- General procedure for allowing us to pick a part from a vehicle to later select (per frame)
if CurrentPlacementConfig then return end
local result = _G.Methods.getMouseHit()
if result and result.Instance then
local part
if _G.Methods.playerOwnsPart(_G.Player,result.Instance) then
part = result.Instance
elseif _G.Methods.playerOwnsPart(_G.Player,result.Instance.Parent) then
part = result.Instance.Parent
end
SelectionBox.Adornee = part
CurrentSaveRoot = part
end
end
function InitTool(script)
SelectionBox = script:WaitForChild("SelectionBox")
local renderSteppedEvent = nil
script.Parent.Activated:Connect(function()
if CurrentSaveRoot then
local root = CurrentSaveRoot
OffloadSelection()
CurrentSaveData = _G.Methods.saves.modelToData(root)
local selection_template = Instance.new("SelectionBox")
for index,item in pairs({
SurfaceTransparency = 0.75,
SurfaceColor3 = Color3.fromRGB(154, 46, 231),
LineThickness = 0
}) do
selection_template[index] = item
end
_G.Methods.forEachConnectedObject(root,function(model)
local selection = selection_template:Clone()
selection.Adornee = model
selection.Parent = model
table.insert(SelectionBoxes,selection)
end)
end
end)
script.Parent.Unequipped:Connect(function()
OffloadSelection()
SaveFrame.Visible = false
SelectionBox.Adornee = nil
if renderSteppedEvent then renderSteppedEvent:Disconnect() end
end)
script.Parent.Equipped:Connect(function()
SaveFrame.Visible = true
if renderSteppedEvent then renderSteppedEvent:Disconnect() end
renderSteppedEvent = game:GetService("RunService").RenderStepped:Connect(RenderStepped)
end)
end
local player_saves
function InitUI(script)
SaveFrame = script.Parent
Stroke = script:WaitForChild("UIStroke")
SaveData = script.Parent:WaitForChild("SaveData")
SaveTabs = script.Parent:WaitForChild("SaveTabs")
SaveLists = script.Parent:WaitForChild("SaveLists")
Templates = script.Parent:WaitForChild("Template")
SaveFrame.Visible = false
Templates.Frame.Visible = false
PlayerSaveList,player_saves = CreateUserSection(_G.UserId,true)
for i = 1,2 do
local SubSlot = PlayerSaveList:CreateSlot()
SubSlot.order = string.format("SUB %i",i)
SubSlot.name = string.format("Subassembly %i",i)
SubSlot.on_save_clicked = function()
if (not CurrentSaveData) or (#CurrentSaveData == 0) then
_G.Events.Notification:Fire("Vehicle Save Failed","Please select a vehicle to save")
return
end
SubassemblyData = CurrentSaveData
SubassemblyModel = _G.Methods.saves.dataToModel(CurrentSaveData,false)
SubSlot.model = SubassemblyModel
OffloadSelection()
end
SubSlot.on_mirror_clicked = function()
OffloadSelection()
if not SubassemblyData then return end
if SubassemblyModel then SubassemblyModel:Destroy() end
SubassemblyModel = _G.Methods.saves.dataToModel(SubassemblyData,false,true)
SubSlot.model = SubassemblyModel
end
SubSlot.on_load_clicked = function()
OffloadSelection()
if not SubassemblyModel then return end
CurrentPlacementConfig = Placement.begin(SubassemblyModel:Clone(),{
confirm = function(config)
_G.Events.Notification:Fire("Subassembly Placing!")
_G.Remotes.PlaceSubassembly:InvokeServer(config.model.PrimaryPart.CFrame,SubassemblyData,config.touching)
config:stop()
end,
stopped = function(config)
config.model:Destroy()
CurrentPlacementConfig = nil
end,
})
end
SubSlot:show()
end
CreateUserSection("public",_G.Remotes.Admin:InvokeServer())
end
Commands.new("open save (.+)","open save <userid>: Opens saves in your tool.",function(userid)
CreateUserSection(userid,false)
_G.Events.Notification:Fire("Opened Read-Only Save",userid)
end)
Commands.new("open write save (.+)","open write save <userid>: Opens write saves in your tool.",function(userid)
CreateUserSection(userid,true)
_G.Events.Notification:Fire("Opened Write Save",userid)
end)
Commands.new("export save (.+) (.+)","export save <userid> <number>: Prints out your save",function(userid,key)
_G.Events.Notification:Fire("Save Export",tostring(_G.Remotes.Load:InvokeServer(userid,key)))
end)
Commands.new("show indices","show indices: Prints out save indices",function()
local result = ""
local indices = _G.SaveIndices
for userid,tab in pairs(indices) do
result = result .. "Userid: "..tostring(userid).."\n"
for index,item in pairs(tab) do
result = result .. " "..tostring(index)..": "..tostring(item).."\n"
end
end
print(result)
_G.Events.Notification:Fire("Save Indicies",result)
end)
return getfenv()
+30
View File
@@ -0,0 +1,30 @@
local module = {}
local function returnNetworkOwner(this)
local owner = this:GetAttribute("NetworkOwner") or this.Parent:GetAttribute("NetworkOwner")
if owner then owner = game.Players:GetPlayerByUserId(owner) end
this:SetNetworkOwner(owner) -- there is a bug here?
end
function module.init(this)
do return end
this.Parent.Changed:Connect(function(name)
if name == "Occupant" then
if this.Parent.Occupant then
local player = game.Players:GetPlayerFromCharacter(this.Parent.Occupant.Parent)
if player then
if this.Parent.Parent:HasTag("P_"..player.UserId) or true then
this.Parent:SetNetworkOwner(player)
--print("Vehicle ownership set to "..player.DisplayName)
end
else
returnNetworkOwner(this.Parent)
end
else
returnNetworkOwner(this.Parent)
end
end
end)
end
return module
+409
View File
@@ -0,0 +1,409 @@
local partStateModule = require(script.Parent.PartStateModule)
local saves = {}
local id_parts = {}
local parts_loaded
spawn(function()
game.ReplicatedStorage:WaitForChild("Parts")
local function added(object)
if object.Parent:IsA("Folder") then
local id = object:GetAttribute("PartId")
if id then id_parts[id] = object end
end
end
for _,object in pairs(_G.Storage.Parts:GetDescendants()) do
added(object)
end
_G.Storage.Parts.DescendantAdded:Connect(added)
wait(1)
parts_loaded = true
end)
do
local pairs = pairs
local ipairs = ipairs
local isa = Instance.new("Part").IsA
local no_cframe = {}
local cframe_of = function(object)
if isa(object,"BasePart") then
return object.CFrame
elseif isa(object,"Model") then
return (object.PrimaryPart or no_cframe)["CFrame"]
end
end
local cframe_set = function(object,cframe)
if isa(object,"Model") then
object:SetPrimaryPartCFrame(cframe)
elseif isa(object,"BasePart") then
object.CFrame = cframe
end
end
local weld_is_build = {["BuiltWeldConstraint"] = true}
local insert = table.insert
local typeof = typeof
local is_table = {["table"]=true}
local id_module = _G.Methods.Ids
local colorPart = _G.Methods.colorPart
local ts = tostring
local tn = tonumber
local format = string.format
local concat = table.concat
local cframe_new = CFrame.new
local vector3_new = Vector3.new
local table_create = table.create
local form_triangle = partStateModule.form_triangle
local get_nodes = partStateModule.get_nodes
local apply_nodes = partStateModule.apply_nodes
local apply_shift = partStateModule.apply_shift
local apply_size = partStateModule.apply_size
local apply_material = partStateModule.apply_material
local apply_internal_welds = partStateModule.apply_internal_welds
local hexxie = require(script.Parent.HexxieModule)
local hd = hexxie.decode
local he = hexxie.encode
local hexxie_cframe_match = string.rep("(...........)",12)
local hexxie_position_match = string.rep("(...........)",3)
local default_cframe_format = string.rep("%s,",10).."%s"
local id_attach = {}
local compress_cframe_decode = function(s)
if not s then return nil end
local a,b,c,d,e,f,g,h,i,j,k,l = s:match(hexxie_cframe_match)
return cframe_new(hd(a),hd(b),hd(c),hd(d),hd(e),hd(f),hd(g),hd(h),hd(i),hd(j),hd(k),hd(l))
end
local compress_cframe_encode = function(c)
local a,b,c,d,e,f,g,h,i,j,k,l = c:GetComponents()
return concat({he(a),he(b),he(c),he(d),he(e),he(f),he(g),he(h),he(i),he(j),he(k),he(l)})
end
local compress_position_decode = function(s)
local a,b,c = s:match(hexxie_position_match)
return vector3_new(hd(a),hd(b),hd(c))
end
local compress_position_encode = function(v)
return concat({he(v.X),he(v.Y),he(v.Z)})
end
local position
local http = game:GetService("HttpService")
local http_json_encode = http.JSONEncode
local http_json_decode = http.JSONDecode
local json_decode = function(value)
return http_json_decode(http,value)
end
local json_encode = function(value)
return http_json_encode(http,value)
end
local ID_INDEX = 1
local CFRAMES_INDEX = 2
local EDIT_INDEX = 3
saves["4"] = {
decode = function(raw,options)
--print(raw)
repeat wait() until parts_loaded
local final = {errors = {}}
local function err(text)
warn(text)
table.insert(final.errors,text)
end
options = options or {}
local _compress_cframe_decode = compress_cframe_decode
local _compress_position_decode = compress_position_decode
if options.mirror then
_compress_cframe_decode = function(str)
local cf = compress_cframe_decode(str)
local x,y,z = cf:ToEulerAnglesYXZ()
return CFrame.fromEulerAnglesYXZ(x,-y,-z)+Vector3.new(cf.X,-cf.Y,cf.Z)
end
_compress_position_decode = function(str)
local pos = compress_position_decode(str)
return vector3_new(-pos.X,pos.Y,pos.Z)
end
end
local compressed = raw:match("\"compress\"%:true")
if compressed then raw = hexxie.rle_decode(raw) end
local data = _G.Http:JSONDecode(raw)
_G.Union(options,data.options or {})
local physics = options.physics
local template_cframe = Instance.new("CFrameValue")
local weld_template = Instance.new("WeldConstraint")
local built_weld_template = weld_template:Clone()
built_weld_template.Name = "BuiltWeldConstraint"
local model = Instance.new("Model")
final.model = model
local parts = {}
local editActions = {
c = function(edit,part)
local color = Color3.new(unpack(edit))
colorPart(part,color)
end,
h = function(edit,part)
local hinge = part:FindFirstChild("Hinge")
if hinge then hinge.CFrame = compress_cframe_decode(edit) end
end,
r = function(edit,part)
local slider = part:FindFirstChild("Slider")
if slider then slider.CFrame = compress_cframe_decode(edit) end
end,
m = function(edit,part)
if edit then apply_material(part,Enum.Material[edit]) end
end,
s = function(edit,part)
apply_size(part,Vector3.new(unpack(edit)))
end,
S = function(edit,part)
apply_shift(part,edit)
end,
}
local poly_to_internally_weld = {}
local data_cframes = data.cframes
local data_edits = data.edits
for id,object in pairs(data.objects) do
local template = id_parts[object[ID_INDEX]]
if not template then err("Missing part with id "..tostring(id)) continue end
local instance = template:Clone()
instance.Parent = model
parts[id] = instance
local nodes = {}
local transform_ids = object[CFRAMES_INDEX]
-- BEGIN PARITY 4.1 (CAN BE REMOVED LATER)
if typeof(transform_ids) ~= "table" then transform_ids = {transform_ids} end
-- END PARITY 4.1
if #transform_ids == 1 then
insert(nodes,compress_cframe_decode(data_cframes[transform_ids[1]]))
elseif #transform_ids == 2 then
insert(nodes,compress_cframe_decode(data_cframes[transform_ids[1]]))
insert(nodes,hd(data_cframes[transform_ids[2]]))
else
for _,id in ipairs(transform_ids) do
insert(nodes,compress_position_decode(data_cframes[id]))
end
insert(poly_to_internally_weld,instance)
end
apply_nodes(instance,nodes)
-- Edit part
if object[EDIT_INDEX] then
for _,edit_id in pairs(object[EDIT_INDEX]) do
local key,value = unpack(data_edits[edit_id]);
(editActions[key] or function(value,instance)
local edit = instance:FindFirstChild(key)
if not edit then
err(string.format("No key %s for %s",key,instance:GetFullName()))
else
if typeof(value) == "table" then value = _G.Http:JSONEncode(value) end
edit.Value = value
end
end)(value,instance)
end
end
-- Change anchoring
if not options.physics then
if instance:IsA("Model") then
instance.PrimaryPart.Anchored = true
else
instance.Anchored = true
end
end
end
if options.physics then
for _,weld in pairs(data.welds) do
local w = built_weld_template:Clone()
local at,bt = unpack(weld)
local ap = parts[at[1]]
local bp = parts[bt[1]]
w.Parent = ap
if ap:IsA("Model") then
if at[2] then
ap = ap:FindFirstChild(at[2]) or ap.PrimaryPart
else
ap = ap.PrimaryPart
err("Missing subpart reference for weld on model A")
end
end
if bp:IsA("Model") then
if bt[2] then
bp = bp:FindFirstChild(bt[2]) or bp.PrimaryPart
else
bp = bp.PrimaryPart
err("Missing subpart reference for weld on model B")
end
end
w.Part0 = ap
w.Part1 = bp
end
for _,poly in ipairs(poly_to_internally_weld) do
apply_internal_welds(poly)
end
else
for _,part in pairs(model:GetDescendants()) do
if part:IsA("BasePart") then part.CanCollide = false part.Anchored = true end
end
end
local root = parts[1]
if not root then error("Fatal: Missing root part; implying #parts = 0") end
if root:IsA("Model") then root = root.PrimaryPart end
model.PrimaryPart = root
final.model = model
return final
end,
encode = function(root,options)
repeat wait() until parts_loaded
local final = {errors = {}}
local function err(text)
warn(text)
table.insert(final.errors,text)
end
id_module.forEachConnectedObject(root,function(object)
if object:IsA("VehicleSeat") then root = object end
end)
final.root = root
if not options then options = {} end
local origin = cframe_of(root)
if not origin then err("Root CFrame could not be encoded, it is probably broken") return final end
local ids = {}
local cframes = {}
local edits = {}
local welds = {}
local objects = {}
local data = {
options = options,
objects = objects,
cframes = cframes,
edits = edits,
welds = welds
}
local should_optimise_cframe = not not options.optimise_cframe
local cframe_epsilon = options.cframe_epsilon or 0.1
local optimise_edit = {}
local optimise_cframe = {}
local function cframe_id(c)
local existing = optimise_cframe[c]
if not existing then
insert(cframes,c)
local len = #cframes
optimise_cframe[c] = len
return len
else
return existing
end
end
local function edit_id(key,value)
local str
if type(value) == "table" then
str = json_encode(value)
elseif type(value) ~="string" then
str = tostring(value)
else
str = value
end
local tab = optimise_edit[key]
if not tab then tab = {} optimise_edit[key] = tab end
if tab[str] then
return tab[str]
else
insert(edits,{key,value})
tab[str] = #edits
return #edits
end
end
local errors = {}
local index = 0
id_module.forEachConnectedObject(root,function(object)
index = index + 1
ids[object] = index
local id = object:GetAttribute("PartId") or object.Name
if not id then insert(errors,string.format("Part without an ID! %s",ts(object))) end
-- ENCODE TRANSFORM
local transform = get_nodes(object)
if #transform >= 3 then
for index,position in pairs(transform) do
transform[index] = cframe_id(compress_position_encode(origin:PointToObjectSpace(position)))
end
elseif #transform == 2 then
transform[1] = cframe_id(compress_cframe_encode(origin:ToObjectSpace(transform[1])))
transform[2] = cframe_id(he(transform[2]))
elseif #transform == 1 then
transform[1] = cframe_id(compress_cframe_encode(origin:ToObjectSpace(transform[1])))
end
-- ENCODE EDITS
local edits = {}
for index,edit in pairs(object:GetChildren()) do
if edit:HasTag("Edit") or edit:HasTag("EditHidden") then
if edit:HasTag("JSON") then
insert(edits,edit_id(edit.Name,json_decode(edit.Value)))
else
local value = edit.Value
if (edit:GetAttribute("Default") ~= value) then
insert(edits,edit_id(edit.Name,value))
end
end
end
end
-- ENCODE CANDID DISPLACEMENT
local hinge = object:FindFirstChild("Hinge")
if hinge then insert(edits,edit_id("h",compress_cframe_encode(origin:ToObjectSpace(hinge.CFrame)))) end
local slider = object:FindFirstChild("Slider")
if slider then insert(edits,edit_id("r",compress_cframe_encode(origin:ToObjectSpace(slider.CFrame)))) end
-- ENCODE PROPERTIES
local color = object:GetAttribute("Color") -- TODO HEXXIE OPTIMISE COLOUR AND SIZE
if color then insert(edits,edit_id("c",{color.R,color.G,color.B})) end
local material = object:GetAttribute("Material")
if material then insert(edits,edit_id("m",material)) end
local size = object:GetAttribute("Size")
if size then insert(edits,edit_id("s",{size.X,size.Y,size.Z})) end
local shift = object:GetAttribute("Shift")
if shift then insert(edits,edit_id("S",shift)) end
-- INSERT OBJECT
local data = {id,transform}
if #edits > 0 then insert(data,edits) end
insert(objects,data)
end)
-- ENCODE WELDS
for object,id in pairs(ids) do
for _,weld in pairs(object:GetDescendants()) do
if weld_is_build[weld.Name] then
if weld.Part0 and weld.Part1 then
local first = {ids[weld.Part0]}
if not first[1] then
first[1] = ids[weld.Part0.Parent]
first[2] = weld.Part0.Name
if not first[1] then err("There was a stray first weld! "..tostring(weld:GetFullName())) continue end
end
local second = {ids[weld.Part1]}
if not second[1] then
second[1] = ids[weld.Part1.Parent]
second[2] = weld.Part1.Name
if not second[1] then err("There was a stray second weld! "..tostring(weld:GetFullName())) continue end
end
insert(data.welds,{first,second})
else
err("There was a dead weld!"..tostring(weld:GetFullName()))
end
end
end
end
local json = _G.Http:JSONEncode(data)
local result = json
if options.compress then result=hexxie.rle_encode(json) end
local check = hexxie.rle_decode(result)
result = "v4save"..result
final.data = result
return final
end,
}
end
return saves
+143
View File
@@ -0,0 +1,143 @@
local is_client = game.Players.LocalPlayer
if is_client then return {} end
local scale = 20 -- * 10
local generation_folder = game.Workspace.Generation
local function triangle(p1,p2,p3,r1,r2) -- simple function, might work?
local s = 0.01
local bottom = {
b1 = nil,
b2 = nil,
dist = 0,
other = nil
}
local function check(point1,point2,other)
local dist = (point1 - point2).Magnitude
if dist > bottom.dist then
bottom.b1 = point1
bottom.b2 = point2
bottom.other = other
bottom.dist = dist
end
end
check(p1,p2,p3) -- check all of the different options for bottom and get the right one?
check(p1,p3,p2)
check(p2,p3,p1)
local view = CFrame.lookAt(bottom.b1,bottom.b2,(bottom.other-bottom.b1).Unit)
local object = view:PointToObjectSpace(bottom.other)
local length = object.Z
local height = object.Y
r1.Size = Vector3.new(0.25,height-s,-length-s)
r1.CFrame = (view * CFrame.Angles(0,math.rad(180),0)) * CFrame.new(0,height/2+s/2,-length/2+s/2)
r2.Size = Vector3.new(0.25,height-s,-(-bottom.dist-length)-s)
r2.CFrame = view * CFrame.new(0,height/2+s/2,-(bottom.dist-length)/2+s/2)
--[[if r1.Position.Y < 0 then
r1.Color = Color3.new(0.764706, 0.756863, 0.478431)
r1.Material = Enum.Material.Sand
end
if r2.Position.Y < 0 then
r2.Color = Color3.new(0.764706, 0.756863, 0.478431)
r2.Material = Enum.Material.Sand
end]]
return r1,r2
end
local editable_mesh_template = Instance.new("EditableMesh")
local editable_mesh_add_vertex = editable_mesh_template.AddVertex
local editable_mesh_add_triangle = editable_mesh_template.AddTriangle
local ta
local tb
local tc
local function ntri(mesh,a,b,c,d)
ta = editable_mesh_add_vertex(mesh,a)
tb = editable_mesh_add_vertex(mesh,b)
tc = editable_mesh_add_vertex(mesh,c)
editable_mesh_add_triangle(mesh,ta,tb,tc)
ta = editable_mesh_add_vertex(mesh,a)
tb = editable_mesh_add_vertex(mesh,c)
tc = editable_mesh_add_vertex(mesh,d)
editable_mesh_add_triangle(mesh,ta,tb,tc)
end
local function part()
local part = Instance.new("Part")
part.Parent = generation_folder
part.Anchored = true
return part
end
local function tri(mat)
local new = mat:Clone()
new.Parent = generation_folder
return new
end
local fidelity = 20
local tree = _G.Storage.Tree
local mat_sand = game.Workspace:WaitForChild("SandTriangle")
local mat_grass = game.Workspace:WaitForChild("GrassTriangle")
local tri_union_pool = {}
local function unionTable(tbl)
if #tbl > 2 then
local start = tbl[#tbl]
table.remove(tbl,#tbl)
start:UnionAsync(tbl).Parent = game.Workspace
for _,item in pairs(tbl) do
item:Destroy()
end
print("union!")
end
end
local function chunk(sx,sy,ox,oy)
--[[for i=18,2,-4 do
local water = _G.Storage.Water:Clone()
water.Size = Vector3.new(sx,0,sy)*scale*scale
water.Position = Vector3.new(ox*sx+sx/2,-2,oy*sy+sy/2)*scale + Vector3.new(0,i,0)
water.Parent = generation_folder
end]]
--[[local object = Instance.new("MeshPart")
object.Parent = generation_folder
object.Name = "MeshObject"
object.Anchored = true
object.Material = Enum.Material.Grass
object.Color = Color3.new(0.254902, 0.403922, 0.133333)
object.Size = Vector3.new(1,1,1)
local edit = Instance.new("EditableMesh")
edit.Parent = object
edit.Name = "VertexObject"
local rows = {}
local last_column = nil
local sumtris = {}
for x=ox*sx,ox*sy+sx,fidelity do
local columns = {}
table.insert(rows,columns)
local last_index = 1
for y=oy*sy,oy*sy+sy,fidelity do
local biome = math.clamp(math.noise(x/1000,y/1000,-6.432),0,1)*50
local island = math.clamp(math.noise(x/200,y/200,-2.132)-0.3,0,1)*30
local height = math.noise(x/10,y/10,0.123) + math.clamp(math.noise(x/50,y/50,0.123),0.2,1)*3 - (0.2-math.clamp(math.abs(math.noise(x/50,y/50,5.213)),0,0.2))*10 + island - 8 + biome
table.insert(columns,Vector3.new(x,height*2,y))
if last_column and last_index > 1 and height > -5 then
local mat = mat_grass
local current = columns[last_index]*scale
local left = columns[last_index-1]*scale
local up_left = last_column[last_index-1]*scale
local up = last_column[last_index]*scale
--ntri(edit,current,up,up_left,left)
local t1,t2 = triangle(current,up,up_left,tri(mat),tri(mat))
local t3,t4 = triangle(current,left,up_left,tri(mat),tri(mat))
table.insert(sumtris,t1)
table.insert(sumtris,t2)
table.insert(sumtris,t3)
table.insert(sumtris,t4)
end
last_index = last_index + 1
end
last_column = columns
end
task.delay(0,function()unionTable(sumtris)end)]]
end
for x=-6,6 do
for y=-6,6 do
wait()
chunk(100,100,x,y)
end
end
return {}
+3
View File
@@ -0,0 +1,3 @@
local module = {}
return module
+126
View File
@@ -0,0 +1,126 @@
local module = {}
local debugWings = game.Workspace.DebugWings.Value
local space = game.Workspace.Objects
local dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
local ndelta = 1/60
local function drag(v,r)
return v * r
end
local rnd = function(vec) return Vector3.new(math.round(vec.X),math.round(vec.Y),math.round(vec.Z)) end
local a = math.abs
local ln = math.log
local debwings = {}
local function deb(wing,force)
local part = debwings[wing]
if not part then
part = Instance.new("Part")
part.Name = "WingDebug"
part.Parent = game.Workspace.Hidden
part.Color = Color3.new(0.152941, 0.764706, 0.0588235)
part.Material = Enum.Material.Neon
part.Anchored = true
part.CanCollide = false
part.Transparency = 0.8
part.Size = Vector3.new(0.25,0.25,2) -- we determine the wing's strength and do math by its size, the bigger, the more lift
debwings[wing] = part
end
part.Transparency = 0.5
part.CFrame = CFrame.lookAt(wing.Position,wing.Position+force)
part.Size = Vector3.new(0.25,0.25,force.Magnitude)
part.CFrame = part.CFrame + part.CFrame.LookVector * part.Size.Z * 0.5
end
require(game.ReplicatedFirst.ReadyModule)("WingCommands")
local process = true
game.Workspace.DebugWings.Changed:Connect(function()
debugWings = game.Workspace.DebugWings.Value
if not debugWings then
for _,item in pairs(debwings) do
item:Destroy()
end
debwings = {}
end
end)
game.Workspace.CompensateDensity.Changed:Connect(function()
dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
end)
function module.new(this,wing,context)
--local mul = wing:GetAttribute("AeroMultiplier") or 1
this.bounds = {}
this.volume = {}
if wing:IsA("Model") then
this.aero = {}
for _,part in pairs(wing:GetChildren()) do
if part:HasTag("IsAerodynamic") then
table.insert(this.aero,part)
end
end
else
this.aero = {wing}
end
for _,part in pairs(this.aero) do
local s = part.Size
this.bounds[part] = Vector3.new(s.Y*s.Z,s.X*s.Z,s.Y*s.X)-- * mul
this.volume[part] = s.X*s.Y*s.Z
end
return this
end
local visc = 0.75
local commands = require(_G.Modules.CommandsModule)
commands.new("viscosity (.+)","viscosity <number>: change air viscosity coefficient",function(n)
visc = tonumber(n)
end)
module.run = @native
function(this,wing,context)
local bounds = this.bounds
local volume = this.volume
if not process then return true end
if not this.aero then print(this,wing) end -- BECAUSE WHAT THE BLOODY HELL IS THIS!?!??
for _,wing in pairs(this.aero) do
--if not (wing.Parent == space or wing.Parent.Parent == space) and v.player then return end
local s = bounds[wing]
if not s then print(this.aero,bounds,wing,s) end
local p = wing.Position
local v = wing:GetVelocityAtPosition(p) -- velocity of wing
local d = dens * 20
local fmass = 2 -- mass of air generally
--[[if p.Y < 0 then
d = d * 5
fmass = 100 -- mass of water
end]]
v = v - Vector3.new(0,
0--0.15 * v.Magnitude -- implement upthrust due to pressure difference across an airfoil (lift)
,0)
local lv = wing.CFrame.Rotation:PointToObjectSpace(v) -- velocity for each wing face
--mv = lv.Magnitude + 0.0001 -- small number to stop zero error
-- "math", YEAH REALLY? i didnt REALISE! I don't even know what this does...
--lp = Vector3.new(lv.X/(a(scale.X*d*lv.X)+1)-lv.X,lv.Y/(a(scale.Y*d*lv.Y)+1)-lv.Y,lv.Z/(a(scale.Z*d*lv.Z)+1)-lv.Z) -- math
local drag = Vector3.new(lv.X*s.X,lv.Y*s.Y,lv.Z*s.Z)
-- implement anti-viscosity (may the riemann sum gods be with the low end machines
local lp = (drag) * context.delta*d*math.tanh(v.Magnitude*0.0075)
local r = (wing.CFrame.Rotation:PointToWorldSpace(lp))
r = r + Vector3.yAxis * fmass * volume[wing] * 2 * context.delta -- implement upthrust due to pressure difference in a fluid column (floating)
wing:ApplyImpulseAtPosition(r,wing.Position)
if debugWings then deb(wing,r) end
end
return true
end
module.manifest = {
predicate = function(instance)
return instance:HasTag("IsAerodynamic") or instance.Name:match("Sheet") -- LAST CONDITION IS NOT NEEDED
end,
}
return module
+34
View File
@@ -0,0 +1,34 @@
local module = {}
local cool_rate = 0.25
function module.new(this,balloon,context)
this.heat = 0
end
function module.run(this,balloon,context)
this.heat = this.heat * (1 - cool_rate * context.delta) -- wrong but idc
local force = Vector3.new(0,1,0) * (1-(1/(1+this.heat))) * 1000 * context.delta * balloon.Size.Magnitude
balloon:ApplyImpulseAtPosition(force,balloon.Position)
context.replicate(this,balloon,module,this.heat)
return true
end
function module.replicate(this,balloon,context,heat)
this.heat = heat
end
module.update = module.new
module.messages = {
flux = function(this,balloon,context,heat)
this.heat = this.heat + heat
end,
}
module.messages.default = nil
module.manifest = {
"LongBalloon",
"RoundBalloon"
}
return module
+54
View File
@@ -0,0 +1,54 @@
local module = {}
local function burn_effect(this,boiler,context,burn)
for index,item in pairs(this.burning) do
item.Color = Color3.fromRGB(255, 137, 73):Lerp(Color3.fromRGB(115, 61, 33),1-math.abs(burn))
end
end
function module.new(this,boiler,context)
this.burning = {}
this.scale = boiler:GetExtentsSize().Magnitude
this.exhaust = _G.GetInstance(boiler,"Exhaust")
for index,item in pairs(boiler:GetChildren()) do
if item.Name == "Fire" then
table.insert(this.burning,item)
end
end
return this
end
function module.idle(this,boiler) return true end
local begin = tick()
module.run = function(this,boiler,context)
local throttle = this.throttle or context.controls[_G.GetValue(boiler,"Binding","throttle")] or this.Throttle or 0
context.replicate(this,boiler,module,throttle)
local objects = {}
context.connected("ConnectorFume","Exhaust",this.exhaust,function(object)
table.insert(objects,object)
end)
for index,object in pairs(objects) do
context.message(object,"flux",this.scale * math.abs(throttle) * context.delta * (1/#objects))
end
return true
end
module.replication_delta = 0.25
function module.replicate(this,boiler,context,burn)
burn_effect(this,boiler,context,burn)
end
module.messages = {
control = function(this,object,context,value)
this.throttle = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Boiler1",
"Boiler2"
}
return module
+26
View File
@@ -0,0 +1,26 @@
local module = {}
function module.new(this,boombox,context)
this.sound = _G.GetInstance(_G.GetInstance(boombox,"Plank"),"Sound")
end
module.messages = {
source = function(this,speaker,context,value)
this.sound.SoundId = "rbxassetid://"..(tostring(value or "") or "")
end,
default = function(this,speaker,context,value)
if value > 0.5 then
this.sound:Play()
elseif value == 0 then
this.sound:Pause()
elseif value < -0.5 then
this.sound:Stop()
end
end,
}
module.manifest = {
"Boombox"
}
return module
+19
View File
@@ -0,0 +1,19 @@
local module = {}
local partStateModule = require(_G.Modules.PartStateModule)
function module.new(this,cable,context)
end
function module.run(this,cable,context)
if context.frequency(this,"cable_material",1) then cable.Material = Enum.Material.Fabric end
return true
end
module.update = module.new
module.manifest = {
"DataCable"
}
return module
+59
View File
@@ -0,0 +1,59 @@
local module = {}
local programModule = require(_G.Modules.ProgramModule)
function module.new(this,computer,context)
this.value = _G.GetInstance(computer,"Program")
this.led = _G.GetInstance(computer,"LED")
this.program = programModule.open_program(this.value,nil)
this.program:load()
this.inputs = {}
this.output = function(pin,name,value)
context.connected("ConnectorData","ConnectorEnd",computer[pin],function(object)
context.message(object,name,value)
end)
end
this.input = function(name)
return this.inputs[name]
end
this.control = function(name)
return context.controls[name]
end
this.message = function(title,value)
if context.queue(this,"notification",3) then _G.Events.Notification:Fire(title,value) end
end
this.led.Color = Color3.fromRGB(255,255,255)
return this
end
local begin = tick()
module.run = _G.Protect(function(this,computer,context)
if not this.program then this.led.Color = Color3.fromRGB(255,0,0) return true end
this.led.Color = Color3.fromRGB(0,255,0)
this.program.variables = this.variables or {}
this.program:run(this)
context.replicate(this,computer,module,this.program.variables)
return true
end)
function module.remove(this,computer,context)
end
module.replication_delta = 0.25
function module.replicate(this,computer,context,variables)
this.variables = variables
end
module.messages = {
default = function(this,object,context,value)
local pin = object.Name
this.inputs[pin] = value
end
}
module.manifest = {
"Microcontroller"
}
return module
+44
View File
@@ -0,0 +1,44 @@
local module = {}
function module.new(this,disconnector,context)
this.grip = _G.GetInstance(disconnector,"Grip")
this.binding = _G.GetInstance(disconnector,"Binding")
return this
end
module.run = _G.Protect(function(this,disconnector,context)
if this.control or 0 < 0.5 and context.queue(this,module.name,0.4) then
context.replicate(this,disconnector,module)
elseif context.controls[this.binding.Value] or 0 < 0.5 then
context.replicate(this,disconnector,module)
end
return true
end)
module.replication_delta = 0.25
function module.replicate(this,disconnector,context)
if _G.IsClient then return end
for _,weld in pairs(this.grip:GetJoints()) do
if
weld:IsA("WeldConstraint") and
weld.Name == "BuiltWeldConstraint" and
weld.Parent
then
weld:Destroy()
end
end
this.grip.Transparency = 1
task.delay(0.3,function() this.grip.Transparency = 0 end)
end
module.messages = {
control = function(this,disconnector,context,value)
this.control = tonumber(value)
end,
}
module.manifest = {
"Disconnector"
}
return module
+23
View File
@@ -0,0 +1,23 @@
local module = {}
function module.new(this,display,context)
this.screen = _G.GetInstance(display,"Screen")
this.gui = _G.GetInstance(this.screen,"Gui")
this.text = _G.GetInstance(this.gui,"Label")
this.text.Text = "<b>PSx Display Driver 1.0</b>\nStandby for message..."
end
module.messages = {
default = function(this,display,context,value)
this.text.Text = tostring(value)
end,
color = function(this,display,context,value)
pcall(function() this.text.TextColor3 = value end)
end,
}
module.manifest = {
"Display"
}
return module
+41
View File
@@ -0,0 +1,41 @@
local module = {}
function module.new(this,emitter,context)
return this
end
function module.idle(this,engine) return true end
local begin = tick()
module.run = _G.Protect(function(this,emitter,context)
return true
end)
module.replication_delta = 0.25
function module.replicate(this,emitter,context,details)
end
module.messages = {
control = function(this,object,context,value)
this.throttle = value
end,
starter = function(this,object,context,value)
this.starter = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Engine",
"Engine2",
"BigEngine",
"BigRadial",
"Radial",
"Boiler1",
"Boiler2"
}
return module
+121
View File
@@ -0,0 +1,121 @@
local module = {}
local function rpm(rot)
return (rot/(math.pi*2))*60
end
local function rot(hinge)
return hinge.CFrame.Rotation:PointToObjectSpace(hinge.AssemblyAngularVelocity).Magnitude
end
function module.new(this,engine,context)
this.powered = _G.GetValue(engine,"Powered",true)
this.inverted = _G.GetInstance(engine,"Inverted")
this.speed = _G.GetInstance(engine,"Speed")
this.rotor = _G.GetInstance(engine,"Hinge")
this.hinge = _G.GetInstance(engine,"HingeConstraint")
this.rotor_sound = this.rotor:FindFirstChild("RotorSound")
this.starter_sound = this.rotor:FindFirstChild("Starter")
this.starter_ratio = engine:GetAttribute("StarterRatio") or 1
if this.powered then
this.hinge.ActuatorType = Enum.ActuatorType.Motor
else
this.hinge.ActuatorType = Enum.ActuatorType.None
end
this.hinge.LimitsEnabled = false
this.torque = engine:GetScale() * 100000
return this
end
function module.idle(this,engine) return true end
local begin = tick()
module.run = _G.Protect(function(this,engine,context)
local throttle = this.throttle or context.controls[_G.GetValue(engine,"Binding","throttle")] or this.Throttle or 0
if this.inverted.Value then throttle = throttle * -1 end
local starter = math.abs(throttle) > 0.1
local target_speed = this.speed.Value * throttle
local current_speed = rot(this.rotor)
local accel
if math.abs(current_speed) > this.speed.Value * 0.1 then
-- Running
starter = false
accel = 40
else
-- Starting
accel = 40 * this.starter_ratio --this.torque * (math.clamp(math.noise(0.123,0.456,(begin-tick())*0.5),0,1) + 0.5) * 0.2
end
this.hinge.MotorMaxAcceleration = accel
if math.abs(throttle) > 0.05 then
this.hinge.AngularVelocity = target_speed
else
this.hinge.AngularVelocity = 0
end
if not this.torque then print(this) return false end
this.hinge.MotorMaxTorque = this.torque * ((engine:GetAttribute("Health") or 1) / (engine:GetAttribute("MaxHealth") or 1)) * (1/(this.speed.Value + 1)) * 60
context.replicate(this,engine,module,{Throttle = throttle, Starter = starter})
return true
end)
local legal_detail = {
Throttle = true,
Mixture = true,
Temperature = true,
Oil = true,
Air = true,
Fuel = true,
Coolant = true,
Power = true
}
module.replication_delta = 0.25
function module.replicate(this,engine,context,details)
for index,item in pairs(details) do
if legal_detail[index] then
this[index] = item
engine:SetAttribute(index,item)
end
end
local speed = math.abs(rot(this.rotor))
if details.Starter then
-- Starter on
if this.starter_sound then if not this.starter_sound.Playing then this.starter_sound:Play() end end
if this.rotor_sound then if this.rotor_sound.Playing then this.rotor_sound:Pause() end end
elseif speed > this.speed.Value * 0.1 then
-- Running
if this.rotor_sound then
if not this.rotor_sound.Playing then this.rotor_sound:Play() end
_G.Print(engine,this.rotor_sound.PlaybackSpeed,speed,this.speed.Value/60)
this.rotor_sound.PlaybackSpeed = math.clamp(math.abs(speed/150) * 1.5,0.5,1.5)
this.rotor_sound.Volume = math.clamp(math.abs(speed/150) * 0.05 + 0.05,0,0.1)
end
if this.starter_sound then if this.starter_sound.Playing then this.starter_sound:Stop() end end
else
if this.rotor_sound then if this.rotor_sound.Playing then this.rotor_sound:Stop() end end
if this.starter_sound then if this.starter_sound.Playing then this.starter_sound:Stop() end end
end
end
module.messages = {
control = function(this,object,context,value)
this.throttle = value
end,
starter = function(this,object,context,value)
this.starter = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Engine",
"Engine2",
"BigEngine",
"BigRadial",
"Radial",
"Jet",
"Motor",
"Motor1",
"BigSpinWheel",
"SpinWheel"
}
return module
+37
View File
@@ -0,0 +1,37 @@
local module = {}
local limit = 4
local bullet = require(_G.Modules.BulletModule)
function module.new(this,gun,context)
this.barrel = _G.GetInstance(gun,"Barrel")
this.binding = _G.GetInstance(gun,"Binding")
this.rate = _G.GetValue(gun,"Rate",0.15)
this.last = tick()
end
module.run = _G.Protect(function(this,gun,context)
if (this.control or context.controls[this.binding.Value] or 0) < 0.5 then return true end
if tick() - this.last < this.rate then return true end
-- Fire
bullet.fire(this.barrel)
this.last = tick()
this.barrel:ApplyImpulse(this.barrel.CFrame.RightVector * -500)
return true
end)
module.fire_rate = 0.05
module.messages = {
control = function(this,gun,context,value)
this.control = value
end,
}
module.manifest = {
"Gun",
"Cannon"
}
return module
+34
View File
@@ -0,0 +1,34 @@
local module = {}
function module.new(this,hinge,context)
this.hinge = _G.GetInstance(hinge,"HingeConstraint")
local function update()
if this.limited then this.hinge.LimitsEnabled = this.limited.Value end
if this.lower then this.hinge.LowerAngle = this.lower.Value end
if this.upper then this.hinge.UpperAngle = this.upper.Value end
end
if _G.IsServer then
this.limited = hinge:FindFirstChild("Limited")
this.upper = hinge:FindFirstChild("Upper")
this.lower = hinge:FindFirstChild("Lower")
if this.limited then this.limited.Changed:Connect(update) end
if this.lower then this.lower.Changed:Connect(update) end
if this.upper then this.upper.Changed:Connect(update) end
end
return this
end
module.manifest = {
"FlatServoRoll",
"FlatServoPitch",
"Servo",
"ControlSheet",
"NanoControlSheet",
"MetalServo",
"Spinner",
"Hinge",
"Rotator",
"RotatorPlate"
}
return module
+45
View File
@@ -0,0 +1,45 @@
local module = {}
function position(motor,phase,phases,target,origin)
motor.C0 = origin
local alpha = (phase)/(phases-1)
motor.C1 = CFrame.new():Lerp(target,alpha)
end
function module.new(this,lever,context)
this.motor = _G.GetInstance(lever,"Motor")
this.phase = _G.GetInstance(lever,"Phase")
this.phases = _G.GetInstance(lever,"Phases")
this.hold = _G.GetInstance(lever,"Hold")
this.click = _G.GetInstance(this.hold,"ClickDetector")
this.target = lever:GetAttribute("Target") or error("Needs target attribute.")
this.origin = lever:GetAttribute("Origin") or error("Needs target attribute.")
if _G.IsServer then
module.update(this,lever,context)
this.click.MouseClick:Connect(function()
if this.phase.Value == this.phases.Value - 1 then
this.phase.Value = 0
else
this.phase.Value += 1
end
print(this.phase.Value)
module.update(this,lever,context)
context.connected("ConnectorData",nil,lever.PrimaryPart,function(object)
print("firing",this.phase.Value)
context.message(object,nil,this.phase.Value/(this.phases.Value-1))
end)
end)
end
end
function module.update(this,lever,context)
if _G.IsServer then
position(this.motor,this.phase.Value,this.phases.Value,this.target,this.origin)
end
end
module.manifest = {
"Lever"
}
return module
+26
View File
@@ -0,0 +1,26 @@
local module = {}
local function setup(this,mesh,context)
this.mesh.MeshId = string.format("rbxassetid://%i",this.meshid.Value)
this.mesh.TextureId = string.format("rbxassetid://%i",this.textureid.Value)
this.mesh.Scale = mesh.Size * this.scale.Value
end
function module.new(this,mesh,context)
this.meshid = _G.GetInstance(mesh,"MeshId")
this.textureid = _G.GetInstance(mesh,"TextureId")
this.scale = _G.GetInstance(mesh,"Scale")
this.mesh = _G.GetInstance(mesh,"Mesh")
setup(this,mesh,context)
return this
end
function module.update(this,mesh,context)
setup(this,mesh,context)
end
module.manifest = {
"Prop"
}
return module
+40
View File
@@ -0,0 +1,40 @@
local module = {}
function module.new(this,piston,context)
this.servo = _G.GetInstance(piston,"Servo")
this.extension = _G.GetInstance(piston,"Extension")
this.binding = _G.GetInstance(piston,"Binding")
this.inverted = _G.GetInstance(piston,"Inverted")
this.force = _G.GetInstance(piston,"Force")
return this
end
local begin = tick()
module.run = _G.Protect(function(this,servo,context)
local control = (this.control or context.controls[this.binding.Value] or 0)
if this.inverted.Value then control = 1 - control end
this.servo.TargetPosition = servo.Extension.Value * control * servo:GetScale() + (this.offset or 0)
context.replicate(this,servo,module,this.servo.TargetPosition)
end)
module.replication_delta = 0.25
function module.replicate(this,servo,context,value)
servo.ServoMaxForce = this.force.Value * servo:GetScale() ^ 2
servo.TargetPosition = value
end
module.messages = {
control = function(this,object,context,value)
this.control = value
end,
offset = function(this,object,context,value)
this.offset = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Piston"
}
return module
+126
View File
@@ -0,0 +1,126 @@
local module = {}
local dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
local bias = 0.2
local propeller_power_mod = 0.1
local propeller_cut_mod = 5/2
local propeller_reaction_mod = 0.05
game.Workspace.CompensateDensity.Changed:Connect(function()
dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
end)
_G.Events.Command.Event:Connect(function(message)
local value = message:match("setprop%((.-)%)")
if not value then return end
if tonumber(value) then
game.Workspace.CompensateDensity = tonumber(value)
_G.Events.Notification:Fire("Set Prop Density","PropBias.Value = "..value)
else
_G.Events.Notification:Fire("Set Prop Density","Invalid number.")
end
end)
local function setup(this,propeller,context)
local angle = _G.GetValue(propeller,"Trim",30)
for _,child in pairs(propeller:GetChildren()) do
if child.Name == "Blade" then
local welds = child:GetJoints()
local joint = child:FindFirstChild("Weld")
if not child:FindFirstChild("Weld") then
local c0 = propeller.PrimaryPart.CFrame:ToObjectSpace(child.CFrame)
joint = Instance.new("Motor6D")
joint.Name = "Weld"
joint.Parent = child
joint.Part0 = propeller.PrimaryPart
joint.Part1 = child
joint.C0 = c0
end
local flip = 0
if angle < 0 then flip = 180 end
local axis = (propeller:GetAttribute("Axis") or Vector3.new(0,0,1)) * (math.rad(-angle+flip))
local rotation = CFrame.Angles(axis.X,axis.Y,axis.Z)
joint.C1 = rotation
for _,weld in pairs(welds) do if weld:IsA("WeldConstraint") then weld:Destroy() end end
end
end
end
function module.new(this,propeller,context)
local size = propeller:GetExtentsSize()
this.scale = size.X * size.Z * 1.5
this.length = size.X
this.power = _G.GetValue(propeller,"Force",1000) * this.scale * 0.01
this.direction = _G.GetValue(propeller,"Direction",CFrame.identity)
this.rotor = propeller:FindFirstChild("Spin") or propeller:FindFirstChild("Hinge")
this.trim = _G.GetValue(propeller,"Trim",30)
this.blur = propeller:FindFirstChild("Blur")
if not this.rotor then error("Missing rotor") end
this.force = _G.GetInstance(propeller,"VectorForce")
this.sound = _G.GetInstance(this.rotor,"PropSound")
this.sound:Play()
setup(this,propeller,context)
return this
end
function module.update(this,propeller,context)
print("upd")
setup(this,propeller,context)
end
function module.idle(this,engine) return true end
local a = 300
local function limit(f)
return math.tanh(f/a)*a
end
local begin = tick()
module.run = _G.Protect(function(this,propeller,context)
local angular_velocity = ((this.rotor.CFrame.Rotation * this.direction):PointToObjectSpace(this.rotor.AssemblyAngularVelocity)).X
local linear_velocity = angular_velocity * math.sin(math.rad(this.trim))
local air_velocity = this.rotor.CFrame.Rotation:PointToObjectSpace(this.rotor.AssemblyLinearVelocity).X
local real_velocity = limit((linear_velocity * propeller_cut_mod * this.length) - air_velocity)
local rpm = (angular_velocity/(math.pi*2))*60
local multiplier = (this.sound:GetAttribute("Multiplier") or 1) * 0.01
this.force.Force = Vector3.new(0,0,-real_velocity*this.power*propeller_power_mod*dens)
this.rotor:ApplyAngularImpulse(
(this.rotor.CFrame.Rotation * this.direction).RightVector
* -angular_velocity
* math.cos(math.rad(this.trim))
* propeller_reaction_mod)
context.replicate(this,propeller,module,math.abs(rpm*dens*multiplier))
return true
end)
module.replication_delta = 0.1
function module.replicate(this,propeller,context,value)
this.sound.PlaybackSpeed = math.clamp(value,0.1,1.5)
this.sound.Volume = math.clamp(value,0,this.sound:GetAttribute("Max") or 1)
if this.blur then
this.blur.Transparency = math.clamp(1-value,0.3,1)
end
for _,blade in pairs(propeller:GetChildren()) do
if blade.Name == "Blade" then
blade.Transparency = math.clamp(value,0,0.7)
end
end
end
module.messages = {
trim = function(this,object,context,value)
end
}
module.messages.default = module.messages.trim
module.manifest = {
"Propeller",
"Turbine",
"LargePropeller",
"BiPropeller",
"Jet"
}
return module
+46
View File
@@ -0,0 +1,46 @@
local module = {}
function module.new(this,rocket,context)
this.scale = rocket:GetExtentsSize().Magnitude -- same as with the equivalent statement on the wing! BRRRRRRRRRRR
this.binding = _G.GetInstance(rocket,"Binding")
this.exhaust = _G.GetInstance(rocket,"Exhaust")
this.sound = _G.GetInstance(this.exhaust,"Sound")
this.force = _G.GetInstance(rocket,"Force")
this.inverted = _G.GetInstance(rocket,"Inverted")
return this
end
function module.idle(this,engine) return true end
local begin = tick()
module.run = _G.Protect(function(this,engine,context)
local throttle = this.throttle or context.controls[_G.GetValue(engine,"Binding","throttle")] or this.Throttle or 0
if this.inverted.Value then throttle = -throttle end
context.replicate(this,engine,module,math.clamp(throttle,0,1))
return true
end)
module.replication_delta = 0.25
function module.replicate(this,rocket,context,throttle)
this.exhaust:ApplyImpulseAtPosition(this.exhaust.CFrame.RightVector * throttle * this.scale * this.force.Value * context.delta * -1000, this.exhaust.Position)
this.exhaust.Flame.Rate = throttle * 1000
this.sound.Volume = math.clamp(throttle,0,1)
if _G.IsServer and this.sound.Volume > 0 then this.sound:Play() else this.sound:Pause() end
end
module.messages = {
control = function(this,object,context,value)
this.throttle = value
end,
starter = function(this,object,context,value)
this.starter = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Rocket",
"BigRocket"
}
return module
+91
View File
@@ -0,0 +1,91 @@
local module = {}
local partStateModule = require(_G.Modules.PartStateModule)
function setup(this,rope,context)
local function new(name,tab)
local part = Instance.new(name)
_G.Union(part,tab)
return part
end
local node1 = rope["1"]
local node2 = rope["2"]
if rope.Name == "Shaft" then -- Make two universal constraints
local constraint1 = new("UniversalConstraint",{
Parent = node1,
Attachment0 = new("Attachment",{
Parent = node1
}),
Attachment1 = new("Attachment",{
Parent = rope,
WorldCFrame = node1.CFrame
})
})
local constraint2 = new("UniversalConstraint",{
Parent = node2,
Attachment0 = new("Attachment",{
Parent = node2
}),
Attachment1 = new("Attachment",{
Parent = rope,
WorldCFrame = node2.CFrame
})
})
elseif rope.Name == "Rope" or rope.Name == "Rod2" then
rope.Transparency = 1
rope.CanCollide = false
rope.Massless = true
local name
if rope.name == "Rope" then name = "RopeConstraint" end
if rope.Name == "Rod2" then name = "RodConstraint" end
local constraint = new(name,{
Parent = rope,
Attachment0 = new("Attachment",{
Parent = node1
}),
Attachment1 = new("Attachment",{
Parent = node2
}),
Visible = true,
Thickness = rope.Size.Y
})
if rope.Length.Value == 0 then rope.Length.Value = constraint.CurrentDistance end
constraint.Length = rope.Length.Value
end
node1.CanCollide = true -- Resize and make nodes non-collide
node2.CanCollide = true
node1.Size = Vector3.one * rope.Size.Y
node2.Size = node1.Size
for index,joint in pairs(rope:GetJoints()) do -- Fix welds in new version
if joint:IsA("WeldConstraint") then
local other = joint.Part0
if joint.Part0 == rope then other = joint.Part1 end
local closest = node1
if (node2.Position-other.Position).Magnitude < (node1.Position-other.Position).Magnitude then closest = node2 end
new("WeldConstraint",{
Parent = rope,
Part0 = closest,
Part1 = other,
Name = "BuiltWeldConstraint"
})
if index > 1 then joint:Destroy() end
end
end
end
function module.new(this,rope,context)
if _G.IsServer then setup(this,rope,context) end
return this
end
module.manifest = {
"Rod2",
"Rope",
"Shaft"
}
return module
+99
View File
@@ -0,0 +1,99 @@
local module = {}
local function targetHinge(target,hinge,stationary,angle,negative)
local attachment = stationary:FindFirstChildWhichIsA("Attachment")
local o = attachment.WorldCFrame:PointToObjectSpace(target)
local x,y,z = CFrame.lookAlong(attachment.WorldCFrame.Position,attachment.WorldCFrame.UpVector,attachment.WorldCFrame.RightVector):ToObjectSpace(
CFrame.lookAt(
attachment.WorldCFrame.Position,
attachment.WorldCFrame:PointToWorldSpace(Vector3.new(0,o.Y,o.Z)*negative)
)):ToEulerAnglesYXZ()
hinge.TargetAngle = -math.deg(y) + angle
end
function module.new(this,servo,context)
this.scale = servo:GetExtentsSize().Magnitude
this.hinge = _G.GetInstance(servo,"HingeConstraint")
this.binding = _G.GetInstance(servo,"Binding")
this.inverted = servo:FindFirstChild("Inverted")
this.offset = servo:FindFirstChild("Offset")
this.range = servo:FindFirstChild("Angle")
this.target = servo:FindFirstChild("Target")
this.torque = servo:FindFirstChild("Torque")
return this
end
local begin = tick()
module.run = _G.Protect(function(this,servo,context)
local shouldInvert = 1
local shouldOffset = 0
local pitchTrim = 0
local angle = 30
if context.seat and context.seat:FindFirstChild("PitchTrim") then pitchTrim = context.seat.PitchTrim.Value end
if this.inverted and this.inverted.Value then shouldInvert = -1 end
if this.offset and this.offset.Value then shouldOffset = this.offset.Value + (this.starter or 0) end
if this.range and this.range.Value then angle = this.range.Value end
if this.torque and this.torque.Value then this.hinge.MotorMaxTorque = this.torque.Value * ((servo:GetAttribute("Health") or 1) / (servo:GetAttribute("MaxHealth") or 1)) end
local control = (this.control or context.controls[this.binding.Value])
if this.target and this.target.Value then
this.hinge.LimitsEnabled = false
if context.cursor then
targetHinge(context.cursor,this.hinge,this.hinge.Attachment1.Parent,this.offset.Value,shouldInvert)
end
elseif this.binding.Value == "auto" and context.seat then
local pos = context.seat:GetPivot():ToObjectSpace(this.hinge.Attachment1.WorldCFrame)
local dir = pos.RightVector
pos = pos.Position.Unit
this.hinge.LimitsEnabled = false
this.hinge.TargetAngle = ( (
-context.controls.pitch*dir.x*pos.Z
-context.controls.yaw*dir.y*pos.Z
+context.controls.roll*dir.x*pos.X
) * angle + pitchTrim*dir.x*pos.Z
) * shouldInvert + shouldOffset
elseif control then
if this.binding.Value == "pitch" then shouldOffset = shouldOffset + pitchTrim end
this.hinge.LimitsEnabled = true
this.hinge.TargetAngle = control * angle * shouldInvert + shouldOffset
end
local distance = (this.hinge.Attachment1.WorldCFrame.Position - this.hinge.Attachment1.WorldCFrame.Position).Magnitude
if distance < 2 then
context.replicate(this,servo,module,this.hinge.TargetAngle,this.hinge.LimitsEnabled)
return true
else
context.replicate(this,servo,module,nil,nil)
return false
end
end)
module.replication_delta = 0.25
function module.replicate(this,servo,context,angle)
if not angle then
this.hinge:Destroy()
else
this.hinge.TargetAngle = angle
end
end
module.messages = {
control = function(this,object,context,value)
this.control = value
end,
offset = function(this,object,context,value)
this.starter = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"FlatServoRoll",
"FlatServoPitch",
"Servo",
"ControlSheet",
"NanoControlSheet",
"MetalServo"
}
return module
+26
View File
@@ -0,0 +1,26 @@
local module = {}
function module.new(this,speaker,context)
this.main = _G.GetInstance(speaker,"Main")
this.tts = _G.GetInstance(this.main,"AudioTextToSpeech")
end
function module.replicate(this,speaker,context,value)
this.tts.Text = tostring(value or "") or ""
if _G.IsClient then
if this.tts.IsPlaying then return end
this.tts:Play()
end
end
module.messages = {
default = function(this,speaker,context,value)
context.replicate(this,speaker,module,value)
end
}
module.manifest = {
"Speaker"
}
return module
+27
View File
@@ -0,0 +1,27 @@
local module = {}
local spring_modifier = 1
local function setup(this,spring,context)
this.spring.Damping = _G.GetValue(spring,"Dampness",this.spring.Damping)
this.spring.Stiffness = _G.GetValue(spring,"Stiffness",this.spring.Stiffness) * spring_modifier * spring:GetScale()^3
this.spring.Visible = this.spring.Stiffness > 0
end
function module.new(this,spring,context)
this.spring = _G.GetInstance(spring,"SpringConstraint")
setup(this,spring,context)
end
function module.update(this,spring,context)
print("setup")
setup(this,spring,context)
end
module.update = module.new
module.manifest = {
"Coilover",
"Wheel"
}
return module
+38
View File
@@ -0,0 +1,38 @@
local module = {}
local cool_rate = 0.5
function module.new(this,turbine,context)
this.heat = 0
this.hinge = _G.GetInstance(turbine,"HingeConstraint")
this.speed = _G.GetInstance(turbine,"Speed")
this.inverted = _G.GetInstance(turbine,"Inverted")
end
function module.run(this,turbine,context)
this.heat = this.heat * (1 - cool_rate * context.delta) -- wrong but idc
local mul = 1
if this.inverted.Value then mul = -1 end
this.hinge.AngularVelocity = (1-(1/(1 + this.heat))) * this.speed.Value * mul
this.hinge.MotorMaxTorque = 10000 * this.speed.Value
context.replicate(this,turbine,module,this.heat)
return true
end
function module.replicate(this,turbine,context,heat)
this.heat = heat
end
module.update = module.new
module.messages = {
flux = function(this,turbine,context,heat)
this.heat = this.heat + heat
end,
}
module.messages.default = nil
module.manifest = {
"Turbine1"
}
return module
+205
View File
@@ -0,0 +1,205 @@
chat = {}
local rchat=game:GetService("Chat")
function chat.Chat(ignore,object,text)
if not object then return end
rchat:Chat(object,text)
--print("AUTO:",text) --
end
local function advancedTarget(pS,t,o) -- pS = projectileSpeed, t = targetPart, o = originPart
-- So, I understand the maths in this. I also understand that it forms a circle for radius of targetting, also the curve to find it is quadratic and there is sine somewhere too
-- https://devforum.roblox.com/t/making-a-lead-shot-indicator/325666/9 (figure out the maths here when you're older, i understand it but not confidently)
local tV = t.Velocity * 1.1 -- target Velocity, *1.5 because of compensation
local di = t.Position - o.Position -- displacement, origin to target
local a = pS^2 - tV:Dot(tV) -- a,b,c as in quadratic equation so that ax^2 + bx + c = 0
local b = -2 * tV:Dot(di)
local c = -di:Dot(di)
local d = b^2 - 4*a*c -- d as an intermediate value for quadratic formula
if d < 0 then return t.Position end -- failed, let's just hope we hit it anyways by luck
local t0 = (-b + math.sqrt(d)) / (2*a) -- hello mr. hegarty, i exist
local t1 = (-b - math.sqrt(d)) / (2*a)
local ti = 0
if t0 > 0 then ti = t0 end
if t1 > 0 then ti = t1 end
local target = t.CFrame.Position + tV * ti
return target
end
function travelToLocation(a)
a.humanoid:MoveTo(a.target.Position)
end
function findPlayer(a)
local closest
local distance = 100
for _,player in pairs(game.Players:GetPlayers()) do
if player.Character and player.Character:FindFirstChild("Humanoid") then
local currentDistance = (player.Character.PrimaryPart.Position - a.head.Position).Magnitude
if currentDistance < distance then distance = currentDistance end
closest = player.Character.PrimaryPart
end
end
if closest then a.task = travelToLocation a.target = closest end
end
function enterPlane(a)
--[[local pathfinding = game:GetService("PathfindingService")
local path = pathfinding:CreatePath({
AgentCanJump = true,
AgentHeight = 4,
AgentWidth = 4
})
chat:Chat(a.head,"computing")
path:ComputeAsync(a.model.PrimaryPart.Position,a.plane.Position)
if path.Status ~= Enum.PathStatus.Success then
chat:Chat(a.head,"no path")
a.model:SetPrimaryPartCFrame(a.plane.CFrame)
a.task = getTask return end
a.plane.Material = Enum.Material.Neon
a.plane.Transparency = 0.5
chat:Chat(a.head,"walking")
local waypointIndex = 0
local waypoints = path:GetWaypoints()
local function operateWaypoint()
if not a.operate then return end
if a.humanoid.SeatPart then chat:Chat(a.head,"piloting") a.task = flyPlane return end
waypointIndex = waypointIndex + 1
if waypointIndex > #waypoints or not waypoints[waypointIndex] then return end
a.humanoid:MoveTo(waypoints[waypointIndex].Position)
local shouldReturn = false
local event = a.humanoid.MoveToFinished:Connect(function()
if waypoints[waypointIndex] and not a.humanoid.SeatPart and waypoints[waypointIndex].Action == Enum.PathWaypointAction.Jump then
a.humanoid.Jump = true
end
shouldReturn = true
operateWaypoint()
end)
wait(1)
if not a.plane or not a.plane:FindFirstChild("Pilot") then return end
event:Disconnect()
if (a.model.PrimaryPart.Position - a.plane.Position).Magnitude < 6 then
end
if shouldReturn then return end
operateWaypoint()
end
operateWaypoint()]]
a.model:SetPrimaryPartCFrame(a.plane.CFrame)
a.task = flyPlaneStraight
a.v.parent = a.plane.Parent.Parent
a.target = game.Workspace.BotTarget.Position
end
local flight = require(_G.Modules.ObjectInteraction.Main)
function flyPlaneStraight(a)
if not a.flyPlaneStraightTick then a.flyPlaneStraightTick = tick() end
if (tick() - a.flyPlaneStraightTick) > 3 then a.task = flyPlane a.flyPlaneStraightTick = nil end
a.v.bindings.throttle = 0.8
a.v.bindings.pitch = 0.3
a.v.bindings.roll = 0
a.v.bindings.yaw = 0
local tar = a.plane.CFrame.LookVector
tar = Vector3.new(tar.X,0,tar.Z).Unit
local pos = (a.plane.Position + tar * 1000 + Vector3.new(0,150,0))
a.v:aimTowards(pos)
end
function stop(a)
end
local trajectoryPart = Instance.new("Part")
for index,item in pairs({
Color = Color3.new(1,1,1),
Material = Enum.Material.Neon,
Transparency = 0.2,
Size = Vector3.new(6,6,6),
CanCollide = false,
Anchored = true,
Shape = Enum.PartType.Ball,
Parent = game.Workspace
}) do
trajectoryPart[index] = item
end
local targetPart = trajectoryPart:Clone()
targetPart.Parent = game.Workspace
function flyPlane(a)
a.target = a.model.Target.Value.PrimaryPart.Position
local trajectory = a.plane.Velocity*2 + a.plane.Position
trajectoryPart.Position = trajectory
targetPart.Position = a.target
print(trajectory)
if trajectory.Y < -20 then
a.task = flyPlaneStraight
print("recover!")
end
a.v.bindings.pitch = 0.0
a.v.bindings.yaw = 0
a.v:aimTowards(a.target)
local speed = a.plane.Velocity.Magnitude
if speed > 350 or speed < 50 or a.plane.Position.Y < -40 then print("stop!",speed) a.task = stop a.v.bindings.throttle = 0 task.delay(1,function() a.humanoid.Health = 0 end) end
end
function getTask(a)
chat:Chat(a.head,"task")
wait(0.5)
if a.humanoid.SeatPart then chat:Chat(a.head,"piloting") a.task = flyPlaneStraight return end
a.task = locatePlane
chat:Chat(a.head,"pilot")
end
function selfDestruct(a)
chat:Chat(a.head,"destruct")
a.humanoid.Health = 0
end
function locatePlane(a)
chat:Chat(a.head,"find plane")
a.plane = nil
local distance = 100
for _,object in pairs(game.Workspace.Auto:GetChildren()) do
local seat = object:FindFirstChildWhichIsA("VehicleSeat",true)
if seat and not seat.Occupant then
local offset = (seat.Position - a.head.Position).Magnitude
if offset < distance then
distance = offset
a.plane = seat
end
end
end
if a.plane then
chat:Chat(a.head,"found plane")
a.task = enterPlane
return
end
chat:Chat(a.head,"no plane")
a.task = selfDestruct
end
function hibernate(a)
end
function init(auto)
local a = {}
a.model = auto
a.head = auto.Head
if a.head:FindFirstChildWhichIsA("SurfaceGui") then a.head.SurfaceGui:Destroy() end
a.face = a.head.Face
a.humanoid = auto.Humanoid
a.team = auto.Team.Value
a.operate = true
wait()
a.plane = a.humanoid.SeatPart
a.task = flyPlaneStraight
a.v = flight.initialise(a.head)
a.v.parent = a.humanoid.SeatPart.Parent.Parent
a.v:seatEntered(true,a.humanoid.SeatPart)
a.target = (a.plane.CFrame * CFrame.new(0,0,1000)).Position
a.target = Vector3.new(a.target.X,a.plane.CFrame.Position.Y + 100,a.target.Z)
a.humanoid.Died:Connect(function()
wait(2)
a.operate = false
a.v.operate = false
a.v:destroy()
a.model:Destroy()
end)
while wait((1/30)*3) and a.operate do
a.task(a)
end
end
return getfenv()
+63
View File
@@ -0,0 +1,63 @@
local autoModel = _G.Storage.Assets.Autos:WaitForChild("Auto")
local planeModel = _G.Storage.Assets.Vehicles:WaitForChild("BP-10")
local purchaseModule = require(game.ServerScriptService.PurchaseModule)
teams = {}
function init(parent)
wait(1)
local m = parent
local team = game.Teams[parent.Parent.Name]
local autoSpawn = parent:WaitForChild("AutoSpawn")
local planeSpawn = parent:WaitForChild("PlaneSpawn")
local function lockPlane(plane)
plane.Pilot.Anchored = true
plane.Pilot.Changed:Connect(function(name)
if name == "Occupant" then
if plane.Pilot.Occupant then wait(3) plane.Pilot.Anchored = false end
end
end)
end
if not teams[team.Name] then teams[team.Name] = 0 end
local function spawnAuto()
local property = game.Workspace.Property[team.Name]
local newAuto = autoModel:Clone()
newAuto.Parent = property
newAuto.PrimaryPart.CFrame = autoSpawn.CFrame + Vector3.new(0,2,0)
newAuto["Body Colors"].TorsoColor = team.TeamColor
local planeModel = planeModel:Clone()
planeModel.Parent = property
planeModel:SetPrimaryPartCFrame(planeSpawn.CFrame)
--lockPlane(planeModel)
purchaseModule.colorVehicle(planeModel,team)
purchaseModule.collisionCheck(planeModel,0.5,10)
newAuto.Humanoid.Died:Connect(function()
wait(3)
planeModel:Destroy()
newAuto:Destroy()
teams[team.Name]-=1
end)
teams[team.Name] += 1
end
while parent.Parent do
if not (teams[team.Name] < _G.Storage.Variables.AutoNumber.Value) then wait(30) continue end
teams[team.Name] = 0
for _,object in pairs(game.Workspace.Property[team.Name]:GetChildren()) do
if object.Name == "Auto" and object.PrimaryPart and object.PrimaryPart.Velocity.Magnitude > 30 then -- and object.PrimaryPart.Velocity.Magnitude > 50 then
teams[team.Name]+=1
end
end
_G.AutoTeams = teams
--print("Auto: ",team.Name,teams[team.Name])
spawnAuto()
wait(30)
end
end
return getfenv()
+123
View File
@@ -0,0 +1,123 @@
require(game.ReplicatedFirst.ReadyModule)("GenerationForeman")
local rules = require(_G.Modules.RuleModule)
local CHUNK_SIZE = 512 -- wae
local RADIUS = 3
--if _G.IsStudio then RADIUS = 1 end -- stop my computer from dying
local LEEWAY = 20 -- WHY IS THIS SO BIG WTF
local COMBAT_RADIUS = 6
local POOL_SIZE = 4 --
local CHUNK_OFFSET_X = -1150
local CHUNK_OFFSET_Z = 120
local pool = 0
local chunks = {}
local schunks = {}
local _c = {}
_c.__index = _c
_c.delete = function(this)
schunks[this.index.x][this.index.z] = nil
--if #schunks[index.x] == 0 then schunks[index.x] = nil end
if this.generator then this.generator:SendMessage("delete") end
chunks[this.index] = nil
end
local function chunk(x,z)
if not schunks[x] then schunks[x] = {} end
local i = {x=x,z=z}
local first,second
local c
c = {viable = true, permanent = nil, generator = nil, started = false, index = i}
setmetatable(c,_c)
chunks[i]=c
schunks[x][z]=c
spawn(function()
repeat wait() until pool < POOL_SIZE
if not c.viable then return end
pool = pool + 1
c.generator = _G.Modules.Generator:Clone()
c.generator.Parent = game.Workspace
c.generator:SendMessage("generate",x * CHUNK_SIZE,z * CHUNK_SIZE,CHUNK_OFFSET_X,CHUNK_OFFSET_Z)
c.generator.Changed:Wait()
pool = pool - 1
end)
return c
end
local function generateCombatZone()
_G.Remotes.Notification:FireAllClients("Generation","Generating combat zone, please wait.")
--[[rules.setRule("GenerationProcessing",true)
for x = -COMBAT_RADIUS,COMBAT_RADIUS do
for z = -COMBAT_RADIUS*2,COMBAT_RADIUS*2 do
chunk(x,z).permanent = true
end
end
wait()
repeat wait() until pool == 0
rules.setRule("GenerationProcessing",false)]]
_G.Remotes.Notification:FireAllClients("Generation","Generation complete.")
end
local function generateForPlayer()
while wait(0.5) and rules.rules.GenerationFollowPlayer do
for index,chunk in pairs(chunks) do
chunk.viable = false
end
local perPlayer = function(p)
for index,chunk in pairs(chunks) do
if Vector2.new(p.x-index.x*CHUNK_SIZE,p.z-index.z*CHUNK_SIZE).Magnitude < CHUNK_SIZE*(RADIUS+LEEWAY) then
chunk.viable = true
end
end
for x=math.ceil(p.x/CHUNK_SIZE)-RADIUS,math.ceil(p.x/CHUNK_SIZE)+RADIUS do
for z=math.ceil(p.z/CHUNK_SIZE)-RADIUS,math.ceil(p.z/CHUNK_SIZE)+RADIUS do
if not schunks[x] then schunks[x] = {} end
if not schunks[x][z] then
chunk(x,z)
end
end
end
end
for _,player in pairs(game.Players:GetPlayers()) do
if player.Character then
if player.Character.PrimaryPart then
perPlayer(player.Character.PrimaryPart.Position)
end
end
end
perPlayer(game.Workspace.CurrentCamera.CFrame.Position)
for index,chunk in pairs(chunks) do
if not chunk.viable and not chunk.permanent then
chunk:delete()
end
end
end
end
local function reloadGeneration()
for index,chunk in pairs(chunks) do
chunk:delete()
end
end
rules.listenRule("GenerationNoiseOffset",function(x,z)
CHUNK_OFFSET_X = x
CHUNK_OFFSET_Z = z
reloadGeneration()
end)
rules.listenRule("GenerationFollowPlayer",function(yes)
if yes then generateForPlayer() else reloadGeneration() end
end)
local r = Random.new(os.time())
rules.listenRule("GenerationCombatZone",function()
CHUNK_OFFSET_X = r:NextInteger(-10000,10000)
CHUNK_OFFSET_Z = r:NextInteger(-10000,10000)
reloadGeneration()
generateCombatZone()
end)
+2
View File
@@ -0,0 +1,2 @@
require(game.ReplicatedFirst.ReadyModule)("GenerationInsurance")
if not _G.IsStudio then script.Parent.GenerationForeman.Enabled = true end
+793
View File
@@ -0,0 +1,793 @@
require(game.ReplicatedFirst.ReadyModule)("ServerScript")
local objectModule = require(_G.Modules.ObjectModule)
--local objectInteraction = require(_G.Modules.ObjectInteraction.Main)
local rules = require(_G.Modules.RuleModule)
local commands = require(_G.Modules.CommandsModule)
local partStateModule = require(_G.Modules.PartStateModule)
DEFAULT_MAX_SAVES = 20
SAVE_DATA_LIMIT = 5*10^6
PLAYER_INTERACTION_DISTANCE = 100
PLAYER_MAX_FLOOD = 10
PLAYER_FLOOD_TICK = 4
VEHICLE_SAVE_STORE = "VehicleSaveStore"
VEHICLE_SAVE_INFO_STORE = "VehicleSaveInfo"
USER_MOD_STORE = "ModStore"
MAIN_GAME_PLACEID = 11370438752
PLACE_COOLDOWN = 0.75
--[[if game.PlaceId ~= MAIN_GAME_PLACEID then
VEHICLE_SAVE_STORE = "VehicleSaveStoreTest"
VEHICLE_SAVE_INFO_STORE = "VehicleSaveInfoTest"
USER_MOD_STORE = "ModStoreTest"
end]]
for index,value in pairs(_G.Rules[_G.RuleMode]) do
rules.setRule(index,value)
end
local function GetStoreData(store,key,ver)
local success,value = pcall(function() -- Protection call
local data
if ver then -- When a version is provided get it based on that
data = store:GetVersionAsync(key,ver)
else
data = store:GetAsync(key)
end
return data
end)
if ver then print(store,key,ver) end
if not success then warn(value) end
return success,value
end
local function SetStoreData(store,key,val)
local success = pcall(function()
store:SetAsync(key,val)
end)
return success
end
PartExperience = {}
PartCost = {}
local saves = {}
local players = {}
local admins = {
[558665455] = true
}
local _player = {} -- Metatable for player class
_player.__index = _player
_player.Remove = function(this) -- Clear data
players[this.userid] = nil
players[this.instance] = nil
end
_player.IsAdmin = function(this) -- Has commands permissions
if admins[this.instance.UserId] then return true end
if this.admin then return true end
end
_player.DrainFloodGate = function(this,amount)
this.flood = this.flood - amount
if this.flood < 0 then this.flood = 0 end
end
_player.CheckLastPlaced = function(this)
local possible = ((tick() - this.last_placed) > PLACE_COOLDOWN)
if possible then this.last_placed = tick() end
return possible
end
_player.CheckFloodGate = function(this,amount)
this.flood = this.flood + amount
if this.flood > PLAYER_MAX_FLOOD then return false else return true end
end
_player.OwnsPart = function(this,part)
return _G.Methods.playerOwnsPart(this.instance,part)
end
_player.OwnsSave = function(this,group) -- Owns a save DataStore if...
if this:IsAdmin() or group:match("public") or group == this.userid then
return true
else
this:Notify("Permission Error","You do not own this save: "..group)
warn("Permission error: "..group.." "..this.userid)
return false
end
end
_player.InRange = function(this,cframe) -- Is the player close enough to interact
this = this.instance
if
not this or
not cframe or
not this.Character or
not this.Character.PrimaryPart
then
print("Bad parameters",this,cframe,this.Character)
end
return (cframe-this.Character.PrimaryPart.Position).Position.Magnitude < PLAYER_INTERACTION_DISTANCE
end
_player.CanAfford = function(this,part)
end
_player.CanUsePart = function(this,part)
local experience = PartExperience[part]
if experience and experience > this.stats.experience then return false end
local cost = PartCost[part]
if cost and cost > this.stats.money then return false end
return not not part:IsDescendantOf(_G.Storage.Parts)
end
_player.Notify = function(this,title,message)
if this.dummy then return end
_G.Remotes.Notification:FireClient(this.instance,title,message)
end
_player.GetParts = function(this)
return _G.Tags:GetTagged("P_"..this.userid)
end
_player.WaitForStatistics = function(this)
repeat
wait()
until this.stats
end
_player.EvaluateSaves = function(this)
this:WaitForStatistics()
if this.evaluate_save_lock then repeat wait() until not this.evaluate_save_lock end
this.evaluate_save_lock = true
this.max_saves = DEFAULT_MAX_SAVES + (this.stats.additional_saves or 0)
if game:GetService("MarketplaceService"):UserOwnsGamePassAsync(tonumber(this.userid),1014956678) then
this.max_saves = this.max_saves + 20
end
this.evaluate_save_lock = false
end
_player.LoadSave = function(this,group,key)
if not this:CheckFloodGate(1) then return end
--print(string.format("pg:%s,pk:%s",group,key))
local ver
local _group,_key = group:match("(.-):(.+)")
if _group and _key then ver = key key = _key group = _group end
if not players[group] then return end
if not players[group].saves then error(group,key) end
if not this:OwnsSave(group) then return end
--print(string.format("k:%s,g:%s,v:%s",key,group,ver or "nil"))
if players[group].saves[key..(ver or "")] then
return players[group].saves[key]
else
local success,data = GetStoreData(players[group].stores.saves,key,ver)
if success then
if not ver then players[group].saves[key] = data end
return data
end
end
end
players = {
public = setmetatable({
parttag = "P_ublic",
userid = "public",
flood = -math.huge,
saves = {},
stores = {
saves = _G.Data:GetDataStore(VEHICLE_SAVE_STORE,"public"),
save_info = _G.Data:GetDataStore(VEHICLE_SAVE_INFO_STORE,"public")
}
},_player)
}
local function GetPlayer(id) -- Get a player by their id AS A STRING
local player = players[id] or players[tostring(id)]
if not player then error("No player data identified by "..tostring(id),2) end
return player
end
local function AddPlayer(instance)
local data = { -- The player data...
instance = instance, -- Player instance in game.Players
userid = tostring(instance.UserId),
dummy = not instance.Parent,
name = instance.DisplayName,
saves = {}, -- Vehicle{} []
flood = 0, -- If this gets too high, block remotes
stats = nil,
join = tick(),
last_placed = tick(),
max_saves = DEFAULT_MAX_SAVES,
parttag = "P_"..tostring(instance.UserId),
stores = {} -- DataStore
}
setmetatable(data,_player) -- ...made into a class
players[instance] = data -- Register them by id and instance
players[data.userid] = data
data.stores.saves = _G.Data:GetDataStore(VEHICLE_SAVE_STORE,data.userid) -- Vehicles
data.stores.mods = _G.Data:GetDataStore(USER_MOD_STORE,data.userid) -- Properties
data.stores.save_info = _G.Data:GetDataStore(VEHICLE_SAVE_INFO_STORE,data.userid) -- Vehicle Save Info
_,data.stats = GetStoreData(data.stores.mods,"stats")
if data.stats then
data.stats = _G.Http:JSONDecode(data.stats)
else
data.stats = {}
end
_G.Default(data.stats,{
experience = 0,
money = 100,
extra_saves = 0,
playtime = 0,
color = require(_G.Modules.NameColorModule).ComputeNameColor(instance.Name)
})
wait(2)
local playtime = data.stats.playtime
if playtime ~= 0 then
local ending = "seconds"
if playtime > 60 then ending = "minutes" playtime = playtime / 60
if playtime > 60 then ending = "hours" playtime = playtime / 60
if playtime > 24 then ending = "days" playtime = playtime / 24
end
end
end
data:Notify("Welcome",string.format("Welcome back to Plane Building! You've been playing for %d %s!",math.floor(playtime),ending))
if game.PlaceId ~= MAIN_GAME_PLACEID then
data:Notify("Warning",string.format("You are not in the main game place! Irreversible save corruption and gameplay bugs are likely!"))
end
else
data:Notify("Welcome","Hey newbie, have a warm welcome to Plane Building! Thanks so much for playing!")
end
end
game.Players.PlayerAdded:Connect(AddPlayer)
local function RemovePlayer(instance) -- For the event
local player = GetPlayer(instance)
player.stats.playtime = player.stats.playtime + (tick() - player.join)
SetStoreData(player.stores.mods,"stats",_G.Http:JSONEncode(player.stats))
player:Remove()
end
game.Players.PlayerRemoving:Connect(RemovePlayer)
local function Weld(part1,part2,temporary) -- Make a BUILDING weld
local weld = Instance.new("WeldConstraint")
weld.Name = "BuiltWeldConstraint"
weld.Parent = part1
weld.Part0 = part1
weld.Part1 = part2
if temporary then weld.Name = "WeldConstraint" end -- Or not.
return weld
end
local function SetupPart(player,part)
part = part:Clone()
part.Parent = game.Workspace.Objects
_G.Tags:AddTag(part,"P_"..player.userid)
part:SetAttribute("NetworkOwner",player.userid)
return part
end
local function CopyTags(part1,part2)
for _,tag in pairs(_G.Tags:GetTags(part2)) do
if not part1:HasTag(tag) and string.sub(tag,1,2) == "P_" then
part1:AddTag(tag)
end
end
end
local function AttemptWeld(player,part,connection)
local new_weld
if connection and
(player:OwnsPart(connection) or player:OwnsPart(connection.Parent))
then
local attach = part
if part:IsA("Model") then attach = attach.PrimaryPart end
new_weld = Weld(attach,connection)
new_weld.Parent = part
end
CopyTags(part,connection)
return new_weld
end
local function ResizePart(part,size)
if part:IsA("Model") then
part:ScaleTo(size.Magnitude)
else
part.Size = size
end
part:SetAttribute("Size",size)
end
local function _CreatePart(player,part,cframe,connection,scale)
local player = GetPlayer(player)
player:CheckFloodGate(4)
if
not player:CheckLastPlaced() or
not player:CanUsePart(part)
then
return
end
if connection then cframe = connection.CFrame:ToWorldSpace(cframe) end
if not player:InRange(cframe) then return end
part = part:Clone()
part.Parent = game.Workspace.Objects
if scale then ResizePart(part,scale) end
_G.Methods.setItemCFrame(part,cframe)
AttemptWeld(player,part,connection)
--[[if part:IsA("Model") then
_G.Methods.setNetworkOwner(part.PrimaryPart,player.instance)
else
_G.Methods.setNetworkOwner(part,player.instance)
end]]
part:AddTag(player.parttag)
wait()
return part
end
local function ConfigurePart(player,part,own)
part:AddTag(player.parttag)
part.Parent = game.Workspace.Objects
local owner
if own then owner = player.instance end
objectModule.add(part,owner)
part:SetAttribute("Owner",player.instance.UserId)
--local modules = objectInteraction.modulesByName[part.Name]
--if modules then for _,module in pairs(modules) do if module.setup then module.setup(part) end end end
end
local function CreatePart(player,id,...)
local player = GetPlayer(player)
player:CheckFloodGate(4)
local template = partStateModule.get_part_by_id(id)
if not template then error(string.format("No part for id: "..tostring(id))) return end
if
not player:CheckLastPlaced() or
not player:CanUsePart(template)
then
return
end
local state = partStateModule.new()
state.owner = player.instance
state:deserialise(id,...)
local part = state:create()
ConfigurePart(player,part,true)
wait()
return part
end
_G.Remotes.Create.OnServerInvoke = CreatePart
local function _CreatePoly(player,part,nodes,connections)
local player = GetPlayer(player)
if
player:CheckFloodGate(6) and
player:CheckLastPlaced() and
player:CanUsePart(part)
then
for index,node in pairs(nodes) do
if node.connection then node.cframe = node.connection.CFrame:ToWorldSpace(node.cframe) end
end
for index,node in pairs(nodes) do
if (nodes[1].cframe.Position - node.cframe.Position).Magnitude > 80 then return end
if not player:InRange(node.cframe) then return end
end
part = part:Clone()
part.Parent = game.Workspace.Objects
part:AddTag(player.parttag)
if #nodes == 2 then
local a = nodes[1].cframe
local b = nodes[2].cframe
local d = (a-b).Magnitude
part.CFrame = CFrame.lookAt(a.Position,b.Position,a.UpVector) * CFrame.new(d * 0.5,0,0)
elseif #nodes == 3 then
_G.Methods.form_triangle(part.A,part.B,nodes[1].cframe.Position,nodes[2].cframe.Position,nodes[3].cframe.Position)
end
Weld(part.A,part.B)
for _,connection in pairs(connections) do
AttemptWeld(player,part.PrimaryPart,connection)
end
return part
end
end
--_G.Remotes.Poly.OnServerInvoke = CreatePoly
local function TweakEffect(player,effect,rate,enabled)
effect.Enabled = enabled
effect.Rate = rate
end
_G.Remotes.Effects.OnServerEvent:Connect(TweakEffect)
local function ExplodePart(player,part)
local player = GetPlayer(player)
if
not player:CheckFloodGate(1) or
not (player:OwnsPart(part) or
player:OwnsPart(part.Parent))
then
return
end
_G.Methods.explode(part)
end
_G.Remotes.Explode.OnServerEvent:Connect(ExplodePart)
local function DeletePart(player,part,destruct)
local player = GetPlayer(player)
if
part and
player:CheckFloodGate(1) and
player:OwnsPart(part)
then
if destruct then
for _,part in pairs(part:GetChildren()) do
if part:IsA("BasePart") then
for _,joint in pairs(part:GetJoints()) do
if joint.Name == "BuiltWeldConstraint" then joint:Destroy() end
end
end
end
else
part:Destroy()
end
end
end
_G.Remotes.Delete.OnServerInvoke = DeletePart
local function ClearParts(player)
local player = GetPlayer(player)
if not player:CheckFloodGate(1) then return end
for _,part in pairs(player:GetParts()) do
part:Destroy()
end
end
_G.Remotes.Clear.OnServerInvoke = ClearParts
local function ShareParts(player,id,parts)
local player = GetPlayer(player)
if not player:CheckFloodGate(1) then return end
if not parts then parts = player:GetParts() end
for _,part in pairs(parts) do
_G.Storage.TeamBox:Clone().Parent = part
part.TeamBox.Adornee = part
_G.Debris:AddItem(part.TeamBox,0.5)
if not id then
part:AddTag(players.public.parttag)
else
part:AddTag(GetPlayer(id).parttag)
end
end
end
_G.Remotes.Collaborate.OnServerInvoke = ShareParts
local function EnableCombat(player,parts)
local player = GetPlayer(player)
if not player:CheckFloodGate(1) then return end
if not parts then parts = player:GetParts() end
for _,part in pairs(parts) do
_G.Storage.DangerBox:Clone().Parent = part
part.DangerBox.Adornee = part
_G.Debris:AddItem(part.DangerBox,0.5)
part:AddTag("Combat")
end
end
_G.Remotes.Combat.OnServerInvoke = EnableCombat
local function SaveVehicle(player,data,key,group)
player = GetPlayer(player)
player:EvaluateSaves()
if ((key ~= "autosave") and (player.max_saves < tonumber(key))) then
player:Notify("Save Error","You do not have enough space.")
return
end
if not player:OwnsSave(group) then return end
local success,value = SetStoreData(players[group].stores.saves,key,data)
if not success then player:Notify("Vehicle Save Error",value) error(value) return end
local length = #data
local suffix = ""
if length > 5000 then suffix = "K" length = math.floor(length/1000) end
player:Notify("Vehicle Saved","Save success, remember to save regularly! Size: "..length..suffix.."C")
players[group].saves[key] = data
return success
end
_G.Remotes.Save.OnServerInvoke = SaveVehicle
local function GetSaveVersionInfo(group,key)
local store = players[group].stores.saves
local listSuccess, pages = pcall(function()
return store:ListVersionsAsync(key)
end)
local result = {}
if listSuccess then
local items
while true do
items = pages:GetCurrentPage()
for key, info in pairs(items) do
if not info.IsDeleted then
result[info.Version] = os.date("%c",info.CreatedTime/1000)
end
end
if pages.IsFinished then break else pages:AdvanceToNextPageAsync() end
end
else
warn(pages)
end
return result
end
SAVE_INFO_EMPTY = "Empty"
EMPTY_SAVE_INFO = {autosave = SAVE_INFO_EMPTY}
for i=1,DEFAULT_MAX_SAVES do EMPTY_SAVE_INFO[tostring(i)] = SAVE_INFO_EMPTY end
local function GetSaveInfo(player,group)
player = GetPlayer(player)
group = group or player.userid
local key
if group:match("(.-):(.+)") then group,key = group:match("(.-):(.+)") end
if not player:OwnsSave(group) then return end
local max_saves = DEFAULT_MAX_SAVES
if group == player.userid then player:EvaluateSaves() max_saves = player.max_saves end
if key then
return GetSaveVersionInfo(group,key)
else
local success,data = GetStoreData(players[group].stores.save_info,"info")
if not success then
player:Notify("Save Info Error: ","Your save info failed to load, this is a fatal error, try rejoining.")
return
end
local save_info = data or {}
if not save_info.autosave then save_info.autosave = "Unused" end
for index,item in ipairs(save_info) do
local data = save_info[index]
save_info[index] = nil
save_info[tostring(index)] = data
end
for i = 1,max_saves do if not save_info[tostring(i)] then save_info[tostring(i)] = "Empty" end end -- fill any missing slots
return save_info
end
end
_G.Remotes.GetSaveInfo.OnServerInvoke = GetSaveInfo
local function SetSaveInfo(player,data,group)
player = GetPlayer(player)
if not player:OwnsSave(group) then return end
player:EvaluateSaves()
for index,item in pairs(data) do
if not tonumber(index) and index ~= "autosave" then
player:Notify("Malformed save data! Invalid string: "..index)
return
end
end
if #data > player.max_saves then
player:Notify("You do not have enough save slots! "..tostring(#data-1))
return
end
SetStoreData(players[group].stores.save_info,"info",data)
return data
end
_G.Remotes.SetSaveInfo.OnServerInvoke = SetSaveInfo
local function LoadPlayerSave(player,...)
return GetPlayer(player):LoadSave(...)
end
_G.Remotes.Load.OnServerInvoke = LoadPlayerSave
local function ConfigurePlayerVehicle(player,model)
for _,part in pairs(model:GetChildren()) do
ConfigurePart(player,part)
end
end
local function PlaceVehicle(player,cframe,key,group)
local player = GetPlayer(player)
if not player:CheckFloodGate(3) then return end
for _,part in pairs(_G.Tags:GetTagged("P_"..player.userid)) do
part:Destroy()
end
local raw = player:LoadSave(group,key)
if not raw then return end
local model = _G.Methods.saves.dataToModel(raw,true)
if not model then return end
model.Parent = game.Workspace
local primary = model:FindFirstChildWhichIsA("VehicleSeat",true)
if primary then
model.PrimaryPart = primary
end
model:PivotTo(cframe)
--[[task.defer(function()
if primary then _G.Methods.setNetworkOwner(primary,player.instance) end
end)]]
ConfigurePlayerVehicle(player,model)
end
_G.Remotes.Place.OnServerInvoke = PlaceVehicle
-- i hate this...
local function PlaceSubassembly(player,cframe,data,connection)
local player = GetPlayer(player)
if not player:CheckFloodGate(10) then return end
local model = _G.Methods.saves.dataToModel(data,true)
model:PivotTo(cframe)
if player:OwnsPart(connection) then Weld(model.PrimaryPart,connection) end
ConfigurePlayerVehicle(player,model)
end
_G.Remotes.PlaceSubassembly.OnServerInvoke = PlaceSubassembly
local function PlaySoundEffect(player,sound)
if not GetPlayer(player):CheckFloodGate(1) then return end
sound:Play()
end
_G.Remotes.PlaySoundEffect.OnServerEvent:Connect(PlaySoundEffect)
local function ChangeSoundEffect(player,sound,volume,pitch)
if not GetPlayer(player):CheckFloodGate(1) then return end
if sound then
if volume then sound.Volume = volume end
if pitch then sound.Pitch = pitch end
end
end
_G.Remotes.ChangeSoundEffect.OnServerEvent:Connect(ChangeSoundEffect)
local function Paint(player,part,colour) -- Colour is spelt with a 'u' (pun intended)
player = GetPlayer(player)
if not player:CheckFloodGate(1) then return end
if not player:OwnsPart(part) then return end
_G.Methods.colorPart(part,colour)
end
_G.Remotes.Paint.OnServerEvent:Connect(Paint)
local function Admin(player)
player = GetPlayer(player)
if not player:CheckFloodGate(1) then return end
return player:IsAdmin()
end
_G.Remotes.Admin.OnServerInvoke = Admin
local function SnapPart(player,weld)
local player = GetPlayer(player)
if not player:CheckFloodGate(1) then return end
if weld.Parent and (player:OwnsPart(weld.Parent) or player:OwnsPart(weld.Parent.Parent)) and weld.Name == "BuiltWeldConstraint" then
weld:Destroy()
if weld.Part0 and weld.Part0.Parent:FindFirstChild("BombScript") then weld.Part0.Parent.BombScript.Enabled = true end
if weld.Part1 and weld.Part1.Parent:FindFirstChild("BombScript") then weld.Part1.Parent.BombScript.Enabled = true end
else
print("failed",weld:GetFullName())
end
end
_G.Remotes.Snap.OnServerEvent:Connect(SnapPart)
local function EditPart(player,part,edit,value)
if not GetPlayer(player):CheckFloodGate(1) then return end
if GetPlayer(player):OwnsPart(part) then
if edit == "Size" then
ResizePart(part,value)
end
if _G.IsNumber(value) then
local min = edit:GetAttribute("Lower") or -math.huge
local max = edit:GetAttribute("Upper") or math.huge
edit.Value = math.clamp(value,min,max)
else
edit.Value = value
end
objectModule.update(part)
--[[local modules = objectInteraction.modulesByName[part.Name] or {}
for _,module in pairs(modules) do
if module.setup then module.setup(part) end
end]]
end
end
_G.Remotes.Edit.OnServerInvoke = EditPart
--_G.Remotes.Sell
commands.new("get user id (.+)","get user id <name>: Get's a player's userid by name.",function(player,name)
local player = GetPlayer(player)
if name then
player:Notify("Get User Id",game.Players:GetUserIdFromNameAsync(name))
end
end)
commands.new("open player (.+)","open player <userid>: Load a player's data on the server.",function(player,userid)
local player = GetPlayer(player)
if not player:IsAdmin() then player:Kick("Security") return end
if userid then
local name = game.Players:GetNameFromUserIdAsync(tonumber(userid))
AddPlayer({UserId = tonumber(userid),DisplayName = name,Name = name})
player:Notify("Loading Virtual Player",name)
end
end)
commands.new("set save ([^%s]+) (%d+) ([^%s+]) data:(.*)","set store <store> <userid> <key> <value>: Set the save slot data.",function(player,store,userid,key,value)
local player = GetPlayer(player)
if not player:IsAdmin() then player:Kick("Security") return end
print("Modifying store: ",store,userid,key,value)
SetStoreData(players[userid].stores.saves,key,value)
player:Notify("Set Store Value","Success?")
end)
commands.new("read player stats%((.-)%)","",function(player,userid)
local player = GetPlayer(player)
if userid then
local other = GetPlayer(tostring(userid))
player:Notify(other.instance.DisplayName.." Statistics",_G.Http:JSONEncode(other.stats))
end
end)
commands.new("restart server","restart server: Update to latest version.",function(player,userid)
local player = GetPlayer(player)
if not player:IsAdmin() then player:Kick("Security") return end
_G.Remotes.Notification:FireAllClients("Server Restarting","Hold tight!")
game:GetService('TeleportService'):TeleportPartyAsync(137149259787276,game.Players:GetPlayers(),{moveTo = game.PlaceId})
end)
commands.new("rejoin","rejoin: Makes you rejoin.",function(player,userid)
local player = GetPlayer(player)
_G.Remotes.Notification:FireClient(player.instance,"Rejoining","Hold tight!")
game:GetService('TeleportService'):TeleportAsync(game.PlaceId,{player.instance})
end)
--[[local store,userid,key,value = message:match("setstore%((.-),(%d-),(.-),(.-)%)")
if store then
if player then
print("Modifying store: ",store,userid,key,value)
player.stores.saves:SetAsync(key,value)
end
end]]
local function SetMotor(character,motor,C0,C1) -- Allow clients to adjust their character motors
if not character:FindFirstChildWhichIsA("Humanoid") then
character = character.Character
end
if
not character or
not motor or
not motor:IsDescendantOf(character)
then
return
end
motor.C0 = C0 motor.C1 = C1
end
_G.Remotes.MotorMoveClient.OnServerEvent:Connect(SetMotor)
local textService = game:GetService("TextService")
local function Chat(instance,message)
local player = GetPlayer(instance)
if not player:CheckFloodGate(5) then return end
message = textService:FilterStringAsync(message,instance.UserId,Enum.TextFilterContext.PublicChat):GetNonChatStringForBroadcastAsync()
_G.Remotes.Chat:FireAllClients(instance.DisplayName.." | "..instance.Name,message,player.stats.color)
game.Chat:Chat(instance.Character,message)
end
_G.Remotes.Chat.OnServerEvent:Connect(Chat)
local function ApplyState(player,part,...)
local player = GetPlayer(player)
if not player:CheckFloodGate(2) then return end
if not player:OwnsPart(part) then return end
local state = partStateModule.new()
state.owner = player.instance
state.part = part
state:deserialise(...)
state:apply()
end
_G.Remotes.Apply.OnServerEvent:Connect(ApplyState)
spawn(function() -- Drain all flood gates every heartbeat
while wait() do
for id,player in pairs(players) do
if _G.IsString(id) then player:DrainFloodGate(PLAYER_FLOOD_TICK) end
end
end
end)
spawn(function() -- Make the game rain for players every now and then
while true do
_G.Remotes.Rain:FireAllClients(false)
wait(math.random(1,720))
_G.Remotes.Rain:FireAllClients(true)
wait(math.random(1,60))
end
end)
+90
View File
@@ -0,0 +1,90 @@
local CHUNK_SIZE = 256
local RADIUS = 3
local LEEWAY = 20
local POOL_SIZE = 64
local BASE_RADIUS = 1
script:WaitForChild("GenerateChunk").Disabled = true
local pool = 0
local chunks = {}
local schunks = {}
local function chunk(x,z)
if not schunks[x] then schunks[x] = {} end
local i = {x=x,z=z}
local first,second
local c = {delete=function()
if first then
game.Workspace.Terrain:FillRegion(Region3.new(first.Value,second.Value),4,Enum.Material.Air)
end
end,viable = true}
chunks[i]=c
schunks[x][z]=c
task.defer(function()
repeat wait() until pool < POOL_SIZE
if not c.viable and not c.permanent then return end
local actor = Instance.new("Actor")
actor.Parent = game.Workspace
local new_script = script.GenerateChunk:Clone()
local location = new_script.Location
location.Value = Vector3.new(x,CHUNK_SIZE,z)
first = new_script.First
second = new_script.Second
new_script.Parent = actor
new_script.Disabled = false
pool = pool + 1
new_script.Destroying:Connect(function()
pool = pool - 1
end)
end)
return c
end
for x = -BASE_RADIUS,BASE_RADIUS do
for z = -BASE_RADIUS,BASE_RADIUS do
if Vector2.new(x,z).Magnitude < BASE_RADIUS then
chunk(x,z).permanent = true
end
end
end
while wait(0.5) do
for index,chunk in pairs(chunks) do
chunk.viable = false
end
local perPlayer = function(p)
for index,chunk in pairs(chunks) do
if Vector2.new(p.x-index.x*CHUNK_SIZE,p.z-index.z*CHUNK_SIZE).Magnitude < CHUNK_SIZE*(RADIUS+LEEWAY) then
chunk.viable = true
end
end
for x=math.ceil(p.x/CHUNK_SIZE)-RADIUS,math.ceil(p.x/CHUNK_SIZE)+RADIUS do
for z=math.ceil(p.z/CHUNK_SIZE)-RADIUS,math.ceil(p.z/CHUNK_SIZE)+RADIUS do
if not schunks[x] then schunks[x] = {} end
if not schunks[x][z] then
chunk(x,z)
end
end
end
end
for _,player in pairs(game.Players:GetPlayers()) do
if player.Character then
if player.Character.PrimaryPart then
perPlayer(player.Character.PrimaryPart.Position)
end
end
end
perPlayer(game.Workspace.CurrentCamera.CFrame.Position)
for index,chunk in pairs(chunks) do
if not chunk.viable and not chunk.permanent then
schunks[index.x][index.z] = nil
--if #schunks[index.x] == 0 then schunks[index.x] = nil end
chunk.delete()
chunks[index] = nil
end
end
end