Copy salient and essential scripts and models from Plane Building Roblox
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Vehicle // ?!
|
||||||
|
Command
|
||||||
|
Create
|
||||||
|
Delete
|
||||||
|
Notification
|
||||||
|
Pick
|
||||||
|
Place
|
||||||
|
Save
|
||||||
|
Saved
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
require(script.Parent.DefaultGlobals)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
require(script.Parent.DefaultGlobals)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
require(game.ReplicatedFirst.ReadyModule)
|
||||||
|
require(_G.Modules.PlacementModule).init(script.Parent)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
require(game.ReplicatedFirst.ReadyModule)
|
||||||
|
require(_G.Modules.SaveModule).InitUI(script)
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Most things are not relative to the original hierarchy. I'm just dumping everything here.
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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}
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
@@ -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}
|
||||||
@@ -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}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
@@ -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
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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 {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
local module = {}
|
||||||
|
|
||||||
|
return module
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
require(game.ReplicatedFirst.ReadyModule)("GenerationInsurance")
|
||||||
|
if not _G.IsStudio then script.Parent.GenerationForeman.Enabled = true end
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user