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