Copy salient and essential scripts and models from Plane Building Roblox

This commit is contained in:
2026-06-10 02:53:40 +01:00
commit 891781366d
76 changed files with 10904 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
if _G.IsClient then
local module = {}
function module.fire(barrel)
spawn(function()
local bullet = _G.Remotes.Shoot:InvokeServer(barrel)
bullet = game.Workspace:WaitForChild(bullet)
if not bullet then return end
bullet.CFrame = barrel.CFrame * CFrame.Angles(0,math.rad(-90),0)
bullet.Velocity = bullet.CFrame.LookVector * 1000
--bullet.BodyVelocity.Velocity = bullet.Velocity
end)
end
return module
elseif _G.IsServer then
-- SERVER-SIDE
function touched(touched,team,player,bullet,barrel)
if not touched or not touched.Parent then return end
local humanoid = touched.Parent:FindFirstChildWhichIsA("Humanoid")
if humanoid then
if humanoid.Parent:FindFirstChild("Team") and humanoid.Parent.Team.Value == game.Teams.Loon then
humanoid.Health = humanoid.Health - 25
bullet:Destroy()
end
end
local object
local part
if touched.Parent:IsA("Model") and touched.Parent:HasTag("Combat") then
object = touched.Parent
part = object.PrimaryPart
else
part = touched
object = touched
end
if object:HasTag("Combat") then
if not object:HasTag("P_"..player.UserId) then
if not object:GetAttribute("Health") then
local max = part.Size.Magnitude/1.5
if object:IsA("Model") then max = object:GetExtentsSize().Magnitude/1.5 end
object:SetAttribute("Health",max)
object:SetAttribute("MaxHealth",max)
end
local health = object:GetAttribute("Health")
local max = object:GetAttribute("MaxHealth")
health = health - 3
--part.Transparency = 1-(health/max)
if barrel.BlastPressure.Value ~= -1 then
local explosion = Instance.new("Explosion")
explosion.BlastPressure = barrel.BlastPressure.Value
explosion.DestroyJointRadiusPercent = barrel.Destruction.Value
explosion.Parent = part
explosion.Position = part.Position
end
object:SetAttribute("Health",health)
if health < 0 then
_G.Methods.break_part(object,player)
object.Parent = game.Workspace.Hidden -- stop busted mechanics please
end
bullet:Destroy()
end
end
end
function fire(barrel,team,player)
if not barrel then return end
local bullet = _G.Storage[barrel.Parent.Name.."Bullet"]:Clone()
delay(1.8,function() if bullet and bullet.Parent then bullet:Destroy() end end)
bullet.CFrame = CFrame.new(0,1000000000,0)
bullet.Parent = game.Workspace
local connection = bullet.Touched:Connect(function(part)
touched(part,team,player,bullet,barrel)
end)
bullet.CFrame = barrel.CFrame * CFrame.Angles(0,math.rad(-90),0)
bullet.Velocity = bullet.CFrame.LookVector * 1000
bullet.Anchored = false
bullet:SetAttribute("Team",team.Name)
if player then
bullet:SetNetworkOwner(player) --[[delay(0,function() bullet:SetNetworkOwnershipAuto() end)]]
bullet:SetAttribute("Owner",player.UserId)
end
bullet:FindFirstChildWhichIsA("Sound"):Play()
bullet.Name = bullet.Name .. tostring(math.random(1,1000000))
return bullet.Name
end
_G.Remotes.Shoot.OnServerInvoke = function(player,barrel) return fire(barrel,player.Team,player) end
return getfenv()
end
+153
View File
@@ -0,0 +1,153 @@
motors = {}
modes = {}
cframes = {}
targets = {}
c0s = {}
c1s = {}
torso = nil
head = nil
model = nil
humanoid = nil
running = false
cameraOffset = Vector3.new(0,0,0)
run = game:GetService("RunService")
input = game:GetService("UserInputService")
motorsEnabled = true
function getMotor(name)
motors[name] = torso:WaitForChild(name)
cframes[name] = CFrame.new(0,0,0) -- need to change this to a {} for each motor, not globals
targets[name] = CFrame.new(0,0,0)
c0s[name] = motors[name].C0
c1s[name] = motors[name].C1
return motors[name]
end
function check(...)
for _,item in pairs({...}) do
if not item or not item.Parent then return false end
end
return true
end
local alpha = 0.1 * (1/60)
function setArm(name,delta)
if not delta then delta = 1/60 end
if not check(model,head,torso,motors[name]) or motorsEnabled or humanoid.SeatPart then return end
local origin = (game.Workspace.CurrentCamera.CFrame.Rotation + head.Position)
if modes[name] ~= 1 then cframes[name] = origin:ToObjectSpace(motors[name].Part1.CFrame) * CFrame.Angles(-1.57,0,0) end
modes[name] = 1
cframes[name] = cframes[name]:Lerp(targets[name],alpha/delta)
motors[name].Part1.CFrame = origin:ToWorldSpace(cframes[name] * CFrame.Angles(1.57,0,0))
end
function restArm(name,delta)
if not delta then delta = 1/60 end
if not check(model,head,torso,motors[name]) or motorsEnabled or humanoid.SeatPart then return end
local origin = (torso.CFrame)
if modes[name] ~= 2 then cframes[name] = origin:ToObjectSpace(motors[name].Part1.CFrame) * CFrame.Angles(-1.57,0,0) end
modes[name] = 2
cframes[name] = cframes[name]:Lerp(targets[name],alpha/delta)
motors[name].Part1.CFrame = origin:ToWorldSpace(cframes[name] * CFrame.Angles(1.57,0,0))
end
function resetMotors()
for name,motor in pairs(motors) do
if motor and motor.Parent then
motor.C0 = c0s[name]
motor.C1 = c1s[name]
end
end
end
function rotateCharacter()
if not model or not model.Parent or not model.PrimaryPart or not humanoid then return end
local state = humanoid:GetState()
local statetype = Enum.HumanoidStateType
if state == statetype.Ragdoll or state == statetype.FallingDown or state == statetype.GettingUp then return end
model.HumanoidRootPart.CFrame =
CFrame.new(model.HumanoidRootPart.Position,
model.HumanoidRootPart.Position
+ Vector3.new(
game.Workspace.CurrentCamera.CFrame.LookVector.X,0,
game.Workspace.CurrentCamera.CFrame.LookVector.Z))
end
function lookArm(name,position)
end
function rotateHead()
if not motors["Neck"] or not torso then return end
local target = torso.CFrame:ToObjectSpace(game.Workspace.Camera.CFrame).LookVector
motors["Neck"].C0 = CFrame.new(0, 1, 0, -1, 0, 0, 0, 0, 1, 0, 1, -0) * CFrame.Angles(math.acos(target.Y)+math.rad(-105),0,math.acos(target.X)+math.rad(-90))
end
function disableMotors()
if not motorsEnabled then return end
motorsEnabled = false
getMotor("Left Shoulder").Enabled = false
local leftArm = motors["Left Shoulder"].Part1
leftArm.CanCollide = false
leftArm.Anchored = true
getMotor("Right Shoulder").Enabled = false
local rightArm = motors["Right Shoulder"].Part1
rightArm.CanCollide = false
rightArm.Anchored = true
end
function enableMotors()
if motorsEnabled then return end
motorsEnabled = true
motors["Left Shoulder"].Enabled = true
local leftArm = motors["Left Shoulder"].Part1
leftArm.CanCollide = true
leftArm.Anchored = false
motors["Right Shoulder"].Enabled = true
local rightArm = motors["Right Shoulder"].Part1
rightArm.CanCollide = true
rightArm.Anchored = false
end
function setRunning(value)
if value then
running = true
humanoid.WalkSpeed = 30
else
running = false
humanoid.WalkSpeed = 15
end
end
run:BindToRenderStep("CharacterMotorReset",1,function() resetMotors() rotateHead()
game.Players.LocalPlayer.CameraMode = Enum.CameraMode.Classic
if humanoid then humanoid.CameraOffset = cameraOffset end cameraOffset = Vector3.new(0,0,0)
end)
run:BindToRenderStep("CharacterHeadRotate",Enum.RenderPriority.Last.Value+1,function()
rotateHead()
end)
function init(character)
torso = character:WaitForChild("Torso")
head = character:WaitForChild("Head")
humanoid = character:WaitForChild("Humanoid")
humanoid.BreakJointsOnDeath = false
motors = {}
model = character
enableMotors()
getMotor("Neck").Enabled = true
getMotor("Left Shoulder").Enabled = true
getMotor("Right Shoulder").Enabled = true
spawn(function() while wait(.1) do
local event = _G.Remotes.MotorMoveClient
event:FireServer(motors["Neck"],motors["Neck"].C0,motors["Neck"].C1)
event:FireServer(motors["Left Shoulder"],motors["Left Shoulder"].C0,motors["Left Shoulder"].C1)
event:FireServer(motors["Right Shoulder"],motors["Right Shoulder"].C0,motors["Right Shoulder"].C1)
end end)
input.InputBegan:Connect(function(object,irrelevant)
if not irrelevant then
if object.KeyCode == Enum.KeyCode.LeftShift or object.KeyCode == Enum.KeyCode.ButtonL1 then
humanoid.WalkSpeed = 30
running = true
end
end
end)
input.InputEnded:Connect(function(object,irrelevant)
if not irrelevant then
if object.KeyCode == Enum.KeyCode.LeftShift or object.KeyCode == Enum.KeyCode.ButtonL1 then
humanoid.WalkSpeed = 15
running = false
end
end
end)
game.Workspace.CurrentCamera.CameraSubject = humanoid
end
return getfenv()
+54
View File
@@ -0,0 +1,54 @@
require(game.ReplicatedFirst.ReadyModule)("CommandsModule")
local help = ""
local commands = {}
function invoke(input,player)
local found = false
for index,command in ipairs(commands) do
local result = table.pack(input:match(command.pattern))
if result[1] then
if player then
table.insert(result,1,player)
end
found = true
command.callback(table.unpack(result))
end
end
return found
end
function new(pattern,documentation,callback)
help = help .. documentation .. "\n"
table.insert(commands,{
pattern = pattern,
documentation = documentation,
callback = callback
})
if _G.IsServer then _G.Storage:WaitForChild("CommandsHelp").Value = help end
end
_G.Events.Command.Event:Connect(function(command)
local cFound = invoke(command)
local sFound = _G.Remotes.Command:InvokeServer(command)
if not cFound and not sFound then _G.Events.Notification:Fire("Command Not Found",[[
If you're struggling to type in a command, note that they use pattern matching.
"<userid>" means you should literally type "12345678", no letters or dots.
Check your spelling or type 'help' for some commands.
]]) end
end)
if _G.IsServer then
_G.Remotes.Command.OnServerInvoke = function(player,command)
return invoke(command,player)
end
end
local notification
new("help","help: displays this message",function()
local final = "Client:\n"..help
if _G.IsClient then final = final.."\nServer:\n".._G.Storage.CommandsHelp.Value end
_G.Events.Notification:Fire("Command Help",final)
end)
return getfenv()
+41
View File
@@ -0,0 +1,41 @@
local module = {}
local function dragger(object,callback,root)
local finger
object.InputBegan:Connect(function(input)
if finger then return end
if root then
local objects = game.Players.LocalPlayer.PlayerGui:GetGuiObjectsAtPosition(input.Position.X, input.Position.Y)
if #objects == 0 or objects[1] == nil then return end -- Roblox what the hell?
while not objects[1]:IsDescendantOf(root) do table.remove(objects,1) end
if objects[1] ~= object then return end
end
if input.UserInputType == Enum.UserInputType.Touch then finger = input end
if input.UserInputType == Enum.UserInputType.MouseButton1 then finger = true end
if not finger then return end
local options = callback(Vector2.new(input.Position.X,input.Position.Y))
local function drag(input)
if options.drag then options.drag(Vector2.new(input.Position.X,input.Position.Y)) end
end
drag(input)
local changed
changed = _G.Input.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement or input == finger then
drag(input)
end
end)
local ended
ended = _G.Input.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input == finger then
drag(input)
if options.ended then options.ended(Vector2.new(input.Position.X,input.Position.Y)) end
changed:Disconnect()
ended:Disconnect()
finger = nil
end
end)
end)
end
return {dragger = dragger}
+127
View File
@@ -0,0 +1,127 @@
-- this code LOOKS scary, but its actually pretty simple and fast
local items = {}
local is_server = not game.Players.LocalPlayer
local userid
if not is_server then
userid = game.Players.LocalPlayer.UserId
end
-- to stop allocating new variables each time (threading doesn't work anyways)
local position
local velocity
local delta1
local delta2
local delta
local difference
local vp
local this_root
local function round(n)
return math.round(n*1000)/1000
end
local common_delta = 1/120
local common_drag_resistance = 0.967
local common_float_resistance = 0.8
local float_terminal = 1
local decay = 10
local complement = -11.5
local idecay = 1/decay
local anti_gravity = Vector3.new(0,196,0)
local floating_velocity = 10
local run = game:GetService("RunService")
local global_drag_resistance = 0
local global_float_resistance = 0
local global_delta = 0
run.PreSimulation:Connect(function(delta)
global_float_resistance = math.pow(common_float_resistance,delta/common_delta)-1
global_drag_resistance = math.pow(common_drag_resistance,delta/common_delta)-1
global_delta = delta
end)
local water_height = -4
local decay_complement = -water_height+decay+complement
local template_instance = _G.Storage.Idol
local impulse = template_instance.ApplyImpulseAtPosition
local angular_impulse = template_instance.ApplyAngularImpulse
local network_owner_check_success, network_owner
local get_network_owner_real = template_instance.GetNetworkOwner
local get_network_owner = function(instance)
network_owner_check_success,network_owner = pcall(function() get_network_owner_real(instance) end)
return ((not network_owner_check_success) or network_owner)
end
local root_part = template_instance.GetRootPart
local new_vec3 = Vector3.new
local counter = 0
local root_reduction = {}
local function reduce_root_part(root)
root.Velocity = root.Velocity * 0.99
root.AssemblyAngularVelocity = root.AssemblyAngularVelocity * 0.99
end
local math_clamp = math.clamp
local local_disconnect_all = true
local function register_part(instance)
if not instance:IsA("BasePart") then return end
if not instance.Parent then return end
local size = instance.Size
local volume = size.X*size.Y*size.Z
local mass = instance.Mass
local event
local operate = true
local splash = instance:FindFirstChild("Splash")
if splash and not splash:IsA("ParticleEmitter") then splash = nil end
if is_server and get_network_owner(instance) then return end
if instance.Massless then return end
instance.AncestryChanged:Connect(function() if not instance.Parent then operate = false event:Disconnect() end end)
--instance.Destroying:Connect(function() print("disconnect") event:Disconnect() end)
event = run.PreSimulation:Connect(function(delta)
--if not operate then event:Disconnect() end
if is_server and get_network_owner(instance) then wait(0.2) return end
vp = instance.Position
position = vp.Y+decay_complement
if position < 0 then
--if splash then splash.Enabled = true splash.Rate = math_clamp(10*instance.AssemblyLinearVelocity.Magnitude,0,100) end -- PLEASE OPTIMISE
position = math.abs(position*idecay)
--instance.Transparency = 0.5
if position > 1 then position = 1 end
velocity = instance.Velocity
--local angular = instance.AssemblyAngularVelocity -- implement later...
difference = velocity*global_drag_resistance + new_vec3(0,velocity.Y-floating_velocity,0)*global_float_resistance
impulse(instance,difference*volume*position+anti_gravity*delta,vp)
else
--if splash then splash.Enabled = false end -- PLEASE OPTIMISE
--velocity = instance.Velocity
wait(0.1) -- to stop checking too many times in air (saving frames)
end
end)
return event
end
if is_server then
game.Workspace.Objects.DescendantAdded:Connect(function(instance)
local owner = instance:GetAttribute("NetworkOwner")
if not owner and is_server then
--register_part(instance)
for _,child in pairs(instance:GetChildren()) do
--register_part(child)
end
end
end)
else
game.Workspace.Objects.DescendantAdded:Connect(function(instance)
local owner = instance:GetAttribute("NetworkOwner")
if owner == userid then
--register_part(instance)
for _,child in pairs(instance:GetChildren()) do
--register_part(child)
end
end
end)
end
local module = {}
module.register_part = register_part
return module
+36
View File
@@ -0,0 +1,36 @@
function init(this,mode)
if mode == "Survival" then
this.Parent.Triggered:Connect(function(player)
for _,tool in pairs(player.Character:GetChildren()) do
if tool:IsA("Tool") then
tool:Destroy()
end
end
for _,tool in pairs(player.Backpack:GetChildren()) do
if tool:IsA("Tool") then
tool:Destroy()
end
end
for _,tool in pairs(_G.Storage.Tools:GetChildren()) do
tool:Clone().Parent = player.Backpack
end
player.Character:SetPrimaryPartCFrame(game.Workspace.Heaven.AngelSpawn.CFrame+Vector3.new(0,4,0))
end)
end
if mode == "Heaven" then
this.Parent.Triggered:Connect(function(player)
for _,tool in pairs(player.Character:GetChildren()) do
if tool:IsA("Tool") then
tool:Destroy()
end
end
for _,tool in pairs(player.Backpack:GetChildren()) do
if tool:IsA("Tool") then
tool:Destroy()
end
end
player.Character:SetPrimaryPartCFrame(game.Workspace.GoonSpawn.CFrame+Vector3.new(0,4,0))
end)
end
end
return getfenv()
+200
View File
@@ -0,0 +1,200 @@
c = require(_G.Modules.CharacterModule)
l = "Left Shoulder"
r = "Right Shoulder"
local lastShot = tick()
local input = game:GetService("UserInputService")
local bulletModule = nil
if script.Parent:FindFirstChild("BulletModule") then bulletModule = require(_G.Modules.BulletModule) end
function init(gun)
local fov = 60
local returnTable = {}
local la = {} -- states for the arm in different animations
local ra = {}
returnTable.la = la
returnTable.ra = ra
returnTable.maxRounds = 10
returnTable.shouldRest = true
returnTable.clock = 0.1
returnTable.rounds = returnTable.maxRounds
local aiming = false
local shootEvent
local cameraLast = game.Workspace.CurrentCamera.CFrame.Rotation
ra.aiming = CFrame.new(0,-1,-2)
ra.idling = CFrame.new(1,-1,-2) * CFrame.Angles(0,0,0.5)
ra.recoil = CFrame.new(0,0,1.5)
ra.sheath = CFrame.new(1.5,-4,0)*CFrame.Angles(-1.57,0,0)
ra.reload = {}
ra.reload.beforeSmashMagazine = CFrame.new(1,-0.5,-2)*CFrame.Angles(0.8,0.5,0)
ra.reload.smashingMagazine = CFrame.new(0.7,-0.9,-2)*CFrame.Angles(0.4,0.3,0)
ra.reload.beforeLoadingMagazine = CFrame.new(1,-0.5,-2)*CFrame.Angles(0.8,-0.5,0)
ra.reload.loadingMagazine = CFrame.new(1,-0.5,-2)*CFrame.Angles(0.8,-0.5,0)
ra.reload.beforePullBack = CFrame.new(2.2,-1,-2.5)*CFrame.Angles(-0.3,1.2,0.1)
ra.reload.pullBack = CFrame.new(1.7,-1,-2.5)*CFrame.Angles(-0.3,1.2,0.1)
la.idling = CFrame.new(-1.5,0,0)*CFrame.Angles(-1.57,0,0)
la.resting = CFrame.new(-1.5,0,0)*CFrame.Angles(-1.57,0,0)
la.aiming = CFrame.new(-0.5,-1.3,-2) * CFrame.Angles(0.3,-1,0)
la.recoil = CFrame.new(0.5,0,0.75)
la.sheath = CFrame.new(-1.5,-1.3,0)*CFrame.Angles(-1.57,0,0)
la.reload = {}
la.reload.beforeSmashMagazine = CFrame.new(-1.1,-1.5,0)*CFrame.Angles(-1.57,0,0)
la.reload.smashingMagazine = CFrame.new(-1.1,-1.5,0)*CFrame.Angles(-1.57,0,0)
la.reload.beforeLoadingMagazine = CFrame.new(0.3,-1.1,-2.5) * CFrame.Angles(0.4,-1,0)
la.reload.loadingMagazine = CFrame.new(0.6,-0.7,-2)*CFrame.Angles(0.6,-1,0)
la.reload.beforePullBack = CFrame.new(-1,-1,-2.6)*CFrame.Angles(0.4,0,0)
la.reload.pullBack = CFrame.new(0.5,-1,-2.4)*CFrame.Angles(0.4,0,0)
for name,cframe in pairs(la.reload) do
la.reload[name] = cframe + Vector3.new(-0.4,0,0)
end
for name,cframe in pairs(ra.reload) do
ra.reload[name] = cframe + Vector3.new(-0.4,0,0)
end
gun.Equipped:Connect(function()
if c.humanoid.SeatPart then return end
local rc = {} -- state for the arm
local lc = {}
local gc = {} -- state for gun pully thing
c.disableMotors()
rc.offset = CFrame.new(0,0,0)
rc.target = ra.idling
lc.offset = CFrame.new(0,0,0)
lc.target = la.resting
rc.janky = CFrame.new()
lc.janky = CFrame.new()
if not returnTable.shouldRest then lc.target = la.idling end
gc.target = CFrame.new(0,0,0)
c.cframes[r] = ra.sheath c.setArm(r)
if not returnTable.shouldRest then c.setArm(l) c.cframes[l] = la.sheath c.setArm(l)
else c.restArm(l) c.cframes[l] = la.resting c.restArm(l) end
c.run:UnbindFromRenderStep("GunMove")
c.run:UnbindFromRenderStep("Transparency")
local modes = {["idle"]=1,["reload"]=function()
local scale = game.Workspace.Scale.Value
rc.target = ra.reload.beforeSmashMagazine
lc.target = la.reload.beforeSmashMagazine
wait(0.3*scale)
gun.MagOut:Play()
rc.target = ra.reload.smashingMagazine
lc.target = la.reload.smashingMagazine
wait(0.4*scale)
rc.target = ra.reload.beforeLoadingMagazine
lc.target = la.reload.beforeLoadingMagazine
wait(0.5*scale)
rc.target = ra.reload.loadingMagazine
lc.target = la.reload.loadingMagazine
gun.MagIn:Play()
wait(0.3*scale)
rc.target = ra.reload.beforePullBack
lc.target = la.reload.beforePullBack
wait(0.4*scale)
gun.Reload:Play()
if gun:FindFirstChild("Slide") then gc.target = gun.Slide.Value end
rc.target = ra.reload.pullBack
lc.target = la.reload.pullBack
wait(0.3*scale)
gc.target = CFrame.new(0,0,0)
end,}
local mode = modes.idle
local reloading = false
if shootEvent then shootEvent:Disconnect() end
shootEvent = gun.Activated:Connect(function()
if not reloading and not c.running then
if returnTable.rounds == 0 and returnTable.maxRounds > 0 then
reloading = true
modes.reload()
reloading = false
returnTable.rounds = returnTable.maxRounds
return
end
if tick()-lastShot < returnTable.clock then return end
gun.Fired:Play()
lastShot = tick()
returnTable.rounds-=1
local rot = Vector3.new(0,0,math.random(-10,10)/10)
rc.offset = ra.recoil * CFrame.Angles(rot.x,rot.y,rot.z)
if aiming or not returnTable.shouldRest then lc.offset = la.recoil end
if gun.Gun:FindFirstChild("GunShot") then
gun.Gun.GunShot.ParticleEmitter.Transparency = NumberSequence.new(0)
end
if gun:FindFirstChild("Slide") then gc.target = gun.Slide.Value end
if bulletModule and gun.Gun:FindFirstChild("Barrel") then bulletModule.fire(gun.Gun.Barrel) end
wait()
if gun.Gun:FindFirstChild("GunShot") then
gun.Gun.GunShot.ParticleEmitter.Transparency = NumberSequence.new(1)
end
rc.offset = CFrame.new(0,0,0)
lc.offset = CFrame.new(0,0,0)
wait(returnTable.clock/2)
gc.target = CFrame.new(0,0,0)
end
end)
c.run:BindToRenderStep("GunMove",Enum.RenderPriority.Last.Value,function(delta)
if gun.Parent ~= c.model then c.enableMotors() return end
--game.Players.LocalPlayer.CameraMode = Enum.CameraMode.LockFirstPerson
if not reloading then
if input:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) or input:IsKeyDown(Enum.KeyCode.Q) then
aiming = true
c.setRunning(false)
rc.target = ra.aiming
lc.target = la.aiming
fov = (fov+60)/2
else
aiming = false
fov = (fov+70)/2
rc.target = ra.idling
rc.janky = CFrame.new():Lerp(game.Workspace.CurrentCamera.CFrame.Rotation:ToObjectSpace(cameraLast),0.3)
cameraLast = game.Workspace.CurrentCamera.CFrame.Rotation
--if lc.target ~= la.resting then c.setArm(l) end
if returnTable.shouldRest then lc.target = la.resting
else lc.target = la.idling end
end
game.Workspace.CurrentCamera.FieldOfView = fov
if c.running then
rc.target = CFrame.new(1.5,-1,-1.5) * CFrame.Angles(1,0,0)
lc.target = la.resting
end
if not reloading and input:IsKeyDown(Enum.KeyCode.R) then
mode = modes.reload
reloading = true
modes.reload()
mode = modes.idle
reloading = false
returnTable.rounds = returnTable.maxRounds
end
end
if not c.humanoid.SeatPart then c.rotateCharacter() c.disableMotors() else --[[print("Enable")]] c.enableMotors() end
c.targets[r] = rc.target * rc.offset * rc.janky
c.targets[l] = lc.target * lc.offset * lc.janky
if gun.Gun:FindFirstChild("Slide") and gun.Gun.Slide:FindFirstChild("Motor6D") then
gun.Gun.Slide.Motor6D.C1 = gun.Gun.Slide.Motor6D.C1:Lerp(gc.target,0.5)
end
c.setArm(r,delta)
if lc.target == la.resting then
c.restArm(l,delta)
else
c.setArm(l,delta)
end
c.motors[r].Part1.LocalTransparencyModifier = 0
c.motors[l].Part1.LocalTransparencyModifier = 0
c.motors[r].Part1.CastShadow = false
c.motors[l].Part1.CastShadow = false
if math.abs(game.Workspace.CurrentCamera.CFrame:ToObjectSpace(c.head.CFrame).Position.Z) > 1 then c.cameraOffset = Vector3.new(1,0,0) end
end)
c.run:BindToRenderStep("Transparency",2,function()
--game.StarterPlayer.CameraMode = Enum.CameraMode.LockFirstPerson
end)
end)
return returnTable
end
return getfenv()
+69
View File
@@ -0,0 +1,69 @@
local extract = bit32.extract
local replace = bit32.replace
local gmatch = string.gmatch
local char = string.char
local pck = string.pack
local unpck = string.unpack
local lshift = bit32.lshift
local rshift = bit32.rshift
local format = string.format
local byte = string.byte
local tostring = tostring
local tonumber = tonumber
local gsub = string.gsub
local rep = string.rep
local sub = string.sub
local r = 33
@native
local function encode(num)
local str = pck("d",num)
local f = unpck("I",str:sub(1,4))
local s = unpck("I",str:sub(5,8))
return format("%s%s%s%s%s%s%s%s%s%s%s",
char(extract(f,0,6)+r),
char(extract(f,6,6)+r),
char(extract(f,12,6)+r),
char(extract(f,18,6)+r),
char(extract(f,24,6)+r),
char(extract(s,0,6)+r),
char(extract(s,6,6)+r),
char(extract(s,12,6)+r),
char(extract(s,18,6)+r),
char(extract(s,24,6)+r),
char(replace(extract(f,30,2),extract(s,30,2),2,2)+r)
)
end
@native
local function decode(str)
local a,b,c,d,e,f,g,h,i,j,k = byte(str,1,11)
k = k - r
local s = replace(replace(replace(replace(replace(f-r,g-r,6,6),h-r,12,6),i-r,18,6),j-r,24,6),extract(k,2,2),30,2)
local f = replace(replace(replace(replace(replace(a-r,b-r,6,6),c-r,12,6),d-r,18,6),e-r,24,6),extract(k,0,2),30,2)
local a = unpck("d",pck("I",f)..pck("I",s))
return a
end
@native
local function rle_encode(str)
--print("nuttin",str)
str = gsub(str,"%$","$$")
--print("dollars sanitised",str)
str = gsub(str,"(!!!!?!?!?!?!?!?)",function(rep)
return format("$%i",#rep)
end)
--print("repeats replaced",str)
return str
end
@native
local function rle_decode(str)
str = gsub(str,"(%$+)(%d)",function(first,num)
if (#first % 2 == 0) then return first..num end
return rep("$",#first-1)..rep("!",tonumber(num))
end)
--print("repeats removed",str)
str = gsub(str,"%$%$","$")
--print("dollars desanities",str)
return str
end
return {decode = decode, encode = encode, rle_encode = rle_encode, rle_decode = rle_decode}
+182
View File
@@ -0,0 +1,182 @@
-- This module can be required and used by other scripts to get information about where the mouse is in the 3D world
local module = {}
module.tags = _G.Tags
-- table of parts in game by index, each index is the object's id, each object has an ID tag associated to it
--[[module.objects = {} -- this should only be regarded for the server
function module.getId(object) -- CLIENT/SERVER
for _,tag in pairs(module.tags:GetTags(object)) do -- remove object from the objects table by its ID
if string.sub(tag,1,3) == "ID_" then
local id = tonumber(string.sub(tag,4,#tag))
return id
end
end
return false
end
function module.removeId(object) -- SERVER ONLY
module.objects[module.getId(object)] = nil
end
function module.newId(object) -- SERVER ONLY
local currentIdIndex = 1
for i=1,#module.objects do
if not module.objects[i] then
module.objects[i] = object
return i
end
end
module.objects[#module.objects+1] = object
return #module.objects+1
end]]
local function getConnectedParts(part)
local connected = part:GetConnectedParts(false)
for _,joint in pairs(part:GetJoints()) do
if joint:IsA("Constraint") then
if joint.Attachment0 and joint.Attachment1 then
if joint.Attachment0.Parent == part then
table.insert(connected,joint.Attachment1.Parent)
else
table.insert(connected,joint.Attachment0.Parent)
end
end
end
end
return connected
end
function module.forEachConnectedObject(start,callback,show,parent,ignore)
if not start then return end
if start:IsA("Model") then start = start.PrimaryPart end
if not parent then parent = game.Workspace.Objects end
local parts = {}
local objects = {}
local function checkPart(part)
local object
if part.Parent:IsA("Model") and part.Parent.Parent == parent then
object = part.Parent
elseif part.Parent == parent then
object = part
end
if not object or objects[object] then return end
objects[object] = object
callback(object)
end
local function tryConnected(part)
if parts[part] then return else parts[part] = part checkPart(part) end
local connected = getConnectedParts(part)
for _,part in pairs(connected) do
tryConnected(part)
end
end
tryConnected(start)
return objects
end
--[[function module.forEachConnectedObjectOld2(start,callback,show,parent,ignore)
if not start then return end
if start:IsA("Model") then start = start.PrimaryPart end
if not parent then parent = game.Workspace.Objects end
local checkedTag = "checked"..tostring(tick())
local objects = {}
local function checkPart(part)
local object
if part.Parent:IsA("Model") and part.Parent.Parent == parent then
object = part.Parent
elseif part.Parent == parent then
object = part
end
if object and not object:HasTag(checkedTag) then
object:AddTag(checkedTag)
table.insert(objects,object)
end
return object
end
local function getConnected(part)
if not part:HasTag(checkedTag) then
checkPart(part)
part:AddTag(checkedTag)
for _,part in pairs(getConnectedParts(part)) do
if part.Name ~= "Separator" or not ignore then getConnected(part) end
end
end
end
for _,part in pairs(start:GetConnectedParts(false)) do
getConnected(part)
end
for _,object in pairs(objects) do
callback(object)
end
for _,part in pairs(module.tags:GetTagged(checkedTag)) do
module.tags:RemoveTag(part,checkedTag)
end
end
function module.forEachConnectedObjectOld(start,callback,debugShow,parent)
local tagId = "checked"..tostring(tick())
if start:IsA("Model") then start = start.PrimaryPart end
local parts = start:GetConnectedParts(true)
module.tags:AddTag(start,tagId)
module.tags:AddTag(start.Parent,tagId)
local function checkPart(part,depth)
local count = 0
if not module.tags:HasTag(part,tagId) then
if depth < 2 then
for _,joint in pairs(part:GetJoints()) do
if joint:IsA("Constraint") then
if joint.Attachment1 and not module.tags:HasTag(joint.Attachment1.Parent,tagId) then
for _,part in pairs(joint.Attachment1.Parent:GetConnectedParts(true)) do
checkPart(part,depth+1)
end
end
if joint.Attachment0 and not module.tags:HasTag(joint.Attachment0.Parent,tagId) then
for _,part in pairs(joint.Attachment0.Parent:GetConnectedParts(true)) do
checkPart(part,depth+1)
end
end
end
end
end
local newObject -- Note, we are walking over the newObject function. this is ok, we're not gonna use it. a neat feature of lua's scope
if part.Parent == game.Workspace.Objects then -- we found a part! lets add it
newObject = part
elseif part.Parent:IsA("Model") and part.Parent.Parent == game.Workspace.Objects then -- we found a model instead from this part (its a child of the model)
newObject = part.Parent
end
print(part,depth)
if newObject and not module.tags:HasTag(newObject,tagId) then
module.tags:AddTag(newObject,tagId)
callback(newObject)
if debugShow then
local previousTransparency = part.Transparency -- fancy bits here show for debug
part.LocalTransparencyModifier = 0.9 - (depth * 0.2)
if count == 6 then count = 0 wait() else count = count + 1 end
task.delay(0.25,function() part.LocalTransparencyModifier = previousTransparency end) -- fancy bits here show for debug
end
end
end
end
for key,part in pairs(parts) do
checkPart(part,0)
end
for _,part in pairs(module.tags:GetTagged(tagId)) do
module.tags:RemoveTag(part,tagId)
end
end]]
return module
+213
View File
@@ -0,0 +1,213 @@
require(game.ReplicatedFirst.ReadyModule)
local module = {}
local input = game:GetService("UserInputService")
local mouse = require(_G.Modules.MouseModule)
local gamepad = Enum.UserInputType.Gamepad1
local function KeyBinding(keycode,value)
return function()
if input:IsKeyDown(keycode) then return value else return nil end
end
end
local function ButtonBinding(keycode,value)
return function()
if input:IsGamepadButtonDown(gamepad,keycode) then return value else return nil end
end
end
local function MouseBinding(inputtype,value)
return function()
if input:IsMouseButtonPressed(inputtype) then return value else return nil end
end
end
local function SequenceBinding(iterations,predicate)
local value = 0
input.InputEnded:Connect(function(input,irrelevant)
if not irrelevant then
if predicate(input) then
value = value + 1/iterations
if value > 1 then value = 0 end
return value
end
end
end)
return function() return value end
end
local function SequenceKeyBinding(keycode,iterations)
return SequenceBinding(iterations,function(input)
return input.UserInputType == Enum.UserInputType.Keyboard and input.KeyCode == keycode
end)
end
local function SequenceButtonBinding(keycode,iterations)
return SequenceBinding(iterations,function(input)
return input.UserInputType == gamepad and input.KeyCode == keycode
end)
end
local function SequenceMouseBinding(inputtype,iterations)
return SequenceBinding(iterations,function(input)
return input.UserInputType == inputtype
end)
end
local function HoldBinding()
return function(old,new)
if not new then return old end
end
end
local function DeltaBinding(velocity,predicate)
return function(value,new,delta)
if predicate() then return (value or 0) + velocity * delta end
end
end
local function DeltaKeyBinding(keycode,velocity)
return DeltaBinding(velocity,function()
return input:IsKeyDown(keycode)
end)
end
local function DeltaButtonBinding(keycode,velocity)
return DeltaBinding(velocity,function()
return input:IsGamepadButtonDown(gamepad,keycode)
end)
end
local function DeltaMouseBinding(inputtype,velocity)
return DeltaBinding(velocity,function()
return input:IsMouseButtonPressed(inputtype)
end)
end
local function MouseMovement(keycode)
return function()
input:IsKeyDown(keycode)
end
end
local controlBindings = {
throttle = {
DeltaKeyBinding(Enum.KeyCode.LeftShift,1),
DeltaKeyBinding(Enum.KeyCode.LeftControl,-1),
HoldBinding()
},
pitch = {
KeyBinding(Enum.KeyCode.W,-1),
KeyBinding(Enum.KeyCode.S,1)
},
yaw = {
KeyBinding(Enum.KeyCode.Q,1),
KeyBinding(Enum.KeyCode.E,-1)
},
roll = {
KeyBinding(Enum.KeyCode.A,1),
KeyBinding(Enum.KeyCode.D,-1)
},
fire = {
MouseBinding(Enum.UserInputType.MouseButton1,1),
KeyBinding(Enum.KeyCode.F,-1)
},
bomb = {
KeyBinding(Enum.KeyCode.B,1)
},
action1 = {
ButtonBinding(Enum.KeyCode.L,1)
},
action2 = {
MouseBinding(Enum.UserInputType.MouseButton3,1)
},
flaps = {
SequenceKeyBinding(Enum.KeyCode.F,4)
},
gear = {
SequenceKeyBinding(Enum.KeyCode.G,1)
},
}
module.values = {}
module.cursor = nil
function module.aimTowards(root,position,values)
local aim = root:PointToObjectSpace(position).Unit
local _,_,z = root:ToEulerAnglesYXZ()
values.pitch += aim.Y*2 + values.pitch
values.yaw += aim.X*-2 + values.yaw
values.roll += values.roll + (-z/3) + values.yaw/5
end
-- Bindings
local shouldAim = true
module.seat = nil
if _G.IsClient then
spawn(function()
local dragger = require(_G.Modules.DraggingModule).dragger
_G:WaitFor("ControlMouseButton")
local function aimOptionCallback()
shouldAim = not shouldAim
if shouldAim then
_G.ControlMouseButton.Text = "Mouse"
else
_G.ControlMouseButton.Text = "WASD"
end
end
_G.ControlMouseButton.Activated:Connect(aimOptionCallback)
input.InputEnded:Connect(function(input,irrelevant)
if not irrelevant then
if
input.UserInputType == Enum.UserInputType.Keyboard and
input.KeyCode == Enum.KeyCode.LeftAlt
then
aimOptionCallback()
end
end
end)
local function processThrottle(throttle)
throttle = math.clamp(throttle,-1,1)
_G.ThrottleFrame.ThrottleBar.Value.Size = UDim2.new(1, 0, 0.5 * math.abs(throttle),0)
_G.ThrottleFrame.ThrottleBar.Value.Position = UDim2.new(0, 0, 0.5 - (0.5 * math.clamp(throttle,0,1)),0)
_G.ThrottleFrame.TextValue.Text = tostring(math.ceil(throttle * 100)).."%"
if throttle > 0 then
_G.ThrottleFrame.ThrottleBar.Value.BackgroundColor3 = Color3.new(0, 1, 0)
end
if throttle < 0 then
_G.ThrottleFrame.ThrottleBar.Value.BackgroundColor3 = Color3.new(1, 0, 0)
end
module.values.throttle = throttle
end
dragger(_G.ThrottleFrame.ThrottleBar,function(begin)
return {
drag = function(changed)
local down = (changed.Y - (_G.ThrottleFrame.ThrottleBar.AbsolutePosition.Y))/_G.ThrottleFrame.ThrottleBar.AbsoluteSize.Y
processThrottle(-(math.clamp(down,0,1) * 2 - 1))
end,
}
end)
repeat
local delta = task.wait()
_G.ThrottleFrame.Visible = not not module.seat
if not module.seat then continue end
for name,bindings in pairs(controlBindings) do
local value = nil
for _,binding in pairs(bindings) do
local new = binding(module.values[name],value,delta)
if new then
--print(name,module.values[name],value,delta)
if math.abs(new) > (value or 0) then value = new end
end
end
module.values[name] = value or 0
end
local instance,ray = mouse.getMouseHit()
module.cursor = ray.Origin + ray.Direction * 300
if module.seat and shouldAim then
local position = ray.Origin + ray.Direction * 300 + Vector3.new(0,25,0)
module.aimTowards(module.seat:GetPivot(),position,module.values)
end
processThrottle(module.values.throttle)
for index,value in pairs(module.values) do
module.values[index] = math.clamp(value or 0,-1,1)
end
until false
end)
end
return module
+176
View File
@@ -0,0 +1,176 @@
params = {}
params.cs = 512
params.cliff = 4
params.offset = Vector3.new(0,-300,0)
params.thresh = 0.18
params.voxels = false
local p = params
exclude = {}
chunks = {}
function part(x,y,z,mat,s)
local stone = Instance.new("Part")
stone.Parent = game.Workspace.Generation
stone.Transparency = 0
stone.Material = Enum.Material.Slate
stone.Position = Vector3.new(x,y,z)*params.s + p.offset
stone.Size = Vector3.new(p.s,p.s,p.s)
if s then stone.Size = s end
stone.Anchored = true
local grass = stone:Clone()
grass.CanCollide = true
grass.Transparency = 0
grass.Material = Enum.Material.Grass
grass.Color = Color3.fromRGB(255, 255, 255)
grass.Parent = stone
grass.Size = Vector3.new(stone.Size.X+1,20,stone.Size.Z+1)
grass.Position = stone.Position + Vector3.new(0,stone.Size.Y/2 - grass.Size.Y/2 + 1,0)
return stone
end
local mod = 1
local n = function(x,y,z,scale)
return math.noise(x*scale,y*scale,z*scale)
end
local function mulv(a,b) return Vector3.new(a.x*b.x,a.y*b.y,a.z*b.z) end
local structures = _G.Storage.Structures:GetChildren()
local foliage = _G.Storage.Foliage:GetChildren()
function islandChunk(ox,oz) -- i had to think there lol
local chunk = chunks[tostring(ox)..","..tostring(oz)]
local cn = n(ox,.5,oz,0.05)
if cn < 0.3 then return end
local offset = math.abs(n(ox,124.5432,oz,0.015)*2)
ox = ox + 20
for oy = 0,3 do
mod = math.clamp(math.pow(Vector2.new(ox,oz).Magnitude/6,2),0,1)
local height = {}
p.thresh = 0.5
p.s = 10
local lx = 1/8
local ly = 1/8
local lz = 1/8
local values = {}
local xi = 0
for x=ox,ox+1,lx do
xi = xi + 1
table.insert(values,{})
local yi = 0
for y=oy,oy+1,ly do
yi = yi + 1
local hshift = math.abs((y/2)-1)/4
table.insert(values[xi],{})
local zi = 0
for z=oz,oz+1,lz do
zi = zi + 1
local v = (n(x,y,z,0.05))*((n(x,y,z,0.1)+n(x,y,z,1)+n(x,y,z,0.5)))-0.2-hshift-math.abs(n(x,0.123,y,0.1))
table.insert(values[xi][yi],v)
if xi > 1 and yi > 1 and zi > 1 then
local temp = values[xi]
local a = temp[yi][zi-1] > 0
local b = temp[yi-1][zi] > 0
local c = values[xi-1][yi][zi] > 0
local d = v > 0
if ((not(a and b and c) and d) or ((a or b or c) and not d)) and math.random(1,4) == 1 then
local part = _G.Storage.Stone:Clone()
part.Position = Vector3.new(x,y+offset,z)*params.cs
part.Size = Vector3.new(lx,ly,lz)*params.cs
part.Parent = chunk.folder
local m = Vector3.new(math.random(1,16)/4,math.random(1,16)/4,math.random(1,16)/4)+Vector3.new(1.2,1.2,1.2)
part.Size = mulv(part.Size,m)
local grass = _G.Storage.Grass:Clone()
grass.Size = Vector3.new(part.Size.X+1,params.cs/math.random(5,30),part.Size.Z+1)
grass.CFrame = part.CFrame + Vector3.new(0,part.Size.Y/2,0)
grass.Parent = chunk.folder
end
end
end
end
end
end
end
function chunk(ox,oz)
math.randomseed(ox+oz)
local chunk = {}
chunk.folder = Instance.new("Folder")
chunk.folder.Name = tostring(ox)..", "..tostring(oz)
chunk.folder.Parent = game.Workspace.Generation
chunk.position = Vector3.new(ox,0,oz) -- ? my hands are cold again
chunk.needed = true
if ox%2==0 and oz%2==0 then
for i=18,2,-4 do
local water = _G.Storage.Water:Clone()
water.Size = Vector3.new(params.cs,2,params.cs)*2
water.Position = Vector3.new(ox,0,oz)*params.cs + Vector3.new(0,i+-41.5,0)
water.Parent = chunk.folder
end
end
chunks[tostring(ox)..","..tostring(oz)] = chunk -- again abusing Lua's hashtables
--[[islandChunk(ox,oz) -- oh lord have mercy on my clockspeed
local cn = n(ox,.5,oz,0.05)
if cn < 0.4 then return end
mod = math.clamp(math.pow(Vector2.new(ox,oz).Magnitude/6,2),0,1)
local height = {}
local lx = 1/8
local ly = 1/8
local lz = 1/8
local values = {}
local xi = 0
for x=ox-lx,ox+1,lx do
xi = xi + 1
table.insert(values,{})
local yi = 1
local y=-0.05
local hshift = y/2
table.insert(values[xi],{})
local zi = 0
for z=oz-lz,oz+1,lz do
zi = zi + 1
local v = (n(x,y,z,1)+n(x,y,z,1))*n(x,y,z,0.05)-0.5-hshift
table.insert(values[xi][yi],v)
if xi > 1 and zi > 1 then
if v > 0 then
local r = CFrame.new()--CFrame.Angles(0,math.random(-10,10)/300,0)
local part = _G.Storage.Sand:Clone()
part.CFrame = CFrame.new(Vector3.new(x,y,z)*params.cs)*r
part.Size = Vector3.new(lx,ly*0.2,lz)*params.cs
part.Parent = chunk.folder
local grass = _G.Storage.Grass:Clone()
grass.CFrame = CFrame.new(Vector3.new(x,y,z)*params.cs)*r
grass.Size = Vector3.new(lx,ly*0.2,lz)*params.cs
grass.Parent = chunk.folder
local m = Vector3.new(math.random(1,10),math.random(1,10)/10,math.random(1,10))/5+Vector3.new(1,1,1)
part.Size = mulv(part.Size,m)
grass.Size = mulv(grass.Size,(m+Vector3.new(0,0.3,0))*0.9)
m = Vector3.new(math.random(1,4)/4+1,1,math.random(1,4)/4+1)
part.Size = mulv(part.Size,m)
local random = Random.new(60391)
for sx=x,x+lx,0.125 do
for sz=z,z+lz,0.125 do
local fv = n(x+1/sx,0,z+1/sz,4.123)
if fv > 0.3 then
local sub = foliage[random:NextInteger(1,#foliage)]:Clone()
sub:SetPrimaryPartCFrame(CFrame.new(Vector3.new(sx,y,sz)*params.cs)*CFrame.Angles(0,math.random(0,3)*math.pi*0.5,0))
sub.Parent = chunk.folder
sub:ScaleTo(math.random(1,10)/10 + 1)
end
if fv < -0.4 then
local sub = structures[random:NextInteger(1,#structures)]:Clone()
sub:SetPrimaryPartCFrame(CFrame.new(Vector3.new(sx-0.125,y+0.015+0.00390625,sz)*params.cs)*CFrame.Angles(0,math.random(0,3)*math.pi*0.5,0))
sub.Parent = chunk.folder
end
end
end
end
end
end
end]]
end
-- ok now more!
return getfenv()
+975
View File
@@ -0,0 +1,975 @@
local module = {}
_G.Methods = module
local tags = _G.Tags
local saveToPartTranslator = {
Barrel = "$B",
Sheet = "$S",
Stick = "$s"
}
local partToSaveTranslator = {
}
local ids = require(_G.Modules.IdModule)
module.Ids = ids
local mouse = require(_G.Modules.MouseModule)
module.Mouse = mouse
local buildParts = {}
local idParts = {}
local idNodes = {}
local polyParts = {}
module.canLoad = false
module.polyParts = polyParts
module.buildParts = buildParts
module.idParts = idParts
task.spawn(function()
require(game.ReplicatedFirst.ReadyModule)("Parts")
_G.Storage:WaitForChild("Parts")
for _,object in pairs(_G.Storage.Parts:GetDescendants()) do
if object.Parent:IsA("Folder") then
local id = object:GetAttribute("PartId")
if not id then continue end
if not object:HasTag("ConnectorObject") then
buildParts[object.Name] = object
if saveToPartTranslator[object.Name] then buildParts[saveToPartTranslator[object.Name]] = object end
idParts[id] = object
else
if object:GetAttribute("Nodes") == 3 then
if not object:FindFirstChild("A") then
warn(tostring(object).." missing sub-wedge 'A'")
end
if not object:FindFirstChild("B") then
warn(tostring(object).." missing sub-wedge 'B'")
end
end
idParts[id] = object
polyParts[id] = object
polyParts[object.Name] = object
--[[if object:FindFirstChild("A") and object:FindFirstChild("B") then
polyParts[object.Name] = object
polyParts[id] = object
elseif object:FindFirstChild("1") and object:FindFirstChild("2") then
idParts[id] = object
else
polyParts[object.Name] = object
polyParts[id] = object
idParts[id] = object
end]]
end
end
end
-- To fix old compatibility issues
local polyCompat = {
Pane = 17,
Wedge = 6,
Stick = 5,
Paper = 20,
HeavySheet = 10,
Bar = 13,
LightSheet = 14,
Rod = 13,
Rope = 22
}
for index,item in pairs(polyCompat) do
--[[local part = idParts[item]:Clone()
part.Parent = idParts[item].Parent
if part:FindFirstChild("1") then part["1"].Name = "A" end
if part:FindFirstChild("2") then part["2"].Name = "B" end]]
polyParts[index] = polyParts[item]
end
module.canLoad = true
end)
for id,part in pairs(idParts) do
local nodeId = part:GetAttribute("NodeId")
if nodeId then
idNodes[id] = idParts[nodeId]
end
end
function module.getPartByName(name)
return buildParts[name]
end
function module.getPolyByName(name)
return polyParts[name]
end
function module.getPartById(id)
return idParts[id]
end
function module.getNodeById(id)
return idNodes[id]
end
function module.setItemCFrame(item,cframe)
if item:IsA("BasePart") then
item.CFrame = cframe
elseif item:IsA("Model") then
if item.PrimaryPart then
local mcframe,size = item:GetBoundingBox()
item:SetPrimaryPartCFrame(cframe-cframe.Position+cframe:PointToWorldSpace(mcframe:PointToObjectSpace(item.PrimaryPart.Position)))
end
end
return cframe
end
function module.getItemSize(item)
if item:IsA("BasePart") then
return item.Size
elseif item:IsA("Model") then
if item.PrimaryPart then
return item:GetExtentsSize()
end
end
end
function module.getItemCFrame(item)
if item:IsA("BasePart") then
return item.CFrame
elseif item:IsA("Model") then
return item:GetBoundingBox()
end
end
function module.colorPart(part,color)
if part:IsA("Model") then
for _,subpart in pairs(part:GetDescendants()) do
if subpart:IsA("BasePart") then
subpart.Color = color
end
end
else
part.Color = color
end
part:SetAttribute("Color",color)
end
function module.shapeTriangle(p1,p2,p3,r1,r2) -- simple function, might work?
local s = 0 --0.01
local bottom = {
b1 = nil,
b2 = nil,
dist = 0,
other = nil
}
local function check(point1,point2,other)
local dist = (point1 - point2).Magnitude
if dist > bottom.dist then
bottom.b1 = point1
bottom.b2 = point2
bottom.other = other
bottom.dist = dist
end
end
check(p1,p2,p3) -- check all of the different options for bottom and get the right one?
check(p1,p3,p2)
check(p2,p3,p1)
local view = CFrame.lookAt(bottom.b1,bottom.b2,(bottom.other-bottom.b1).Unit)
local object = view:PointToObjectSpace(bottom.other)
local length = object.Z
local height = object.Y
r1.Size = Vector3.new(r1.Size.X,height-s,-length-s)
r1.CFrame = (view * CFrame.Angles(0,math.rad(180),0)) * CFrame.new(0,height/2+s/2,-length/2+s/2)
r2.Size = Vector3.new(r2.Size.X,height-s,-(-bottom.dist-length)-s)
r2.CFrame = view * CFrame.new(0,height/2+s/2,-(bottom.dist-length)/2+s/2)
return r1,r2
end
function module.positionPart(part,nodes,s)
local d = (nodes[1].Position - nodes[2].Position).Magnitude -- distance
local a = nodes[1].Position -- first node
local b = nodes[2].Position -- second node
if #nodes == 2 then -- stick on Z (XY thick)
part.CFrame = CFrame.lookAt(a,b) * CFrame.Angles(0,math.pi*0.5,0)
part.CFrame = part.CFrame + part.CFrame.RightVector * d * 0.5
part.Size = Vector3.new(d,part.Size.Y,part.Size.Z)
end
if #nodes == 3 then -- triangle on YZ (X thick)
local c = nodes[3].Position -- third node
local p2 = part["B"]
local p1 = part["A"]
module.shapeTriangle(a,b,c,p1,p2)
end
end
function module.shiftPart(part,shift)
shift = shift - (part:GetAttribute("Shift") or 0)
for _,part in pairs(part:GetChildren()) do
if part:IsA("BasePart") then
part.Position = part.Position + part.CFrame.RightVector * shift
end
end
end
function module.getNodePositions(part)
end
function module.form_rod(pa,p1,p2)
local d = (p1 - p2).Magnitude -- distance
pa.CFrame = CFrame.lookAt(p1,p2) * CFrame.Angles(0,math.pi*0.5,0)
pa.CFrame = pa.CFrame + pa.CFrame.RightVector * d * 0.5
pa.Size = Vector3.new(d,pa.Size.Y,pa.Size.Z)
end
module.saves = require(script.Parent:WaitForChild("SerialisationModule"))
spawn(function()
require(game.ReplicatedFirst.ReadyModule)("MethodCommands")
if _G.IsClient then
local commands = require(_G.Modules.CommandsModule)
commands.new("save format (%d)","set save format <number:string>: Sets current save version. Use '4'",function(v)
_G.Events.Notification:Fire("Set Save Format","Set save format to: "..v)
_G.SaveFormat = v
end)
commands.new("save comp[^%s]- (.+)","set save compress <on:bool>: Enables or disables save compression for debugging. A bad idea.",function(v)
v = not not v:match("t")
_G.Events.Notification:Fire("Set Save Compress","Set save compress to: "..tostring(v))
_G.SaveCompression = v
end)
end
end)
function module.saves.modelToData(root)
if root then
if module.saves[_G.SaveFormat] then
local success,result = xpcall(module.saves[_G.SaveFormat].encode,function(err)
print("before")
_G.Error(string.format("Save V%s Error: %s",_G.SaveFormat,err))
end,root,{
compress = _G.SaveCompression
})
if not success then return nil end
if #result.errors > 0 then print(result.errors) end
return result.data
else
return _G.Methods.dataToString(_G.Methods.modelToTable(root))
end
else
error("Root passed is nil!",2)
end
end
function module.saves.dataToModel(raw,physics,mirror)
if _G.IsString(raw) then
local v,r = raw:match("v(%d)save(.+)")
if v then
local success,result = xpcall(module.saves[v].decode,function(err)
warn(debug.traceback(err))
end,r,{
physics = physics,
mirror = mirror,
})
if not success then return nil end
if #result.errors > 0 then print(result.errors) end
if result.model then result.model:SetAttribute("Version","V"..v) end
return result.model
else
local tabular = _G.Protected(module.universalConvertToTable,raw)
if not tabular or not tabular[1] then return nil end
local model = _G.Protected(module.convertToModel,tabular,physics)
if not model then return nil end
if not tabular[1]:match("V%dSAVE") then
tabular.Name = tabular[1]
--model:AddTag("Old")
end
model:AddTag("Old")
return model
end
else
return nil
end
end
function module.tableToModel(data,physics)
end
function module.stringToData(str)
if type(str) ~= "string" then error("Not a string",2) end
local insert = table.insert
if str:sub(1,1) ~= "{" then return end
local function parse(start)
local data = {}
local function register(str)
if #str < 1 then return end
local index,part = str:match("(.*)=(.*)")
if not index then
insert(data,str)
else
data[index] = str
end
end
while start do
local first,last = str:find("[{},]",start+1,false)
local c = str:sub(last,last)
if c == "{" then
local sub_data
local index = str:sub(start+1,last-2)
sub_data,start = parse(last)
if #index > 0 then
data[index] = sub_data
else
insert(data,sub_data)
end
elseif c == "}" then
register(str:sub(start+1,last-1))
return data,last
else
if c == "," then
register(str:sub(start+1,last-1))
end
start = last
end
end
wait()
end
return parse(1)
end
function module.can_destroy(part,player)
return part:HasTag("Combat") or part.Parent:HasTag("Combat")
end
local built = {BuiltWeldConstraint = true}
function module.break_part(part,player)
if part:IsA("Model") then
for _,item in pairs(part:GetDescendants()) do
if built[item.Name] then
item:Destroy()
end
end
elseif part:IsA("BasePart") then
part:BreakJoints()
end
end
function module.wash_part(part,colour,material)
if part:IsA("Model") then
for _,item in pairs(part:GetDescendants()) do
if part:IsA("BasePart") then
part.Color = colour
part.Material = material
end
end
elseif part:IsA("BasePart") then
part.Color = colour
part.Material = material
end
end
function module.fire(root,player)
if module.can_destroy(root,player) then
if root:HasTag("Engine") then
local fire = _G.Storage.Fire:Clone()
fire.Parent = root.PrimaryPart
end
module.wash_part(root,Color3.new(1, 0.831617, 0.298161),Enum.Material.Neon)
wait(math.random(1,10)*0.333)
module.break_part(root,player)
for _,part in pairs(root:GetTouchingParts()) do
module.fire(part,player)
end
wait(math.random(1,20)*0.333)
module.wash_part(root,Color3.new(0,0,0),Enum.Material.Sand)
end
end
function module.explode(root,player,power)
print("explode")
if root:HasTag("Exploded") then return end
root:AddTag("Exploded")
local debris = game:GetService("Debris")
if not power then power = 10 end
local explosion = _G.Sounds.Explosion:Clone()
explosion.Parent = root
explosion:Destroy()
local burn = _G.Storage.Burn:Clone()
burn.Parent = root
debris:AddItem(burn,1)
local params = OverlapParams.new()
params.MaxParts = 40
params.RespectCanCollide = true
params.FilterType = Enum.RaycastFilterType.Include
params.FilterDescendantsInstances = {game.Workspace.Objects}
local result = game.Workspace:GetPartBoundsInRadius(root.Position,10,params)
wait()
for _,hit in pairs(result) do
if module.can_destroy(hit,player) then
module.break_part(hit,player)
if math.random(1,3) == 1 then module.fire(hit,player) end
end
end
wait(0.1)
for _,hit in pairs(result) do
hit:ApplyImpulse((hit.Position-root.Position)*-100)
end
end
function module.playerOwnsPart(player,part) return part:HasTag("P_"..player.UserId) or part:HasTag("P_ublic") end
function module.dataToString(data)
local insert = table.insert
local isString = {["string"] = true}
local isNumber = {["number"] = true}
local isTable = {["table"] = true}
local function converts(t)
local result = {"{"}
local first = true
for index,item in pairs(t) do
if not first then insert(result,",") else first = false end
local value
if isTable[type(item)] then
value = converts(item)
else
value = tostring(item)
end
if isNumber[type(index)] then
insert(result,value)
elseif isString[type(index)] then
insert(result,index.."="..value)
end
end
insert(result,"}")
return table.concat(result)
end
return converts(data)
end
function module.convertToData(data)
if not data:match("{.*}") then return end
data = data:gsub("%s","")
local index = 2
local function parseTable(t)
local this = {} -- The table we're working with now
local idx -- Special named table index
local _,last
local char
local value
while true do
_,last = data:find("[{,=}]",index)
if not last then return end
char = data:sub(last,last) -- The next significant character
if index ~= last then value = data:sub(index,last-1) end -- Current value in table
index = last + 1
--print(t,value,char)
local function terminate()
if idx then this[idx] = value -- If there is an index, add it by index
else table.insert(this,value) end -- Otherwise just plainly insert it (by order)
end
if char == "=" then -- When finding an '=', take the existing piece and make it an index
idx = value
elseif char == "," then -- When finding a ',', the entry has terminated so add it
terminate()
elseif char == "{" then -- When finding a '{', there is a new table whose value needs to be computed
value = parseTable(t+1)
else-- char == "}" then -- When finding a '}', this table has terminated so return
terminate()
return this
end
end
end
return parseTable(1)
end
function module.universalConvertToTable(data)
if not (data:match("{V%dSAVE,objects=")) then return module.stringToData(data)
else return module.convertToData(data) end
end
function module.removeSpaces(data)
local new = ""
for index = 1, #data do
local char = string.sub(data,index,index)
if char ~= " " then new = new .. char end
end
return new
end
function module.round(number,decimals)
local pow = math.pow(10,decimals)
return math.round(number*pow)/pow
end
function module.makeSaveString(data)
local saveDataString = "{"..data.saveName..","
local saveObjectsString = "{"
for index,item in ipairs(data.saveObjects) do
local editString = ""
local cframeString = module.removeSpaces(tostring(item.CFrame))
if item.Edit then
for _,edit in pairs(item.Edit) do
editString = editString .. "{"..edit[1]..","..edit[2].."},"
end
end
if #editString ~= 0 then editString = "{"..editString.."}," end
saveObjectsString = saveObjectsString .. "{{"
.. cframeString
.. "}" .. ","
.. item.Name .. ","..editString.."},"
end
saveObjectsString = saveObjectsString .. "}"
saveDataString = saveDataString .. saveObjectsString .. ","
local saveWeldsString = "{"
for index,item in pairs(data.saveWelds) do
saveWeldsString = saveWeldsString .. "{{"
.. tostring(item.Part0[1])
if item.Part0[2] then
saveWeldsString = saveWeldsString .. "," .. item.Part0[2]
end
saveWeldsString = saveWeldsString .. "},{"
.. tostring(item.Part1[1])
if item.Part1[2] then
saveWeldsString = saveWeldsString .. "," .. item.Part1[2]
end
saveWeldsString = saveWeldsString .. "}},"
end
saveWeldsString = saveWeldsString .. "}"
saveDataString = saveDataString .. saveWeldsString
saveDataString = saveDataString .. "}"
--print(saveDataString)
return saveDataString
end
function module.convertValue(value) -- BRUH
if value == "true" then return true
elseif value == "false" then return false
elseif tonumber(value) then return tonumber(value)
else return value end
end
function module.tableToCFrame(c)
local n = tonumber
return CFrame.new(
n(c[1]),n(c[2]),n(c[3]),
n(c[4]),n(c[5]),n(c[6]),
n(c[7]),n(c[8]),n(c[9]),
n(c[10]),n(c[11]),n(c[12])
)
end
local function weld(part1,part2)
local weld = Instance.new("WeldConstraint")
weld.Name = "BuiltWeldConstraint"
weld.Parent = part1
weld.Part0 = part1
weld.Part1 = part2
return weld
end
local function weldNoPersist(part1,part2)
weld(part1,part2).Name = "WeldConstraint"
end
local CFrameNew = CFrame.new
local CFrameFromAxisAngle = CFrame.fromAxisAngle
local tableUnpack = table.unpack
local Vector3New = Vector3.new
local n = tonumber
function module.cframeToData(cframe)
local p = cframe.Position
local r,w = cframe:ToAxisAngle()
return {{cframe.Position},{r},w}
end
local tp = table.pack
function module.cframeToDataNoAxis(cframe)
return tp(cframe:GetComponents())
end
local nc = CFrame.new
local tu = table.unpack
function module.dataToCFrameNoAxis(data)
return nc(tu(data))
end
function module.dataToCFrame(data)
if not (data[1] and data[2] and data[3]) then return CFrame.new() end
local p1,p2,p3 = tableUnpack(data[1])
local r1,r2,r3 = tableUnpack(data[2])
local w = data[3]
return CFrameNew(n(p1),n(p2),n(p3))*CFrameFromAxisAngle(Vector3New(n(r1),n(r2),n(r3)),n(w))
end
local editCompress = {Color="C",HingeCFrame="HC",Size="S"}
local editDepress = {HC="HingeCFrame",C="Color",S="Size"}
function module.modelToTable(root)
local getItemCFrame = function(object) if object:IsA("BasePart") then return object.CFrame elseif object:IsA("Model") then return object.PrimaryPart.CFrame end end
local origin = getItemCFrame(root)
local insert = table.insert
local saveData = {"V3SAVE"}
saveData.objects = {}
saveData.welds = {}
local objects = {}
--[[Agreed object structure:
{
1: name
2: {node1,node2,node3} cframes (nodes included)
3: {colour={a,b,c},size={a,b,c},speed=120} edits (size included)
}
]]
local isBuiltWeld = {["BuiltWeldConstraint"] = true}
local index = 1
ids.forEachConnectedObject(root,function(object)
insert(objects,object)
objects[object] = index
index = index + 1
local cframes = {}
local edits = {}
local nodes = object:GetAttribute("Nodes")
if nodes then
for i=1,nodes do
insert(cframes,module.cframeToDataNoAxis(origin:ToObjectSpace(object[i].CFrame)))
end
else
insert(cframes,module.cframeToDataNoAxis(origin:ToObjectSpace(getItemCFrame(object))))
end
-- edits
for index,edit in pairs(object:GetChildren()) do
if tags:HasTag(edit,"Edit") and
(edit:GetAttribute("Default") ~= edit.Value)
then
edits[editCompress[edit.Name] or edit.Name] = tostring(edit.Value)
end
end
local id = object:GetAttribute("PartId") or object.Name
--local original = module.getPartById(id) or module.getPartByName(id)
-- psuedo edits
local color = object:GetAttribute("Color")
if color then edits[editCompress["Color"]] = {color.R,color.G,color.B} end
local hinge = object:FindFirstChild("Hinge")
if hinge then edits[editCompress["HingeCFrame"]] = module.cframeToDataNoAxis(origin:ToObjectSpace(hinge.CFrame)) end
local size = object:GetAttribute("Size")
if size then size = size * math.sqrt(0.333); edits[editCompress["Size"]] = {size.X,size.Y,size.Z} end
-- final object
local final = {id,cframes}
if next(edits) then insert(final,edits) end
insert(saveData.objects,final)
end)
for index,object in ipairs(objects) do
for _,weld in pairs(object:GetDescendants()) do
if isBuiltWeld[weld.Name] then
if not weld.Part0 or not weld.Part1 then continue end
local first = objects[weld.Part0]
if not first then first = {objects[weld.Part0.Parent],weld.Part0.Name}
else first = {first} end
local second = objects[weld.Part1]
if not second then second = {objects[weld.Part1.Parent],weld.Part1.Name}
else second = {second} end
insert(saveData.welds,{first,second})
end
end
end
return saveData
end
function module.convertToModel(data,physics)
if not data then return end
local v = data[1]:match("V(%d)SAVE")
if not v or not data.objects then return module.oldConvertToModel(data,physics) end
local insert = table.insert
local objects = {}
local model = Instance.new("Model")
local toCFrame = module.dataToCFrameNoAxis
local setCFrame = function(part,cframe) if part:IsA("BasePart") then part.CFrame = cframe else part:SetPrimaryPartCFrame(cframe) end end
model:SetAttribute("Version","V3")
if v == "2" then setCFrame = module.setItemCFrame toCFrame = module.dataToCFrame model:SetAttribute("Version","V2") end
local editActions = {
Color = function(edit,part)
module.colorPart(part,Color3.new(n(edit[1]),n(edit[2]),n(edit[3])))
end,
HingeCFrame = function(edit,part)
local hinge = part:FindFirstChild("Hinge")
if hinge then hinge.CFrame = toCFrame(edit) end
end,
MaterialAttribute = function()
end,
Size = function(edit,part)
local size = Vector3.new(n(edit[1]),n(edit[2]),n(edit[3]))
if part:IsA("Model") then
part:ScaleTo(size.Magnitude)
size = (size/math.sqrt(0.333)) -- For parity with the new stronger system
else
part.Size = size
end
part:SetAttribute("Size",size)
end,
}
repeat wait() until module.canLoad
for index,object in ipairs(data.objects) do
local name = object[1]
name = n(name) or name
local isPoly = #object[2] > 1
local part = idParts[name]
local node
if not part then
if isPoly then
part = module.getPolyByName(name)
if not part then print(name) end
node = part.Parent:FindFirstChild("Node") or idParts[-1]
else part = module.getPartByName(name) end
else
if isPoly then
node = idNodes[name] or part.Parent:FindFirstChild("Node") or idParts[-1]
end
end
if not part then warn(name,isPoly,idParts,buildParts,polyParts) end
part = part:Clone()
local isModel = not part:IsA("BasePart")
local primary = part
if isModel then primary = part.PrimaryPart end
if isPoly then
local nodes = {}
for index,cframe in pairs(object[2]) do
local newNode = node:Clone()
newNode.CFrame = toCFrame(cframe)
newNode.Parent = part
newNode.Name = tostring(index)
newNode.Transparency = 1
nodes[index] = newNode
end
module.positionPart(part,nodes)
for _,subpart in pairs(part:GetChildren()) do if subpart ~= primary and subpart:IsA("BasePart") then weldNoPersist(subpart,primary) end end
else
setCFrame(part,toCFrame(object[2][1]))
end
local cnv = module.convertValue
if object[3] then
for index,edit in pairs(object[3]) do
if editDepress[index] then index = editDepress[index] end
local value = part:FindFirstChild(index)
if not value then
if editActions[index] then
editActions[index](edit,part)
end
else
value.Value = cnv(edit)
end
end
end
if not physics then
if isModel then
for _,object in pairs(part:GetChildren()) do
if object:IsA("BasePart") then
object.Anchored = true
object.CanCollide = false
end
end
else
part.CanCollide = false
part.Anchored = true
end
end
if index == 1 then if part:IsA("Model") then model.PrimaryPart = part.PrimaryPart else model.PrimaryPart = part end end
if part.Name == "VehicleSeat" then model.PrimaryPart = part.PrimaryPart end
part.Parent = model
insert(objects,part)
end
if physics then
for index,weld in pairs(data.welds) do
local weld11 = n(weld[1][1])
local weld12 = weld[1][2]
local weld21 = n(weld[2][1])
local weld22 = weld[2][2]
local part1 = objects[weld11]
local part2 = objects[weld21]
if weld12 then part1 = part1:FindFirstChild(weld12) or part1 end
if weld22 then part2 = part2:FindFirstChild(weld22) or part2 end
if not (part1 and part2) then print("Weld Missing",objects[weld11],objects[weld21],weld21,weld22) continue end
local new = Instance.new("WeldConstraint")
if part1:IsA("Model") then part1 = part1.PrimaryPart end
if part2:IsA("Model") then part2 = part2.PrimaryPart end
new.Part0 = part1
new.Part1 = part2
new.Parent = part1
new.Name = "BuiltWeldConstraint"
end
end
return model
end
function module.oldConvertToModel(data,physics)
if not data[1] then return end
local parts = {}
local n = tonumber
local currentModel = Instance.new("Model")
currentModel:SetAttribute("Version","V1")
local internalSuccess = true
local internalValue = {}
local success,value = xpcall(function()
for index,part in pairs(data[2]) do
local success,value = pcall(function()
local isPoly = #part[1] == 0
local new = module.getPartByName(part[2])
if new then new = new:Clone() end
if isPoly then
new = module.getPolyByName(part[2])
if not new then warn("Missing Poly: "..part[2]) end
local old = new
new = new:Clone()
local realNodes = {}
local attach = new
if new:IsA("Model") then attach = new.PrimaryPart end
for _,edit in pairs(part[3]) do
if #edit[1] == 1 and tonumber(edit[1]) then
local realNode = (old.Parent:FindFirstChild("Node") or _G.Storage.Parts.DefaultNode):Clone()
realNode.CFrame = module.tableToCFrame(edit[2])
realNode.Name = edit[1]
realNode.Parent = new
realNode.Transparency = 1
table.insert(realNodes,realNode)
end
end
module.positionPart(new,realNodes)
for _,node in pairs(realNodes) do
weldNoPersist(node,attach)
end
for _,part in pairs(new:GetChildren()) do
if part:IsA("BasePart") and part ~= attach then weldNoPersist(part,attach) end
end
end
if new then
table.insert(parts,new)
new.Parent = currentModel
if index == 1 then
if new:IsA("Model") then
currentModel.PrimaryPart = new.PrimaryPart else currentModel.PrimaryPart = new
end
end
if part[3] then
local cnv = module.convertValue
if typeof(part[3]) == "string" then print(part[3]) end
for _,edit in pairs(part[3]) do
if #edit ~= 1 and not tonumber(edit[1]) and not (edit[1] == "C") and new:FindFirstChild(edit[1]) then
new[edit[1]].Value = module.convertValue(edit[2])
elseif (edit[1] == "C") then
module.colorPart(new,Color3.new(cnv(edit[2][1]),cnv(edit[2][2]),cnv(edit[2][3])))
end
end
end
if not physics then
if new:IsA("Model") then
new.PrimaryPart.Anchored = true
for _,part in pairs(new:GetDescendants()) do
if part:IsA("BasePart") then part.CanCollide = false part.CanTouch = false end
end
else
new.Anchored = true
new.CanCollide = false
new.CanTouch = false
end
end
if not isPoly then
module.setItemCFrame(new,module.tableToCFrame(part[1]))
end
end
end)
if not success then internalSuccess = false table.insert(internalValue,value) end
end
if not physics then return currentModel,data end
for index,weld in pairs(data[3]) do
local new = Instance.new("WeldConstraint")
local part0 = parts[n(weld[1][1])]
if not part0 then warn("Weld missing.") continue end
if weld[1][2] and part0:FindFirstChild(weld[1][2]) then part0 = part0[weld[1][2]] end
local part1 = parts[n(weld[2][1])]
if not part1 then continue end
if weld[2][2] and part1:FindFirstChild(weld[2][2]) then part1 = part1[weld[2][2]] end
if part0:IsA("Model") then part0 = part0.PrimaryPart end
if part1:IsA("Model") then part1 = part1.PrimaryPart end
new.Part0 = part0
new.Part1 = part1
new.Parent = part0
new.Name = "BuiltWeldConstraint"
end
end,print)
if not internalSuccess then data[1] = "CORRUPTED" for _,item in pairs(internalValue) do print(data) print(item) debug.traceback(item,2) end end
--print(data)
return currentModel,data
end
local function getConnectedParts(part)
local connected = part:GetConnectedParts(false)
for _,joint in pairs(part:GetJoints()) do
if joint:IsA("Constraint") then
if joint.Attachment0 and joint.Attachment1 then
if joint.Attachment0.Parent == part then
table.insert(connected,joint.Attachment1.Parent)
else
table.insert(connected,joint.Attachment0.Parent)
end
end
end
end
return connected
end
function module.forEachConnectedObject(start,callback,show,parent)
if not start then return end
if start:IsA("Model") then start = start.PrimaryPart end
if not parent then parent = game.Workspace.Objects end
local parts = {}
local objects = {}
local function checkPart(part)
local object
if part.Parent:IsA("Model") and part.Parent.Parent == parent then
object = part.Parent
elseif part.Parent == parent then
object = part
end
if not object or objects[object] then return end
objects[object] = object
if callback then callback(object) end
end
local function tryConnected(part)
if parts[part] then return else parts[part] = part checkPart(part) end
local connected = getConnectedParts(part)
for _,part in pairs(connected) do
tryConnected(part)
end
end
tryConnected(start)
return objects
end
function module.setNetworkOwner(part,player)
for _,part in pairs(part:GetConnectedParts(true)) do
if part.Anchored then
return
end
end
part:SetNetworkOwner(player)
end
function module.getOwnedOrNil(player,part)
if module.playerOwnsPart(player,part) then
return part
elseif module.playerOwnsPart(player,part.Parent) then
return part
end
return nil
end
function module.getObjectFromPart(part)
if part:GetAttribute("PartId") then
return part
elseif part.Parent and part.Parent:GetAttribute("PartId") then
return part.Parent
end
end
module.getMouseHit = mouse.getMouseHit
return module
+56
View File
@@ -0,0 +1,56 @@
-- This module can be required and used by other scripts to get information about where the mouse is in the 3D world
local module = {}
local input = game:GetService("UserInputService")
module.mouseDistance = 500
module.mouseRaycastParameters = RaycastParams.new()
local dominantFinger
local usingMouse = true
local lastMouseInstance
local lastMouseRay
game:GetService("UserInputService").TouchStarted:Connect(function(touch,irrelevant)
if not irrelevant and not dominantFinger then
dominantFinger = touch
usingMouse = false
end
end)
game:GetService("UserInputService").TouchEnded:Connect(function(touch,irrelevant)
if touch == dominantFinger then dominantFinger = nil end
end)
local uit = Enum.UserInputType
local mouseInputs = {uit.MouseButton1,uit.MouseWheel,uit.MouseMovement,uit.MouseButton2}
for _,item in ipairs(mouseInputs) do mouseInputs[item] = true end
game:GetService("UserInputService").InputBegan:Connect(function(object)
if mouseInputs[object.UserInputType] then usingMouse = true end
end)
function module.getMouseHit(location)
local mousePosition = location or input:GetMouseLocation()
local mouseRay = game.Workspace.CurrentCamera:ViewportPointToRay(mousePosition.X,mousePosition.Y)
if dominantFinger then
local position = dominantFinger.Position
mouseRay = game.Workspace.CurrentCamera:ScreenPointToRay(position.X,position.Y)
end
local mouseInstance = game.Workspace:Raycast(mouseRay.Origin,mouseRay.Direction*module.mouseDistance,module.mouseRaycastParameters)
return mouseInstance,mouseRay,(dominantFinger or usingMouse)
end
function module.isMouseDown()
return input:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) or dominantFinger
end
function mouse()
local pos = input:GetMouseLocation()
local vec = game.Workspace.CurrentCamera:ViewportPointToRay(pos.x,pos.y,0)
return game.Workspace.CurrentCamera.CFrame:PointToObjectSpace(vec.Origin+vec.Direction*300).Unit
end
function module.initialise()
module.mouseRaycastParameters.FilterDescendantsInstances = {game.Workspace.Hidden,game.Workspace.Players}
module.mouseRaycastParameters.FilterType = Enum.RaycastFilterType.Exclude
module.mouseRaycastParameters.IgnoreWater = true
end
return module
+34
View File
@@ -0,0 +1,34 @@
NAME_COLORS =
{
Color3.new(253/255, 41/255, 67/255), -- BrickColor.new("Bright red").Color,
Color3.new(1/255, 162/255, 255/255), -- BrickColor.new("Bright blue").Color,
Color3.new(2/255, 184/255, 87/255), -- BrickColor.new("Earth green").Color,
BrickColor.new("Bright violet").Color,
BrickColor.new("Bright orange").Color,
BrickColor.new("Bright yellow").Color,
BrickColor.new("Light reddish violet").Color,
BrickColor.new("Brick yellow").Color,
}
local function GetNameValue(pName)
local value = 0
for index = 1, #pName do
local cValue = string.byte(string.sub(pName, index, index))
local reverseIndex = #pName - index + 1
if #pName%2 == 1 then
reverseIndex = reverseIndex - 1
end
if reverseIndex%4 >= 2 then
cValue = -cValue
end
value = value + cValue
end
return value
end
local color_offset = 0
function ComputeNameColor(pName)
return NAME_COLORS[((GetNameValue(pName) + color_offset) % #NAME_COLORS) + 1]
end
return getfenv()
+435
View File
@@ -0,0 +1,435 @@
-- Module for handling all game objects
require(game.ReplicatedFirst.ReadyModule)
local input = require(_G.Modules.InputModule)
-- Note that closures absorb upvalues. I'm trying this as a programming paradigm here.
local modulesByName = {}
local modules = {} -- [Name] = {...module}
local predicates = {}
local objects = {} -- Table of globally considered parts (responsible for considering) (consider = input and background tasks)
local owning = {} -- Table of locally owned parts (responsible for all processing)
-- Get the object from a part and do something to it
local function asObject(part,callback)
if part.Parent:GetAttribute("PartId") then return callback(part.Parent) -- Since parts can be INSIDE of objects
elseif part:GetAttribute("PartId") then return callback(part) end
end
local processOwnership
local context = {
controls = {},
root = nil -- Where the system is controlling from (Seat)
}
context.message = function(instance,name,...)
local stuff = {...}
asObject(instance,function(object)
local data = objects[object]
if not data then return end
if name == "" then name = "default" end
name = name or "default"
for _,module in pairs(modules[object.Name] or {}) do
if module.messages then
if module.messages[name] then
if objects[object] then
module.messages[name](data,instance,context,table.unpack(stuff))
end
end
end
end
end)
end
context.connected = function(wire_tag,end_tag,root,callback)
local searched = {}
local search = {root}
while #search > 0 do
local new_search = {}
for _,search_part in pairs(search) do
local connected = search_part:GetConnectedParts()
for _,part in pairs(connected) do
if not searched[part] then
searched[part] = true
if part:HasTag(wire_tag) then
table.insert(new_search,part)
if part:GetAttribute("PartId") and part:HasTag("Glow") then part.Material = Enum.Material.Neon end
end
if (not end_tag) or part:HasTag(end_tag) or part.Parent:HasTag(end_tag) then
asObject(part,function(object)
callback(part)
end)
end
end
end
end
search = new_search
end
end
local queues = {}
context.queue = function(this,tag,delta,valid)
-- Init
valid = valid or owning
local queue = queues[tag]
if queue then queue = {last = tick()} queues[tag] = queue end
if not queue[this] then queue[this] = this table.insert(queue,1,this) end
-- Get next in line
local first = queue[1]
while not valid[first] do table.remove(queue,1) first = queue[1] end -- Remove removed objects (lol)
-- Are you ready!?
if tick() - queue.last > delta then -- Aye aye captain!!
queue.last = tick() -- Oooo
table.remove(queue,1)
table.insert(queue,this)
return true -- hhhh
else
return false
end
end
context.frequency = function(this,tag,delta)
if not this[tag] then this[tag] = tick() end
if tick() - this[tag] > delta then
this[tag] = tick()
return true
else
return false
end
end
context.asObject = asObject
if _G.IsServer then
context.replicate = function(this,object,module,...)
module.replicate(this,object,context,...)
end
elseif _G.IsClient then
context.replicate = function(this,object,module,...)
if module.replicate then module.replicate(this,object,context,...) end
if tick() > module.next_replication then
module.next_replication = tick() + module.replication_delta
_G.Remotes.ObjectReplicate:FireServer(object,module.name,...)
end
end
end
local thisPlayer
if _G.IsServer then
thisPlayer = nil
elseif _G.IsClient then
thisPlayer = game.Players.LocalPlayer
end
-- Make object data from an instance (it's shared, so modules can communicate easily)
local function new(object)
local this = {}
for module,predicate in pairs(predicates) do
if predicate(object) then
local tab = modules[object.Name]
if not tab then tab = {} modules[object.Name] = tab end
tab[module] = module
end
end
for _,module in pairs(modules[object.Name] or {}) do
_G.Protected(module.new,this,object,context)
end
objects[object] = this
return this
end
local function remove(object)
local this = objects[object]
if not this then return end
for _,module in pairs(modules[object.Name] or {}) do
if module.remove then _G.Protected(module.remove,this,object,context) end
end
objects[object] = nil
owning[object] = nil
end
local own
-- Add an object to be considered by this client or server
local function add(instance,owner)
-- If this is not an instance or we already considered it then ignore
local data = objects[instance]
if data then return data end
if not instance:GetAttribute("PartId") then return end
data = new(instance)
objects[instance] = data
instance.Destroying:Connect(function()
remove(instance)
end)
if _G.IsServer then
if owner then
data.owner = owner
_G.Protected(function()
if instance:IsA("Model") then
instance.PrimaryPart:SetNetworkOwner(owner)
if instance:FindFirstChild("Hinge") then
instance.Hinge:SetNetworkOwner(owner)
end
else
instance:SetNetworkOwner(owner)
end
end)
else
own(instance)
end
else
if owner then own(instance) end
end
return data
end
local function update(instance)
local data = objects[instance]
if data then
for _,module in pairs(modules[instance.Name] or {}) do
if module.update then _G.Protected(module.update,data,instance,context) end
end
end
end
-- Make the client drop ownership and continue to run idle tasks
local function sleep(object)
if add(object) then owning[object] = nil end
end
-- Make the client own the object and take responsibility for processing
own = function(object)
if add(object) then owning[object] = objects[object] end
end
-- Get all parts connected to an instance, determine if the player is still connected in any way
local hrp = {HumanoidRootPart = true}
local function playerInSeat(seat)
if (seat:IsA("Seat") or seat:IsA("VehicleSeat")) and seat.Occupant then
return game.Players:GetPlayerFromCharacter(seat.Occupant.Parent)
else
local seat = seat:FindFirstChildWhichIsA("Seat") or seat:FindFirstChildWhichIsA("VehicleSeat")
if seat and seat.Occupant then
return game.Players:GetPlayerFromCharacter(seat.Occupant.Parent)
end
end
end
local vs = {VehicleSeat = true}
local function maxRankSeat(seat1,seat2)
if not seat1 then return seat2 end
if not seat2 then return seat1 end
local priority1 = seat1:GetAttribute("Priority") or 0
local priority2 = seat2:GetAttribute("Priority") or 0
-- A seat has priority over the other
if priority1 == priority2 then
local class1 = vs[seat1.Name]
local class2 = vs[seat2.Name]
-- One seat is a VehicleSeat and the other isn't
if class1 and not class2 then return seat1
elseif class2 and not class1 then return seat2
else
local owner1 = game.Players:GetPlayerByUserId(seat1:GetAttribute("Owner") or 0) == playerInSeat(seat1)
local owner2 = game.Players:GetPlayerByUserId(seat2:GetAttribute("Owner") or 0) == playerInSeat(seat2)
-- One player is seating in a seat that they placed themselves
if owner1 and not owner2 then return seat1
elseif owner2 and not owner1 then return seat2
else return seat1 -- Since it was probably closest
end
end
elseif priority1 > priority2 then return seat1 else return seat2 end
end
local function getParts(instance)
local searchedParts = {} -- Searched connected instance parts
local objectsFound = {} -- Found connected objects
local assemblyRoots = {} -- Found AssemblyRootParts
local playersSeated = {} -- Players that are connected to this vehicle in some way and their associated seat
-- Recursive function to look through all the parts
local function search(part)
-- DRY
local function add(instance)
searchedParts[instance] = true
local root = instance.AssemblyRootPart
if not assemblyRoots[root] then assemblyRoots[root] = root end
asObject(instance,function(object)
if instance:FindFirstChild("SeatWeld") then
local hrp = instance.SeatWeld.Part1
local player = game.Players:GetPlayerFromCharacter(hrp.Parent)
if player then
-- Players could be in multiple seats so just pick the highest one
playersSeated[player] = maxRankSeat(playersSeated[player],object)
end
end
objectsFound[object] = object
end)
end
-- If not found, add it, otherwise return
if not searchedParts[part] then
add(part)
else return end
-- For all the connected parts do the same thing, but not 'search' since it causes a bunch of excess checking
for _,part in pairs(part:GetConnectedParts(true)) do
-- If not found, add it, otherwise continue
if not searchedParts[part] then
add(part)
else continue end
-- If the part is connected to a Character
local joints = part:GetJoints()
for _,joint in pairs(joints) do
if joint:IsA("HingeConstraint") or joint:IsA("PrismaticConstraint") then
-- Also check joints since their network ownership is funky otherwise
if joint.Attachment0 and joint.Attachment1 then
search(joint.Attachment0.Parent)
search(joint.Attachment1.Parent)
end
end
end
end
end
search(instance)
return objectsFound,playersSeated,assemblyRoots
end
-- Out of a list of player-seats get the highest priority player seated (nil == server)
local function maxPlayerSeated(playersSeated)
local maxPlayer,maxSeat = nil
for player,seat in pairs(playersSeated) do
if maxRankSeat(maxSeat,seat) then maxPlayer = player maxSeat = seat end
end
return maxPlayer
end
local function mark(object)
if object:FindFirstChild("Mark") then object.Mark:Destroy() end
local box = Instance.new("SelectionBox")
box.Parent = object
box.Adornee = object
box.Name = "Mark"
return box
end
-- This sort of runs on every client, so maybe it's far too slow?
processOwnership = function(seat)
local objectsFound,playersSeated,assemblyRoots = getParts(seat)
local ownerPlayer = maxPlayerSeated(playersSeated)
-- The server needs to change the network owner in this case
if _G.IsServer then
_G.Remotes.OwnershipReplicate:FireAllClients(seat)
for _,root in pairs(assemblyRoots) do
local success,result = pcall(function()
root:SetNetworkOwner(ownerPlayer)
end)
if not success then warn(result) end
end
for object in pairs(objectsFound) do
local data = objects[object]
if data then data.owner = ownerPlayer end
end
if ownerPlayer then
for object in pairs(objectsFound) do
sleep(object)
end
end
end
if ownerPlayer == thisPlayer then
for _,object in pairs(objectsFound) do -- Connected and Owning
--if _G.IsClient then mark(object).Color3 = Color3.fromRGB(0,255,0) end
own(object)
end
elseif playersSeated[thisPlayer] then -- Connected and not Owning
for _,object in pairs(objects) do
if objectsFound[object] then
--if _G.IsClient then mark(object).Color3 = Color3.fromRGB(255,0,0) end
sleep(object)
end
end
elseif _G.IsClient then -- Disconnected and not Owning
for object,data in pairs(objects) do
if objectsFound[object] then
--if _G.IsClient then mark(object).Color3 = Color3.fromRGB(0,0,255) end
remove(object)
end
end
end
if _G.IsClient then
local ourSeat = playersSeated[thisPlayer]
if ourSeat then
input.seat = ourSeat
context.seat = ourSeat
elseif input.seat == asObject(seat,function(...) return ... end) then
input.seat = nil
context.seat = nil
end
end
end
local function objectsProcess(delta)
context.delta = delta
context.cursor = input.cursor
for object,data in pairs(owning) do
for _,module in pairs(modules[object.Name] or {}) do
if module.run then if not module.run(data,object,context) then remove(object) end end
end
end
for object,data in pairs(objects) do
for _,module in pairs(modules[object.Name] or {}) do
if module.idle then if not module.idle(data,object,context) then remove(object) end end
end
end
for _,module in pairs(modulesByName) do
if module.loop then
module.loop(context)
end
end
end
if _G.IsServer then
-- Clients and servers will send this to update information between each other (should be handled by values)
_G.Remotes.ObjectReplicate.OnServerEvent:Connect(function(player,object,name,...)
local data = objects[object]
if data and data.owner == player then
modulesByName[name].replicate(data,object,context,...)
end
end)
game.Players.PlayerRemoving:Connect(function(player)
for object,data in pairs(objects) do
if data.owner == player then
own(object)
end
end
end)
elseif _G.IsClient then
-- Respond to processing events from the server
_G.Remotes.OwnershipReplicate.OnClientEvent:Connect(function(seat)
processOwnership(seat)
end)
end
for _,instance in pairs(script.Parent.Objects:GetChildren()) do
local module = require(instance)
modulesByName[instance.Name] = module
module.name = instance.Name
module.next_replication = tick()
module.replication_delta = module.replication_delta or 0.5
if module.manifest.predicate then
predicates[module] = module.manifest.predicate
else
for _,name in pairs(module.manifest) do
modules[name] = modules[name] or {}
table.insert(modules[name],module)
end
end
end
if _G.IsServer then
for _,instance in pairs(game.Workspace.Objects:GetChildren()) do
add(instance)
end
end
spawn(function()
_G.Run.Heartbeat:Connect(function(delta)
if _G.IsClient then context.controls = input.values end
objectsProcess(delta)
end)
end)
return {processOwnership = processOwnership, add = add, update = update, own = own}
+242
View File
@@ -0,0 +1,242 @@
require(game.ReplicatedFirst.ReadyModule)
-- This is a script that handles a tool for creating objects
local player = game.Players.LocalPlayer
-- Gui Objects
local paintFrame = _G:WaitFor("PaintFrame")
--local paintSelection = paintFrame:WaitForChild("ScrollingFrame")
--local paintDefault = paintSelection:WaitForChild("Default")
local userInputEvent = nil
local renderEvent = nil
local mouseModule = require(_G.Modules.MouseModule)
local currentColor = Color3.new(1, 1, 1)
local lastPainted = nil
local input = game:GetService("UserInputService")
local partStateModule = require(_G.Modules.PartStateModule)
local partState = partStateModule.new()
local paintFrame = _G:WaitFor("PaintFrame")
local colorPickerFrame = paintFrame.ColorPickerFrame
local colorSelectorFrame = paintFrame.ColorSelectorFrame
local colorLabel = paintFrame.ColorLabel
local materialSelectorFrame = paintFrame.MaterialSelectorFrame
local materialLabel = paintFrame.MaterialLabel
local polyFrame = paintFrame.PolyFrame
local polyLabel = paintFrame.PolyLabel
-- local label = game:GetService("Selection"):Get()[1] for name,id in ([[DiamondPlate7546654401Metal7547178395Wood7547190453WoodPlanks7547301709SmoothPlastic Rubber14108673018Neon Glass7547304577]]):gmatch("([a-zA-Z]+)([0-9]*)") do local l = label:Clone() l.Parent = label.Parent l.Image = "rbxassetid://"..id l.Name = name end
local hues = {}
local hue = Color3.new(0,0,1)
local finalColor = nil
local function makeColorGradient()
local n = tonumber
local t = {}
for r,g,b in ("100101001011010110100"):gmatch("(.)(.)(.)") do
local color = Color3.new(n(r),n(g),n(b))
--table.insert(t,ColorSequenceKeypoint.new(#t/7,color))
table.insert(hues,color)
end
--return ColorSequence.new(t)
end
makeColorGradient()
local function getHue(value)
local index1 = math.clamp(math.floor(value*6)+1,1,7)
local index2 = math.clamp(math.floor(value*6)+2,2,7)
local a = hues[index1]
local b = hues[index2]
local alpha = value*6 - math.floor(value*6)
print(value,index1,index2,alpha)
return a:Lerp(b,alpha)
end
local function pickerFactory(object,callback,use_x,use_y)
local held
local inputs = {[Enum.UserInputType.MouseButton1]=true,[Enum.UserInputType.Touch]=true,[Enum.UserInputType.MouseMovement]=true}
local function changed(input)
if input == held or (held and input.UserInputType == Enum.UserInputType.MouseMovement) then
local p = object.Pointer.Position
local s = object.Pointer.AbsoluteSize
local x_scale,y_scale,x_offset,y_offset,x_value,y_value
if use_x then
x_scale = math.clamp((input.Position.X - object.AbsolutePosition.X) / (object.AbsoluteSize.X + s.X),0,object.AbsoluteSize.X/(object.AbsoluteSize.X + s.X))
x_value = math.clamp((input.Position.X - object.AbsolutePosition.X) / (object.AbsoluteSize.X),0,1)
x_offset = s.X/2
else
x_scale = p.X.Scale
x_offset = p.X.Offset
end
if use_y then
y_scale = math.clamp((input.Position.Y - object.AbsolutePosition.Y) / (object.AbsoluteSize.Y + s.Y),0,object.AbsoluteSize.Y/(object.AbsoluteSize.Y + s.Y))
y_value = math.clamp((input.Position.Y - object.AbsolutePosition.Y) / (object.AbsoluteSize.Y),0,1)
y_offset = s.Y/2
else
y_scale = p.Y.Scale
y_offset = p.Y.Offset
end
object.Pointer.Position = UDim2.new(x_scale,x_offset,y_scale,y_offset)
callback(x_value,y_value)
end
end
object.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
held = input
changed(input)
end
end)
object.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
held = nil
end
end)
input.InputChanged:Connect(changed)
end
local saturation = 0
local brightness = 0
local function updateColor()
finalColor = hue:Lerp(Color3.new(1,1,1),saturation):Lerp(Color3.new(0,0,0),brightness)
colorSelectorFrame.Preview.BackgroundColor3 = finalColor
colorLabel.CancelButton.Visible = true
end
pickerFactory(colorPickerFrame.Hue,function(_,y)
local color = getHue(y)
hue = color
colorPickerFrame.Picker.BackgroundColor3 = color
updateColor()
end,false,true)
pickerFactory(colorPickerFrame.Picker,function(x,y)
saturation = 1-x
brightness = y
updateColor()
end,true,true)
local colorPicking = false
colorSelectorFrame.PickButton.Activated:Connect(function()
colorPicking = true
colorSelectorFrame.PickButton.BackgroundColor3 = Color3.new(0.5,1,0.5)
end)
colorLabel.CancelButton.Activated:Connect(function()
colorLabel.CancelButton.Visible = false
finalColor = nil
end)
local finalMaterial = nil
local materialStroke = materialSelectorFrame:WaitForChild("UIStroke")
materialStroke.Enabled = true
materialStroke.Parent = nil
for _,item in pairs(materialSelectorFrame:GetChildren()) do
if item:IsA("ImageButton") then
item.Activated:Connect(function()
finalMaterial = Enum.Material[item.Name]
materialLabel.CancelButton.Visible = true
materialStroke.Parent = item
end)
end
end
materialLabel.CancelButton.Activated:Connect(function()
materialLabel.CancelButton.Visible = false
materialStroke.Parent = nil
finalMaterial = nil
end)
local finalShift = nil
local finalThickness = nil
polyFrame.ShiftButton.Activated:Connect(function()
if not finalShift then finalShift = 1
elseif finalShift == 1 then finalShift = -1
elseif finalShift == -1 then finalShift = nil end
if not finalShift then polyFrame.ShiftButton.Text = "Shift Off"
elseif finalShift == -1 then polyFrame.ShiftButton.Text = "Shift Out"
elseif finalShift == 1 then polyFrame.ShiftButton.Text = "Shift In" end
polyLabel.CancelButton.Visible = true
end)
polyFrame.ThicknessBox.FocusLost:Connect(function(enter)
if enter then
finalThickness = tonumber(polyFrame.ThicknessBox.Text) or nil
if finalThickness < 0.05 then finalThickness = 0.05 end
local text = ""
if finalThickness then text = tostring(finalThickness).." studs" end
polyFrame.ThicknessBox.Text = text
end
polyLabel.CancelButton.Visible = true
end)
polyLabel.CancelButton.Activated:Connect(function()
polyLabel.CancelButton.Visible = false
finalShift = nil
finalThickness = nil
polyFrame.ShiftButton.Text = "Shift Off"
polyFrame.ThicknessBox.Text = ""
end)
polyLabel.CancelButton.Visible = false
colorLabel.CancelButton.Visible = false
materialLabel.CancelButton.Visible = false
local function render(delta)
local result = mouseModule.getMouseHit()
if result and result.Instance then
local object = _G.Methods.getObjectFromPart(result.Instance,player)
if object then script.SelectionBox.Adornee = object else script.SelectionBox.Adornee = nil end
script.SelectionBox.FillTransparency = 1
if not mouseModule.isMouseDown() then return end
script.SelectionBox.FillTransparency = 0.5
if colorPicking then
finalColor = result.Instance.Color
colorSelectorFrame.Preview.BackgroundColor3 = finalColor
colorPicking = false
colorSelectorFrame.PickButton.BackgroundColor3 = Color3.new(1,1,1)
if object then lastPainted = object end
return
end
if lastPainted == object then return end
lastPainted = object
if not lastPainted then return end
local state = partStateModule.new()
if finalColor then state.color = finalColor end
if finalShift then state.look = game.Workspace.CurrentCamera.CFrame.LookVector state.shift = finalShift end
if finalThickness then state.size = Vector3.new(finalThickness,0,0) end
if finalMaterial then state.material = finalMaterial end
_G.Remotes.Apply:FireServer(lastPainted,state:serialise())
else
lastPainted = nil
script.SelectionBox.Adornee = nil
end
end
paintFrame.Visible = false
local function init(s)
if renderEvent then renderEvent:Disconnect() end
s.Parent.Equipped:Connect(function()
if renderEvent then renderEvent:Disconnect() end
renderEvent = game:GetService("RunService").RenderStepped:Connect(render)
paintFrame.Visible = true
end)
s.Parent.Unequipped:Connect(function()
if renderEvent then renderEvent:Disconnect() end
paintFrame.Visible = false
script.SelectionBox.Adornee = nil
end)
end
return {init = init}
+14
View File
@@ -0,0 +1,14 @@
-- module which serves the purpose of providing a universal function so that I can change how checks for whether a player is allowed to activate something works (such as collaborative building)
local module = {}
module.tagService = _G.Tags
function module.activator(realScript,callback)
local clickDetector = realScript.Parent:FindFirstChildWhichIsA("ClickDetector")
clickDetector.MouseClick:Connect(function(player)
if module.tagService:HasTag(realScript.Parent,"P_"..player.UserId) or module.tagService:HasTag(realScript.Parent.Parent,"P_"..player.UserId) then
callback(player)
end
end)
end
return module
+508
View File
@@ -0,0 +1,508 @@
local module = {}
-- PARTS TABLE
local parts = {}
_G.Storage:WaitForChild("Parts")
local function added(object)
if object.Parent:IsA("Folder") then
local id = object:GetAttribute("PartId")
if id then parts[id] = object end
end
end
for _,object in pairs(_G.Storage.Parts:GetDescendants()) do
added(object)
end
_G.Storage.Parts.DescendantAdded:Connect(function(object)
if object:GetAttribute("PartId") then
warn(string.format("%i:%s, you are late!",object:GetAttribute("PartId"),object:GetFullName()))
end
added(object)
end)
function module.get_template_part(part)
local id = part:GetAttribute("PartId")
if not id then return end
local template = parts[id]
return template
end
function module.get_part_by_id(id)
return parts[id]
end
function module.change_material(part,material)
if part.Material == Enum.Material.Glass then
part.Transparency = part:GetAttribute("OriginalTransparency",part.Transparency)
end
if material == Enum.Material.Glass then
part:SetAttribute("OriginalTransparency",part.Transparency)
part.Transparency = 0.5
end
part.Material = material
end
-- PART METHODS
function module.apply_material(part,material)
if material then
part:SetAttribute("Material",material.Name)
if part:IsA("Model") then
for _,subpart in pairs(part:GetDescendants()) do
if subpart:IsA("BasePart") then
module.change_material(subpart,material)
end
end
else
module.change_material(part,material)
end
end
end
function module.apply_edit(part,index,value)
part[index].Value = value
end
function module.get_size(part)
local nodes = module.get_nodes_class(part)
if nodes == 1 then
return part:GetAttribute("Size")
elseif nodes == 2 then
return Vector3.new(part.Size.Z,0,0) -- Yeah the bloody irony I know right?
elseif nodes >= 3 then
return Vector3.new(part:FindFirstChildWhichIsA("Part").Size.X,0,0)
end
end
function module.get_thickness(part)
return module.get_size(part).X -- WHAT?!
end
function module.apply_size(part,size)
part:SetAttribute("Size",size)
local nodes = module.get_nodes_class(part)
if nodes == 1 then
if part:IsA("Model") then
part:ScaleTo(size.X)
elseif part:IsA("BasePart") then
if size.Y == 0 or size.Z == 0 then return end -- Since this is not for normal parts it's a poly size
part.Size = size
end
elseif nodes == 2 then
part.Size = Vector3.new(part.Size.X,size.X,size.X) -- Yeah, this is serious
elseif nodes >= 3 then
for _,item in pairs(part:GetChildren()) do
item.Size = Vector3.new(size.X,item.Size.Y,item.Size.Z)
end
end
end
function module.apply_color(part,color)
part:SetAttribute("Color",color)
if part:IsA("Model") then
for _,subpart in pairs(part:GetDescendants()) do
if subpart:IsA("BasePart") then
subpart.Color = color
end
end
else
part.Color = color
end
end
local function Weld(part1,part2,temporary) -- Make a BUILDING weld
local weld = Instance.new("WeldConstraint")
weld.Name = "BuiltWeldConstraint"
weld.Parent = part1
weld.Part0 = part1
weld.Part1 = part2
if temporary then weld.Name = "WeldConstraint" end -- Or not.
return weld
end
function module.weld(part,connection,temporary)
local attach = part
if part:IsA("Model") then attach = attach.PrimaryPart end
local new_weld = Weld(attach,connection,temporary)
new_weld.Parent = part
return new_weld
end
function module.form_triangle(pa,pb,p1,p2,p3,s2)
local s1 = pa.Size.X
s2 = s2 or s1*0.5
s2 = 0
local p12 = p2-p1
local p23 = p3-p2
local p13 = p3-p1
local p12l = p12.Magnitude
local p23l = p23.Magnitude
local p13l = p13.Magnitude
local bottom
local bot_len
local totop
local root
local tip
local far
local t
if p12l > p23l and p12l > p13l then -- TRUE: p12 largest FALSE: p12 smaller than p23l or p13l
tip = p3
root = p1
far = p2
bottom = p12
bot_len = p12l
totop = p13
--[[pa.Color = Color3.new(1,0,0)
pb.Color = Color3.new(1,0,0)]]
elseif p23l > p13l then -- TRUE: p23 must be largest FALSE: p13 largest
tip = p1
root = p2
far = p3
bottom = p23
bot_len = p23l
totop = -p12
--[[pa.Color = Color3.new(0,1,0)
pb.Color = Color3.new(0,1,0)]]
else -- p13 largest
tip = p2
root = p1
far = p3
bottom = p13
bot_len = p13l
t = pa
pa = pb
pb = t
--[[pa.Color = Color3.new(0,0,1)
pb.Color = Color3.new(0,0,1)]]
totop = p12
end
bottom = bottom.Unit
local coeff = totop:Dot(bottom)
local mid = root+coeff*bottom
local cross = totop:Cross(bottom).Unit
local top = (mid-tip)
pa.Size = Vector3.new(s1,top.Magnitude,coeff)
pb.Size = Vector3.new(s1,top.Magnitude,bot_len - coeff)
top = top.Unit
pa.CFrame = CFrame.fromMatrix(s2*cross+(root+tip)*0.5,cross,-top,bottom)
pb.CFrame = CFrame.fromMatrix(s2*cross+(tip+far)*0.5,-cross,-top,-bottom)
end
function module.get_tri_positions1(pa,pb)
local ca = pa.CFrame
local ra = ca.Rotation
local cb = pb.CFrame.Position
local sa = pa.Size
local lb = pb.Size.Z
local lh = sa.Y
local fb = Vector3.new(0,lh,-lb)
return (cb - ra * (fb * 0.5)) -- FOR THIRD RETURN VALUE OF BELOW
end
function module.get_tri_positions2(pa,pb)
local ca = pa.CFrame
local ra = ca.Rotation
ca = ca.Position
local sa = pa.Size
local la = sa.Z
local lh = sa.Y
local fa = Vector3.new(0,lh,la)
return (ca + ra * (fa * -0.5)),(ca + ra * (fa * 0.5))
end
-- apply data positions of parts
local form_triangle = module.form_triangle
local number_to_string = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}
local string_to_number = {}
for num,str in pairs(number_to_string) do
string_to_number[str] = num
end
--; for i = 1,10 do number_to_string[i] = tostring(i) end
local mod = CFrame.Angles(0,0,0)
function module.apply_nodes(part,nodes)
if #nodes < module.get_nodes_class(part) then return true end
if #nodes == 1 then
part:PivotTo(nodes[1])
elseif #nodes == 2 then
part.CFrame = nodes[1]*mod
part.Size = Vector3.new(nodes[2],part.Size.Y,part.Size.Z)
local first = part:FindFirstChild("1")
local second = part:FindFirstChild("2")
local p1,p2 = module.get_stick_ends(part)
local offset = Vector3.new(nodes[2]*0.5,0,0)
if first then
first.Position = nodes[1]:PointToWorldSpace(offset)
if first:FindFirstChild("WeldConstraint") then
first.WeldConstraint.Enabled = true
end
end
if second then
second.Position = nodes[1]:PointToWorldSpace(-offset)
if second:FindFirstChild("WeldConstraint") then
second.WeldConstraint.Enabled = true
end
end
elseif #nodes >= 3 then
local template = part["A"]
for i=1,(#nodes-2)*2 do
local name = number_to_string[i]
if not part:FindFirstChild(name) then
local piece = template:Clone()
piece.Parent = part
piece.Name = name
end
end
for i=1,(#nodes-2) do
form_triangle(part[number_to_string[i*2-1]],part[number_to_string[i*2]],nodes[1],nodes[i+1],nodes[i+2])
end
end
end
-- get the poly 'type' of a part
function module.get_nodes_class(part)
return part:GetAttribute("Nodes") or 1
end
-- get data positions of parts
function module.get_nodes(part)
local nodes = part:GetAttribute("Nodes")
if not nodes then
return {part:GetPivot()}
elseif nodes == 2 then
local first = part:FindFirstChild("1")
local second = part:FindFirstChild("2")
if first and second then
local offset = second.Position - first.Position
return {CFrame.lookAt(first.Position,second.Position,first.CFrame.UpVector)*CFrame.Angles(0,math.pi*0.5,0) + offset * 0.5,offset.Magnitude}
else
return {part.CFrame,part.Size.X}
end
elseif nodes >= 3 then
local i = -1
local p1 = part["A"]
local p2 = part["B"]
local t = {module.get_tri_positions2(p1,p2)}
while true do
i = i + 2
local pn1 = part:FindFirstChild(number_to_string[i])
if not pn1 then return t end
local pn2 = part:FindFirstChild(number_to_string[i+1])
if not pn2 then return t end
table.insert(t,module.get_tri_positions1(p1,p2))
end
return t
end
end
-- shift a poly part along its normal axis while keeping static node positions
function module.apply_shift(part,shift)
if module.get_nodes_class(part) < 3 then return end
local previous = part:GetAttribute("Shift") or 0
if shift == 0 and previous == 0 then part:SetAttribute("Shift",nil) return end
part:SetAttribute("Shift",shift)
shift = shift - previous
for _,item in pairs(part:GetChildren()) do
if item:IsA("BasePart") and not item:HasTag("Node") then
local neg = (string_to_number[item.Name] % 2 == 0)
if neg then neg = -1 else neg = 1 end
item.Position = item.Position + item.CFrame.RightVector * item.Size.X * 0.5 * shift * neg
end
end
end
-- stupid name, it just tries to shift in the direction based on how we're looking
function module.attempt_shift(part,shift,look)
local template = part:FindFirstChild("A")
if not template then return end
module.apply_shift(part,math.sign(template.CFrame.RightVector:Dot(look)) * shift)
end
-- get the cframe of a single connection
function module.eval_connection(connection)
local connected = connection.connected
local relative = CFrame.identity
if connected then relative = connected:GetPivot() end
return relative:ToWorldSpace(connection.CFrame)
end
function module.get_stick_ends(part)
local nodes = module.get_nodes(part)
if #nodes ~= 2 then error("Part is not a stick",2) end
local cframe = nodes[1]
local offset = Vector3.new(nodes[2]*0.5,0,0)
return cframe:PointToWorldSpace(offset),cframe:PointToWorldSpace(-offset)
end
function module.get_points(part)
local class = module.get_nodes_class(part)
if class == 1 then return {}
elseif class == 2 then return {module.get_stick_ends(part)}
else return module.get_nodes(part) end
end
-- apply driven connections by attachments in the placement engine
function module.eval_connections(part,connections)
local class = module.get_nodes_class(part)
local cframes = {}
for _,connection in ipairs(connections) do
table.insert(cframes,module.eval_connection(connection))
end
local nodes = {}
if class == 1 then
nodes = cframes
elseif class == 2 then
if #cframes == 2 then
local to = cframes[2].Position - cframes[1].Position
nodes = {CFrame.lookAt(cframes[1].Position,cframes[2].Position,cframes[1].UpVector)*CFrame.Angles(0,math.pi*0.5,0) + (to * 0.5), to.Magnitude}
end
elseif class == 3 then
for _,cframe in ipairs(cframes) do table.insert(nodes,cframe.Position) end
end
return nodes
end
function module.apply_internal_welds(part)
if part:IsA("Model") then
local main = part.PrimaryPart
for _,item in pairs(part:GetChildren()) do
if item:IsA("BasePart") and item ~= main then Weld(item,main,true) end
end
end
end
-- PART STATE METHODS
local _state = {}
_state.__index = _state
function _state.apply_material(this)
if this.material then module.apply_material(this.part,this.material) end
end
function _state.apply_size(this)
if this.size then module.apply_size(this.part,this.size) end
end
function _state.apply_color(this)
if this.color then module.apply_color(this.part,this.color) end
end
function _state.apply_edit(this,index,value)
local edit = this.part:FindFirstChild(index)
if not edit then error("Could not find edit "..index,2) end
module.apply_edit(this.part,index,value)
end
function _state.apply_edits(this)
for index,value in pairs(this.edits) do
this:apply_edit(index,value)
end
end
function _state.attempt_shift(this,...)
module.attempt_shift(this.part,...)
end
function _state.apply_internal_welds(this)
if module.get_nodes_class(this.part) > 1 then -- Weld together parts inside of poly
module.apply_internal_welds(this.part)
end
end
function _state.apply_welds(this)
this:apply_internal_welds()
for index,connection in pairs(this.welds) do
local target = this.part
if connection.name then target = this.part[connection.name] end
if target:IsA("Model") then target = target.PrimaryPart end
if (not this.owner) or (_G.Methods.getOwnedOrNil(this.owner,connection.part)) then
module.weld(target,connection.part)
end
end
end
function _state.apply_connections(this)
this.nodes = module.eval_connections(this.part,this.connections)
end
function _state.apply_nodes(this)
module.apply_nodes(this.part,this.nodes)
end
function _state.apply_shift(this)
if this.look then this:attempt_shift(this.shift or 0,this.look)
else module.apply_shift(this.part,this.shift or 0) end
end
function _state.apply(this,part,options)
options = options or {}
this.part = part or this.part
if this.color then this:apply_color() end
if this.size then this:apply_size() end
if this.material then this:apply_material() end
if this.edits and not options.no_edit then this:apply_edits() end
if this.connections then this:apply_connections() end
if this.nodes then this:apply_nodes() end
if this.welds and not options.no_weld then this:apply_welds() else this:apply_internal_welds() end
if this.shift then this:apply_shift() end
end
function _state.create(this)
if not this.id then error("No id",2) end
local template = parts[this.id]:Clone()
template.Parent = game.Workspace.Objects
this:apply(template)
return template
end
function _state.get_nodes_class(this)
return module.get_nodes_class(this.part)
end
function _state.take(this,existing)
this.color = existing:GetAttribute("Color")
this.material = existing:GetAttribute("Material")
this.size = existing:GetAttribute("Size")
this.shift = existing:GetAttribute("Shift")
this.edits = {}
for _,edit in existing:GetChildren() do
if edit:HasTag("Edit") then
if edit.Value ~= (edit:GetAttribute("Default") or nil) then
this.edits[edit.Name] = edit.Value
end
end
end
this.nodes = module.get_nodes(existing)
return this
end
function _state.update(this)
this:take(this.part)
return this
end
function _state.copy(this,existing)
this:take(existing)
this.id = existing:GetAttribute("PartId")
this.part = parts[this.id]:Clone()
this:apply(nil,{no_weld = true})
return this
end
function module.copy(existing)
return module.new():copy(existing)
end
function module.new(original)
return setmetatable(_G.Union({
material = nil, -- Enum.Material SERIAL
color = nil, -- Color3.new() SERIAL
size = nil, -- Vector3.new() SERIAL
shift = nil, -- 1 -> 0 -> -1 SERIAL
connections = {}, -- {[n]={cframe=,connection=}} for placement engine etc
welds = {}, -- {{name=,touched=},...},... SERIAL
edits = {}, -- ? SERIAL
owner = nil, -- player
part = nil, -- part being worked with (applied)
id = nil -- part id SERIAL
},original or {}),_state)
end
-- SERIALISATION
--[[local function serialisation_factory(match_string)
local serialisation_table = {}
for index in match_string:gmatch("[^,]+") do
table.insert(serialisation_table,index)
end
return {
serialise = function(this)
local t = {}
for _,key in pairs(serialisation_table) do
table.insert(t,this[key])
end
return table.unpack(t)
end,
deserialise = function(this,...)
local t = {...}
for index,key in pairs(serialisation_table) do
this[key] = t[index]
end
end,
}
end
local state_serialiser = serialisation_factory("id,connections,color,size,shift,material,edits,welds")]]
_state.serialise = function(this) --state_serialiser.serialise
local color
if this.color then color = {this.color.R,this.color.G,this.color.B} end
return this.id,this.connections,color,this.size,this.shift,this.material,this.edits,this.welds,this.look
end
_state.deserialise = function(this,id,connections,color,size,shift,material,edits,welds,look) --state_serialiser.deserialise
if color then this.color = Color3.new(table.unpack(color)) end
this.id = id
this.connections = connections
this.size = size
this.shift = shift
this.material = material
this.edits = edits
this.welds = welds
this.look = look
end
return module
+627
View File
@@ -0,0 +1,627 @@
local run = game:GetService("RunService")
local inputService = game:GetService("UserInputService")
local collectionService = _G.Tags
local mouseModule = require(_G.Modules.MouseModule)
local methodModule = require(_G.Modules.MethodModule)
local partStateModule = require(_G.Modules.PartStateModule)
local selectionBox = script.SelectionBox
local adjacentBox = script.AdjacentBox
local statGui
if game.Players.LocalPlayer then
statGui = game.Players.LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("StatGui")
end
local gui
local current = nil
local mode = "place"
local realMode = "place"
local rotSnap = 15
local rotSnapKey = 45.
local moveSnap = 0.5
local modelScaleSnap = 0.1
local touchModeConnect = true
local touchingBoxes = {}
local controlModifier = false -- embed
local shiftModifier = false -- lower snap
local ModeButtons = {
clone = nil,
resize = nil
}
local pointer = nil
local function temp()
local a = Instance.new("Part")
a.Parent = game.Workspace
a.Size = Vector3.new(1,1,1)
a.Anchored = true
a.CanCollide = false
a.Transparency = 0.5
a.Material = Enum.Material.Neon
return a
end
local a = temp()
local b = temp()
local modeEnd = function()
if current.axes then current.axes:Destroy() end
end
local focusAxes = function()
current.axes:ScaleTo(current.size.Magnitude*1.3)
methodModule.setItemCFrame(current.axes,current.cframe)
for _,part in pairs(current.axes:GetChildren()) do
if not current.axis then
part.Transparency = 0.5
else
part.Transparency = 1
end
end
if current.axis then current.axis.Transparency = 0 end
end
local createAxes = function(name)
current.axes = _G.Storage.Assets[name]:Clone()
current.axes.Parent = game.Workspace
current.axes.PrimaryPart.Shape = Enum.PartType.Ball
focusAxes()
end
local getBounds = function()
local cframe,size
if current.model:IsA("Model") then
cframe,size = current.model:GetBoundingBox()
else
cframe = current.model.CFrame
size = current.model.size
end
current.cframe = cframe
current.size = size
end
local styleModel = function()
local function style(part)
part.Anchored = true
part.CanCollide = false
end
if current.model:IsA("BasePart") then style(current.model) end
for _,part in pairs(current.model:GetChildren()) do
if part:IsA("BasePart") then
style(part)
end
end
end
local distanceAlong = function(o : Vector3,n : Vector3,p : Vector3) -- origin, normal, point
local k = (n.X*(p.X-o.X)+n.Y*(p.Y-o.Y)+n.Z*(p.Z-o.Z))--/(n.x*n.x+n.y*n.y+n.z*n.z) -- is 1
return (n*k+o-p).Magnitude
end
local processPointer = function(inputObject)
local position = inputObject.Position
local result,ray = mouseModule.getMouseHit() -- get whatever we're hovering
current.location = position
current.ray = ray
current.result = result
end
local function axisClick(inputObject) -- For whenever we click in a mode supporting axis
if current.axes then -- if there are axes (there should be)
processPointer(inputObject)
if current.result then
if current.result.Instance.Parent == current.axes then -- if it's an axis
current.axis = current.result.Instance -- set it
pointer = inputObject -- set the inputObject for current selection
end
end
end
end
local function round(v : Vector3,snap) -- just round a vector based on the snapping
snap = snap or moveSnap
return Vector3.new(math.round(v.X/snap)*snap,math.round(v.Y/snap)*snap,math.round(v.Z/snap)*snap)
end
local function mask(a : Vector3,b : Vector3) -- multiply each axis together
return Vector3.new(a.X*b.X,a.Y*b.Y,a.Z*b.Z)
end
local function clamp(a : Vector3, min : Vector3, max : Vector3)
return Vector3.new(math.clamp(a.X,min.X,max.X),math.clamp(a.Y,min.Y,max.Y),math.clamp(a.Z,min.Z,max.Z))
end
local function rayProjectToRay(ray,axis,root)
local n2 = ray.Direction
local o2 = ray.Origin
local n1 = (axis.Position - axis.Parent.PrimaryPart.Position).Unit
local o1 = root or current.axis.Position
-- using more vector magic to find the closest point on the axis line to the ray of the pointer
local s = n2:Dot(n1)
return o1+n1*((n1:Dot(o2-o1)+s*(n2:Dot(o1-o2)))/(1-s*s))
end
local function axisDragClick(inputObject)
axisClick(inputObject)
if current.axis then
local p = rayProjectToRay(current.ray,current.axis)
current.modelOrigin = current.touchingCFrame
current.dragOrigin = current.axes.PrimaryPart.CFrame:PointToObjectSpace(p)
current.axisOrigin = current.touching.CFrame:ToObjectSpace(current.axis.CFrame)
end
end
local function rayProjectToPlane(ray,axis)
-- using some vector magic to find the point of intersection between the ray of the pointer and the plane of rotation (arc handle plane)
return ray.Origin+(axis.CFrame.RightVector:Dot(axis.Position-ray.Origin))/(axis.CFrame.RightVector:Dot(ray.Direction))*ray.Direction
end
local function axisRotateClick(inputObject)
axisClick(inputObject)
if current.axis then
local p = rayProjectToPlane(current.ray,current.axis)
current.dragOrigin = current.axis.CFrame:PointToObjectSpace(p)
current.modelOrigin = current.touchingCFrame
current.axisOrigin = current.touching.CFrame:ToObjectSpace(current.axis.CFrame)
current.initialRotation = current.rotation
end
end
local modeStart = {}
modeStart.clone = function()
modeEnd()
end
modeStart.rotate = function()
modeEnd()
createAxes("RotateAxes")
end
modeStart.resize = function()
modeEnd()
createAxes("ResizeAxes")
for _,item in pairs(current.axes:GetChildren()) do
local axis = item.Name:match("Part(.)")
if axis then
if (current.scaleTerms or Vector3.one)[axis] == 0 then axis:Destroy() end -- if the scale terms say that this axis should not be scaled
if current.model:IsA("Model") then item.Color = Color3.fromRGB(13, 105, 172) end
end
end
end
modeStart.move = function()
modeEnd()
createAxes("MoveAxes")
end
modeStart.place = function()
modeEnd()
end
local function setMode(newMode)
for _,other_button in pairs(_G.PlaceModeFrame:GetChildren()) do
if other_button:IsA("TextButton") then
other_button.BackgroundColor3 = Color3.new(1,1,1)
if (current.disabled or {})[other_button.Name] then other_button.Visible = false else other_button.Visible = true end
end
end
realMode = newMode
mode = newMode
gui.Confirm.Text = "Place"
if not current.touching and newMode ~= "place" then
mode = "place"
gui.Confirm.Text = "Confirm"
end
_G.PlaceModeFrame[realMode].BackgroundColor3 = Color3.new(1, 0.956863, 0.345098)
_G.PlaceModeFrame[mode].BackgroundColor3 = Color3.new(0.6, 1, 0.333333)
modeStart[mode]()
end
local function confirmPlace(inputObject,forced)
if not current then return end
if realMode ~= mode then setMode(realMode) return end
if (inputObject and inputObject.UserInputType == Enum.UserInputType.MouseButton1) or not inputObject then
current:confirm(forced)
if not current then return end
methodModule.setItemCFrame(current.model,CFrame.new())
current.touching = nil
current.touchingCFrame = CFrame.new()
setMode(realMode)
end
end
local modeSnap = {
}
local modeClick = {}
modeClick.place = function(inputObject)
confirmPlace(inputObject,false)
end
modeClick.clone = function(inputObject)
setMode("place")
end
modeClick.move = function(inputObject)
axisDragClick(inputObject)
current.moveOffset = current.newMoveOffset or Vector3.zero
end
modeClick.rotate = axisRotateClick
modeClick.resize = function(inputObject)
axisDragClick(inputObject)
current.startSize = current.size
if current.model:IsA("Model") then current.startScale = current.model:GetScale() end
end
local modeLoop = {}
modeLoop.default = function()
if current.touching and current.touching:IsA("BasePart") then
methodModule.setItemCFrame(current.model,current.touching.CFrame:ToWorldSpace(current.touchingCFrame or CFrame.identity))
end
statGui.Enabled = not not statGui.Adornee
getBounds()
end
modeLoop.clone = function()
current:clone(current.touching)
modeLoop.default()
end
modeLoop.rotate = function()
modeLoop.default()
focusAxes()
if pointer then
processPointer(pointer)
local p = rayProjectToPlane(current.ray,current.axis)
local plane = current.touching.CFrame:ToWorldSpace(current.axisOrigin)
local toDrag = plane.Rotation:PointToWorldSpace(current.dragOrigin)
local toPointer = p-plane.Position
local angle = math.round(toDrag:Angle(toPointer,plane.RightVector)/math.rad(rotSnap))*math.rad(rotSnap)
local modelCFrame = current.touching.CFrame:ToWorldSpace(current.modelOrigin) -- cframe of axes/object in the world
local newCFrame = plane.Rotation:ToWorldSpace(CFrame.Angles(angle,0,0)*plane.Rotation:ToObjectSpace(modelCFrame.Rotation)) + modelCFrame.Position -- put it back into object space
current.rotation = current.initialRotation * newCFrame:ToObjectSpace(modelCFrame):Inverse()
current.touchingCFrame = current.touching.CFrame:ToObjectSpace(newCFrame) -- need to add rotation
statGui.TextLabel.Text = tonumber(math.round(math.deg(angle)*1000)/1000)
statGui.Adornee = current.model
else
statGui.Adornee = nil
end
end
local function psumag(v)
return v.X + v.Y + v.Z
end
modeLoop.resize = function()
modeLoop.default()
focusAxes()
if pointer then
processPointer(pointer)
local origin = current.touching.CFrame:ToWorldSpace(current.modelOrigin)
local start = origin:PointToWorldSpace(current.dragOrigin) -- the start of the scale drag
local diff = (rayProjectToRay(current.ray,current.axis)-start) -- difference between start and end
local scaleChange = origin.Rotation:PointToObjectSpace(diff)
if current.model:IsA("Model") then
local before = mask(current.startSize,current.dragOrigin.Unit) -- the size in this axis before scaling
local after = psumag(mask(scaleChange,current.dragOrigin.Unit)) -- the size in this axis after scaling
local scale = math.round(((((after*math.sign(psumag(before)))/psumag(before))+1)/modelScaleSnap)*current.startScale)*modelScaleSnap
scale = math.clamp(scale,0.5,current.model:GetAttribute("MaxSize") or 3)
current.model:ScaleTo(scale)
current.model:SetAttribute("Size",Vector3.new(scale,0,0))
current.touchingCFrame = current.modelOrigin + current.modelOrigin.Rotation:PointToWorldSpace(((scale/current.startScale)-1)*before*0.5)
statGui.TextLabel.Text = tonumber(math.round(scale*1000)/1000)
else
local change = round(mask(scaleChange,current.scaleTerms or Vector3.one))
current.model.Size = clamp(current.startSize + mask(change,current.dragOrigin.Unit),Vector3.one*0.1,current.model:GetAttribute("MaxSize") or Vector3.one*20)
current.model:SetAttribute("Size",current.model.Size)
current.touchingCFrame = current.modelOrigin + current.modelOrigin.Rotation:PointToWorldSpace(change/2)
statGui.TextLabel.Text = tonumber(math.round(mask(current.model.Size,current.dragOrigin.Unit).Magnitude*1000)/1000)
end
statGui.Adornee = current.model
else
statGui.Adornee = nil
end
modeLoop.default() -- the resize system has this weird lag, i cannot be asked fixing it unfortunately
end
modeLoop.move = function()
modeLoop.default()
focusAxes()
if pointer then
processPointer(pointer)
local origin = current.touching.CFrame:ToWorldSpace(current.modelOrigin)
local start = origin:PointToWorldSpace(current.dragOrigin)
local diff = (rayProjectToRay(current.ray,current.axis,start)-start) -- difference between start and end
diff = current.touching.CFrame.Rotation:PointToObjectSpace(diff)
local offset = psumag(mask(current.moveOffset,diff.Unit))
local len = math.abs(psumag(mask(current.size,origin:ToObjectSpace(current.axis.CFrame).RightVector)))
diff = diff.Unit * math.round(math.clamp(diff.Magnitude,-len*0.5-offset,len*0.5-offset)/moveSnap) * moveSnap
current.touchingCFrame = current.modelOrigin + diff
current.newMoveOffset = current.moveOffset + diff
statGui.TextLabel.Text = tonumber(math.round(diff.Magnitude*1000)/1000)
statGui.Adornee = current.model
else
statGui.Adornee = nil
end
end
modeLoop.place = function()
local result,ray,update = mouseModule.getMouseHit()
if result and result.Instance and update then
current.touching = result.Instance
if current.touching.Locked then adjacentBox.Adornee = nil end
if not current.touching then return end
if not current.touching.Position then print(current.touching) return end
local rotation = (current.touching.CFrame - current.touching.Position)
rotation = rotation * current.rotation
local s = current.size*0.5 -- So half the size of the part, rotated correctly
local max = 0
local points = {Vector3.new(s.X,s.Y,s.Z),Vector3.new(s.X,-s.Y,s.Z),Vector3.new(-s.X,s.Y,s.Z),Vector3.new(-s.X,-s.Y,s.Z),Vector3.new(s.X,s.Y,-s.Z),Vector3.new(s.X,-s.Y,-s.Z),Vector3.new(-s.X,s.Y,-s.Z),Vector3.new(-s.X,-s.Y,-s.Z)}
for _,point in pairs(points) do
max = math.max(max,result.Normal:Dot(rotation:PointToWorldSpace(point)))
end
local push = max
local snap = moveSnap
if shiftModifier then snap = moveSnap * 0.5 end
local depth = (Ray.new(result.Position,-result.Normal):ClosestPoint(current.touching.Position)-result.Position).Magnitude
local flat = current.touching.CFrame:PointToWorldSpace(round(current.touching.CFrame:PointToObjectSpace(result.Position - result.Normal * depth),snap))
if controlModifier or current.model:HasTag("Node") then push = 0 end
local cframe = rotation + flat + result.Normal * (depth + push) -- Add the rotation, Snapped movement perpendicular to normal and the Offset/Pushback
if current.model:HasTag("Node") then
local part
if current.touching:GetAttribute("Nodes") then
part = current.touching
elseif current.touching.Parent:GetAttribute("Nodes") then
part = current.touching.Parent
end
if part then
local ideal
local current_distance = 1
for _,node in ipairs(partStateModule.get_points(part)) do
local distance = (node - result.Position).Magnitude
if distance < current_distance then
current_distance = distance
if part:IsA("Model") then
ideal = part.PrimaryPart
else
ideal = part
end
if current.model:IsA("Model") then
cframe = rotation + node
else
cframe = current.model.CFrame.Rotation + node
end
current.touching = ideal
end
end
end
end
if current.touching:HasTag("Node") then
if current.model:IsA("Model") then
cframe = rotation + current.touching.Position
else
cframe = current.model.CFrame.Rotation + current.touching.Position
end
end
methodModule.setItemCFrame(current.model,cframe)
current.touchingCFrame = current.touching.CFrame:ToObjectSpace(cframe)
current.newMoveOffset = Vector3.zero
adjacentBox.Adornee = current.touching
end
getBounds()
end
local function render()
if not current then return end
controlModifier = inputService:IsKeyDown(Enum.KeyCode.LeftControl) or inputService:IsKeyDown(Enum.KeyCode.LeftMeta)
shiftModifier = inputService:IsKeyDown(Enum.KeyCode.LeftShift)
modeLoop[mode]()
end
--[[
config:
- Can Resize
- Confirm Callback
]]
local placetouchbuttoncolor
local function toggleTouching()
touchModeConnect = not touchModeConnect
if touchModeConnect then
_G.PlaceTouchButton.BackgroundColor3 = placetouchbuttoncolor
else
_G.PlaceTouchButton.BackgroundColor3 = Color3.new(1,1,1)
end
end
function begin(model,config)
if current then current:stop() end
gui.Visible = true
current = config or {}
current.stop = function(config)
if not config then warn("No config passed to PlacementModule; current:stop(). Using current.") config = current end
if not config then return end
if config.axes then config.axes:Destroy() end
if config.stopped then config:stopped() end
current = nil
selectionBox.Adornee = nil
adjacentBox.Adornee = nil
for _,box in pairs(touchingBoxes) do
box:Destroy()
end
touchingBoxes = {}
gui.Visible = false
end
model.Parent = game.Workspace.Hidden
if model:IsA("Model") then
model.PrimaryPart.Anchored = true
else
model.Anchored = true
end
ModeButtons.resize.Visible = config.resize
if not config.resize and mode == "resize" then mode = "place" end
if not config.rotate and mode == "rotate" then mode = "place" end
_G.PlaceTouchButton.Visible = config.touch
selectionBox.Adornee = model
current.model = model
if not current.model then error("urmum") end
current.connections = current.connections or {}
current.rotation = current.model:GetAttribute("Rotation") or CFrame.identity
setMode(realMode)
styleModel()
getBounds()
modeStart[mode]()
return config
end
function init(_gui)
gui = _gui
_G:WaitFor("PlaceModeFrame") _G:WaitFor("PlaceSnapFrame") _G:WaitFor("PlaceCancelButton")
statGui.Enabled = false
placetouchbuttoncolor = _G.PlaceTouchButton.BackgroundColor3
gui.Visible = false
run:BindToRenderStep("placementmodule",Enum.RenderPriority.Last.Value,render)
local touchOverlapParams = OverlapParams.new()
touchOverlapParams.MaxParts = 3
touchOverlapParams.FilterType = Enum.RaycastFilterType.Include
touchOverlapParams.FilterDescendantsInstances = {game.Workspace.Objects}
touchOverlapParams.RespectCanCollide = true
touchOverlapParams.Tolerance = 0.05
run.Heartbeat:Connect(function() -- Touching parts loop
if current and current.model and current.touch then -- in placement
local loop_current = current
local connections = {}
if touchModeConnect then
local items
if current.model:IsA("Model") then -- Check all potential subparts
items = current.model:GetChildren()
elseif current.model:IsA("BasePart") then
items = {current.model}
end
local count = 0
for _,item in pairs(items) do
if item:IsA("BasePart") then
for _,part in pairs(workspace:GetPartsInPart(item,touchOverlapParams)) do -- Get touching parts
if current.connections[part] then connections[part] = part continue end
local object = _G.Methods.getOwnedOrNil(_G.Player,part) -- Check if this is owned
if object then
connections[object] = true -- Add to the current touching ones
current.connections[object] = { -- Add to the final list
part = object
}
if current.model ~= item then current.connections[object].name = item.Name end
--if part == current.touching then continue end
local box = object:FindFirstChild("TouchingBox")
if not box then box = script.TouchingBox:Clone() box.Parent = object end
touchingBoxes[object] = box
end
end
end
if count > 3 then wait() count = 0 else count = count + 1 end -- Dangerous! Things can change after this (wait)
if current ~= loop_current then return end -- Current is invalid now so stop!
end
elseif current.touching then
local connection = {
part = current.touching
}
current.connections[current.touching] = connection
connections[current.touching] = connection
end
for index,connection in pairs(current.connections) do -- Remove all invalid connections too
if not connections[index] then
if touchingBoxes[index] then
touchingBoxes[index]:Destroy()
touchingBoxes[index] = nil
end
current.connections[index] = nil
end
end
end
end)
_G.PlaceTouchButton.Activated:Connect(toggleTouching)
inputService.InputBegan:Connect(function(inputObject,irrelevant)
if not current then return end
if irrelevant then return end
local sign = 1
if shiftModifier then sign = -1 end
if inputObject.UserInputType == Enum.UserInputType.Keyboard then
if inputObject.KeyCode == Enum.KeyCode.R then
if controlModifier then
_G.PlaceSnapInputFrame.RotSnap:CaptureFocus()
else
current.rotation = current.rotation * CFrame.Angles(math.rad(rotSnapKey*sign),0,0)
end
elseif inputObject.KeyCode == Enum.KeyCode.T then
if controlModifier then
_G.PlaceSnapInputFrame.MoveSnap:CaptureFocus()
else
current.rotation = current.rotation * CFrame.Angles(0,math.rad(rotSnapKey*sign),0)
end
elseif inputObject.KeyCode == Enum.KeyCode.Y then
if controlModifier then
_G.PlacePolyInputFrame.PolyThicknessInputBox:CaptureFocus()
else
current.rotation = current.rotation * CFrame.Angles(0,0,math.rad(rotSnapKey*sign))
end
elseif inputObject.KeyCode == Enum.KeyCode.G and not controlModifier then
current.rotation = CFrame.identity
elseif inputObject.KeyCode == Enum.KeyCode.E then
setMode("place")
elseif inputObject.KeyCode == Enum.KeyCode.Q and current.rotate then
setMode("rotate")
elseif inputObject.KeyCode == Enum.KeyCode.Z and current.resize then
setMode("resize")
elseif inputObject.KeyCode == Enum.KeyCode.X then
setMode("move")
elseif inputObject.KeyCode == Enum.KeyCode.U and current.touch then
toggleTouching()
elseif inputObject.KeyCode == Enum.KeyCode.Backspace then
if current and current.cancel then current:cancel() end
end
elseif inputObject.UserInputType == Enum.UserInputType.MouseButton1 then
if not shiftModifier then modeClick[mode](inputObject) else confirmPlace(inputObject,true) end
end
end)
inputService.TouchStarted:Connect(function(object)
modeClick[mode](object)
end)
inputService.InputEnded:Connect(function(inputObject,irrelevant)
if current and inputObject == pointer then
pointer = nil
current.axis = nil
end
end)
gui.Confirm.Activated:Connect(function()
confirmPlace(nil,true)
end)
_G.PlaceCancelButton.Activated:Connect(function()
if current and current.cancel then current:cancel() end
end)
for _,button in pairs(_G.PlaceModeFrame:GetChildren()) do
if button:IsA("TextButton") then
ModeButtons[button.Name] = button
if mode == button.Name then
button.BackgroundColor3 = Color3.new(0.6, 1, 0.333333)
end
button.Activated:Connect(function()
setMode(button.Name)
modeStart[mode]()
end)
end
end
local moveSnapInput = _G.PlaceSnapInputFrame.MoveSnap
local function evaluateSnapInput(text)
return text:match("([%d%.]+).-")
end
_G.PlaceSnapInputFrame.MoveSnap.FocusLost:Connect(function()
local num = evaluateSnapInput(moveSnapInput.Text)
if num then moveSnap = tonumber(num) end
moveSnapInput.Text = tostring(moveSnap).." stud"
end)
local rotSnapInput = _G.PlaceSnapInputFrame.RotSnap
_G.PlaceSnapInputFrame.RotSnap.FocusLost:Connect(function()
local num = evaluateSnapInput(rotSnapInput.Text)
if num then rotSnap = tonumber(num) end
rotSnapInput.Text = tostring(rotSnap).." deg"
end)
moveSnapInput.Text = tostring(moveSnap).." stud"
rotSnapInput.Text = tostring(rotSnap).." deg"
local snap_buttons = {}
for _,button in pairs(_G.PlaceModeFrame:GetChildren()) do
if button:IsA("TextButton") then
table.insert(snap_buttons,button)
if mode == button.Name then
button.BackgroundColor3 = Color3.new(0.6, 1, 0.333333)
end
button.Activated:Connect(function()
setMode(button.Name)
modeStart[mode]()
end)
end
end
end
return getfenv()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
local module = {}
local rules = {}
module.rules = rules
local listeners = {}
function module.listenRule(name,callback)
if rules[name] then task.spawn(callback,rules[name]) end
if not listeners[name] then listeners[name] = {} end
table.insert(listeners[name],callback)
end
function module.setRule(name,value)
_G.Remotes.Rule:FireAllClients(name,value)
if not rules[name] then rules[name] = {} end
if not listeners[name] then listeners[name] = {} end
rules[name] = value
for _,callback in pairs(listeners[name]) do
task.spawn(callback,value)
end
end
if _G.IsClient then
_G.Remotes.Rule.OnClientEvent:Connect(module.setRule)
end
return module
+503
View File
@@ -0,0 +1,503 @@
-- 41458171
require(game.ReplicatedFirst.ReadyModule)("SaveModule")
local Commands = require(_G.Modules.CommandsModule)
Placement = require(_G.Modules.PlacementModule)
Debris = _G.Debris
SaveFrame = nil
Stroke = nil
SaveData = nil
SaveTabs = nil
SaveLists = nil
Templates = nil
PlayerSaveList = nil
SelectionBox = nil
parts = _G.Storage:WaitForChild("Parts")
SubassemblyData = ""
SubassemblyModel = nil
CurrentSaveData = ""
CurrentPlacementConfig = nil
CurrentSaveRoot = nil
LastInteractedPart = nil
SelectionBoxes = {}
local function OffloadSelection()
if CurrentPlacementConfig then CurrentPlacementConfig:stop() end
CurrentSaveRoot = nil
CurrentSaveData = nil
SelectionBox.Adornee = nil
for _,object in pairs(SelectionBoxes) do
object:Destroy()
end
SelectionBoxes = {}
end
local function LoadModel(key,group)
wait()
local raw = _G.Remotes.Load:InvokeServer(group,key)
local success,result = pcall(function()
return _G.Methods.saves.dataToModel(raw,false)
end)
local model
if success then
model = result
else
_G.Error(string.format("Load Model Error (%s:%s): %s",key,group,result))
end
if not model then
return _G.Storage.Missing:Clone()
end
local primary = model:FindFirstChildWhichIsA("VehicleSeat",true)
if primary then
model.PrimaryPart = primary
end
return model
end
local _tab = {}
_tab.__index = _tab
local _slot = {}
_slot.__index = _slot
_slot.__newindex_operations = {
name = function(this,name)
this.frame.NameBox.Text = name
end,
order = function(this,index)
this.frame.LayoutOrder = tonumber(index) or 0
this.frame.IdLabel.Text = tostring(index):upper()
if index == "autosave" then
this.frame.IdLabel.BackgroundColor3 = Color3.new(1, 0.768627, 0.223529)
elseif index == "subassembly" then
this.frame.IdLabel.BackgroundColor3 = Color3.new(0.623529, 0.0941176, 1)
end
end,
on_mirror_clicked = function(this,callback)
_G.Error("Mirror function unimplemented")
end,
on_name_change = function(this,callback)
this.frame.NameBox.TextEditable = true
this.frame.NameBox.FocusLost:Connect(function(enter)
if enter then callback(this.frame.NameBox.Text) end
end)
end,
on_save_clicked = function(this,callback)
this.frame.SaveButton.Visible = true
this.frame.SaveButton.Activated:Connect(function()
callback(CurrentSaveData)
end)
end,
on_load_clicked = function(this,callback)
this.frame.LoadButton.Activated:Connect(function()
callback(this)
end)
end,
model = function(this,model)
local old = this.viewport:FindFirstChildWhichIsA("Model")
if old then old:Destroy() end
if not model then
this.frame.OldLabel.Visible = true
this.frame.OldLabel.Text = "CORRUPT"
return
else
if not (model.Name == "Model") then this.frame.NameBox.Text = model.Name end
end
model.Parent = this.viewport
this.frame.OldLabel.Visible = model:HasTag("Old")
this.frame.OldLabel.Text = model:GetAttribute("Version") or "Err"
local cframe,size = model:GetBoundingBox()
this.camera.CFrame = cframe * CFrame.fromEulerAnglesYXZ(-0.5,-2.5,0)
this.camera.CFrame = this.camera.CFrame + this.camera.CFrame.LookVector * -(size.Magnitude/2)
end
}
_slot.__newindex = function(this,index,value)
if not _slot.__newindex_operations[index] then error("No index to set: "..index,2) end
_slot.__newindex_operations[index](this,value)
end
_slot.show = function(this)
this.frame.Visible = true
_G.Tween:Create(this.frame.UIPadding,TweenInfo.new(0.5,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{PaddingLeft = UDim.new(0,0)}):Play()
end
_tab.CreateSlot = function(this)
local new = {}
new.frame = Templates.Frame:Clone()
new.frame.Parent = this.list
new.raw = ""
_G.Union(new.frame,{
Parent = this.list,
Visible = true,
})
new.frame.NameBox.TextEditable = false
new.frame.SaveButton.Visible = false
new.angle = 45
new.viewport = new.frame.ViewportFrame
new.undo = new.frame.UndoButton
new.refresh = new.frame.RefreshButton
new.camera = Instance.new("Camera")
new.camera.Parent = new.viewport
new.camera.CFrame = CFrame.Angles(0,math.rad(new.angle),0)
new.viewport.CurrentCamera = new.camera
local hover = false
new.viewport.MouseEnter:Connect(function()
hover = true
new.viewport.BackgroundColor3 = Color3.new(0.75,0.75,0.75)
while wait() and hover do
new.camera.CFrame = CFrame.Angles(0,math.rad(2),0) * new.camera.CFrame
end
end)
new.viewport.MouseLeave:Connect(function()
hover = false
new.viewport.BackgroundColor3 = Color3.new(1,1,1)
end)
new.frame.Visible = false
new.frame.OldLabel.Visible = false
return setmetatable(new,_slot)
end
_tab.FocusSection = function(this)
for _,list in pairs(SaveLists:GetChildren()) do
if list:IsA("ScrollingFrame") then list.Visible = false end
end
this.list.Visible = true
Stroke.Parent = this.tab
end
local function CreateSection(title)
local new = setmetatable({},_tab)
new.list = Templates.List:Clone()
_G.Union(new.list,{
Parent = SaveLists,
Visible = false
})
new.tab = Templates.Tab:Clone()
_G.Union(new.tab,{
Parent = SaveTabs,
Visible = true,
Text = title
})
new.slots = {}
new.tab.CloseButton.Activated:Connect(function()
Stroke.Parent = PlayerSaveList
new.tab:Destroy()
new.list:Destroy()
PlayerSaveList:FocusSection()
end)
new.tab.Activated:Connect(function() new:FocusSection() end)
return new
end
_G.SaveIndices = {}
local function CreateUserSection(userid,modify,close)
userid = tostring(userid or _G.UserId)
local title = (game.Players:GetPlayerByUserId(tonumber(userid) or 0) or {DisplayName = userid}).DisplayName
local section = CreateSection(title)
if userid == tostring(_G.UserId) then section:FocusSection() end
local save_info = {}
local save_info_locked = false
local saves = {}
local slots = {}
if not userid:match(":") and not close then section.tab.CloseButton.Visible = false end
local function update_save_info(callback)
repeat
wait()
until not save_info_locked
save_info_locked = true
if callback then callback(save_info) end
_G.Remotes.SetSaveInfo:InvokeServer(save_info,userid)
save_info = _G.Remotes.GetSaveInfo:InvokeServer(userid)
for index,name in pairs(save_info) do
local slot = slots[tostring(index)] or slots[index]
if slot then
slot.name = name
end
end
save_info_locked = false
end
spawn(function()
save_info = _G.Remotes.GetSaveInfo:InvokeServer(userid)
local indices = {}
if not save_info then return end
for index,_ in pairs(save_info) do
table.insert(indices,index)
end
_G.SaveIndices[userid] = indices
if not userid:match(":") then
table.sort(indices,function(before,after)
before = tonumber(before)
after = tonumber(after)
if not before then return true end
if not after then return false end
return before < after
end)
end
for _,index in pairs(indices) do
spawn(function()
local name = save_info[index]
index = tostring(index)
local slot = section:CreateSlot()
slots[index] = slot
local model = LoadModel(index,userid)
saves[index] = model
_G.Union(slot,{
name = name,
order = index,
on_name_change = function(text)
update_save_info(function()
save_info[tonumber(index) or index] = text
end)
end,
on_load_clicked = function()
if not saves[index] or not saves[index].PrimaryPart then _G.Events.Notification:Fire("Vehicle Load Failed","There is no vehicle.") return end
OffloadSelection()
CurrentPlacementConfig = Placement.begin(saves[index]:Clone(),{
confirm = function(config)
_G.Remotes.Place:InvokeServer(config.model.PrimaryPart.CFrame,index,userid)
config:stop()
end,
stopped = function(config)
config.model:Destroy()
CurrentPlacementConfig = nil
end,
disabled = {
resize = true
}
})
end,
model = model
})
if modify then
slot.on_save_clicked = function()
if (not CurrentSaveData) or (#CurrentSaveData == 0) then
_G.Events.Notification:Fire("Vehicle Save Failed","Please select a vehicle to save")
return
end
_G.Remotes.Save:InvokeServer(CurrentSaveData,index,userid)
model = LoadModel(index,userid)
saves[index] = model
slot.model = model
OffloadSelection()
end
slot.viewport.MouseEnter:Connect(function()
slot.undo.Visible = true
slot.refresh.Visible = true
end)
slot.viewport.MouseLeave:Connect(function()
slot.undo.Visible = false
slot.refresh.Visible = false
end)
end
slot.undo.Activated:Connect(function()
CreateUserSection(userid..":"..index,false)
end)
slot.refresh.Activated:Connect(function()
model = LoadModel(index,userid)
saves[index] = model
slot.model = model
OffloadSelection()
end)
slot:show()
end)
wait(0.2)
end
if tostring(_G.UserId) == userid then -- autosave
spawn(function()
wait(5)
repeat
if not LastInteractedPart then continue end
local part = LastInteractedPart.part
if part and part.Parent and _G.Methods.playerOwnsPart(game.Players.LocalPlayer,part) then
_G.Events.Notification:Fire("Auto Saving","Attempting Autosave")
CurrentSaveData = _G.Methods.saves.modelToData(part)
_G.Remotes.Save:InvokeServer(CurrentSaveData,"autosave",userid)
_G.Events.Save:Fire(CurrentSaveData)
local selection_template = Instance.new("SelectionBox")
for index,item in pairs({
SurfaceTransparency = 0.75,
SurfaceColor3 = Color3.fromRGB(231, 72, 183),
LineThickness = 0,
}) do
selection_template[index] = item
end
_G.Methods.forEachConnectedObject(part,function(model)
wait()
local selection = selection_template:Clone()
selection.Adornee = model
selection.Parent = model
Debris:AddItem(selection,0.1)
end)
update_save_info(function()
save_info["autosave"] = tostring(os.date("%c",os.time()))
end)
local model = LoadModel("autosave",userid)
slots["autosave"].model = model
saves["autosave"] = model
end
until not wait(60*2.5)
end)
end
end)
return section,saves
end
_G.Events.Create.Event:Connect(function(part)
LastInteractedPart = part
end)
local function RenderStepped(delta) -- General procedure for allowing us to pick a part from a vehicle to later select (per frame)
if CurrentPlacementConfig then return end
local result = _G.Methods.getMouseHit()
if result and result.Instance then
local part
if _G.Methods.playerOwnsPart(_G.Player,result.Instance) then
part = result.Instance
elseif _G.Methods.playerOwnsPart(_G.Player,result.Instance.Parent) then
part = result.Instance.Parent
end
SelectionBox.Adornee = part
CurrentSaveRoot = part
end
end
function InitTool(script)
SelectionBox = script:WaitForChild("SelectionBox")
local renderSteppedEvent = nil
script.Parent.Activated:Connect(function()
if CurrentSaveRoot then
local root = CurrentSaveRoot
OffloadSelection()
CurrentSaveData = _G.Methods.saves.modelToData(root)
local selection_template = Instance.new("SelectionBox")
for index,item in pairs({
SurfaceTransparency = 0.75,
SurfaceColor3 = Color3.fromRGB(154, 46, 231),
LineThickness = 0
}) do
selection_template[index] = item
end
_G.Methods.forEachConnectedObject(root,function(model)
local selection = selection_template:Clone()
selection.Adornee = model
selection.Parent = model
table.insert(SelectionBoxes,selection)
end)
end
end)
script.Parent.Unequipped:Connect(function()
OffloadSelection()
SaveFrame.Visible = false
SelectionBox.Adornee = nil
if renderSteppedEvent then renderSteppedEvent:Disconnect() end
end)
script.Parent.Equipped:Connect(function()
SaveFrame.Visible = true
if renderSteppedEvent then renderSteppedEvent:Disconnect() end
renderSteppedEvent = game:GetService("RunService").RenderStepped:Connect(RenderStepped)
end)
end
local player_saves
function InitUI(script)
SaveFrame = script.Parent
Stroke = script:WaitForChild("UIStroke")
SaveData = script.Parent:WaitForChild("SaveData")
SaveTabs = script.Parent:WaitForChild("SaveTabs")
SaveLists = script.Parent:WaitForChild("SaveLists")
Templates = script.Parent:WaitForChild("Template")
SaveFrame.Visible = false
Templates.Frame.Visible = false
PlayerSaveList,player_saves = CreateUserSection(_G.UserId,true)
for i = 1,2 do
local SubSlot = PlayerSaveList:CreateSlot()
SubSlot.order = string.format("SUB %i",i)
SubSlot.name = string.format("Subassembly %i",i)
SubSlot.on_save_clicked = function()
if (not CurrentSaveData) or (#CurrentSaveData == 0) then
_G.Events.Notification:Fire("Vehicle Save Failed","Please select a vehicle to save")
return
end
SubassemblyData = CurrentSaveData
SubassemblyModel = _G.Methods.saves.dataToModel(CurrentSaveData,false)
SubSlot.model = SubassemblyModel
OffloadSelection()
end
SubSlot.on_mirror_clicked = function()
OffloadSelection()
if not SubassemblyData then return end
if SubassemblyModel then SubassemblyModel:Destroy() end
SubassemblyModel = _G.Methods.saves.dataToModel(SubassemblyData,false,true)
SubSlot.model = SubassemblyModel
end
SubSlot.on_load_clicked = function()
OffloadSelection()
if not SubassemblyModel then return end
CurrentPlacementConfig = Placement.begin(SubassemblyModel:Clone(),{
confirm = function(config)
_G.Events.Notification:Fire("Subassembly Placing!")
_G.Remotes.PlaceSubassembly:InvokeServer(config.model.PrimaryPart.CFrame,SubassemblyData,config.touching)
config:stop()
end,
stopped = function(config)
config.model:Destroy()
CurrentPlacementConfig = nil
end,
})
end
SubSlot:show()
end
CreateUserSection("public",_G.Remotes.Admin:InvokeServer())
end
Commands.new("open save (.+)","open save <userid>: Opens saves in your tool.",function(userid)
CreateUserSection(userid,false)
_G.Events.Notification:Fire("Opened Read-Only Save",userid)
end)
Commands.new("open write save (.+)","open write save <userid>: Opens write saves in your tool.",function(userid)
CreateUserSection(userid,true)
_G.Events.Notification:Fire("Opened Write Save",userid)
end)
Commands.new("export save (.+) (.+)","export save <userid> <number>: Prints out your save",function(userid,key)
_G.Events.Notification:Fire("Save Export",tostring(_G.Remotes.Load:InvokeServer(userid,key)))
end)
Commands.new("show indices","show indices: Prints out save indices",function()
local result = ""
local indices = _G.SaveIndices
for userid,tab in pairs(indices) do
result = result .. "Userid: "..tostring(userid).."\n"
for index,item in pairs(tab) do
result = result .. " "..tostring(index)..": "..tostring(item).."\n"
end
end
print(result)
_G.Events.Notification:Fire("Save Indicies",result)
end)
return getfenv()
+30
View File
@@ -0,0 +1,30 @@
local module = {}
local function returnNetworkOwner(this)
local owner = this:GetAttribute("NetworkOwner") or this.Parent:GetAttribute("NetworkOwner")
if owner then owner = game.Players:GetPlayerByUserId(owner) end
this:SetNetworkOwner(owner) -- there is a bug here?
end
function module.init(this)
do return end
this.Parent.Changed:Connect(function(name)
if name == "Occupant" then
if this.Parent.Occupant then
local player = game.Players:GetPlayerFromCharacter(this.Parent.Occupant.Parent)
if player then
if this.Parent.Parent:HasTag("P_"..player.UserId) or true then
this.Parent:SetNetworkOwner(player)
--print("Vehicle ownership set to "..player.DisplayName)
end
else
returnNetworkOwner(this.Parent)
end
else
returnNetworkOwner(this.Parent)
end
end
end)
end
return module
+409
View File
@@ -0,0 +1,409 @@
local partStateModule = require(script.Parent.PartStateModule)
local saves = {}
local id_parts = {}
local parts_loaded
spawn(function()
game.ReplicatedStorage:WaitForChild("Parts")
local function added(object)
if object.Parent:IsA("Folder") then
local id = object:GetAttribute("PartId")
if id then id_parts[id] = object end
end
end
for _,object in pairs(_G.Storage.Parts:GetDescendants()) do
added(object)
end
_G.Storage.Parts.DescendantAdded:Connect(added)
wait(1)
parts_loaded = true
end)
do
local pairs = pairs
local ipairs = ipairs
local isa = Instance.new("Part").IsA
local no_cframe = {}
local cframe_of = function(object)
if isa(object,"BasePart") then
return object.CFrame
elseif isa(object,"Model") then
return (object.PrimaryPart or no_cframe)["CFrame"]
end
end
local cframe_set = function(object,cframe)
if isa(object,"Model") then
object:SetPrimaryPartCFrame(cframe)
elseif isa(object,"BasePart") then
object.CFrame = cframe
end
end
local weld_is_build = {["BuiltWeldConstraint"] = true}
local insert = table.insert
local typeof = typeof
local is_table = {["table"]=true}
local id_module = _G.Methods.Ids
local colorPart = _G.Methods.colorPart
local ts = tostring
local tn = tonumber
local format = string.format
local concat = table.concat
local cframe_new = CFrame.new
local vector3_new = Vector3.new
local table_create = table.create
local form_triangle = partStateModule.form_triangle
local get_nodes = partStateModule.get_nodes
local apply_nodes = partStateModule.apply_nodes
local apply_shift = partStateModule.apply_shift
local apply_size = partStateModule.apply_size
local apply_material = partStateModule.apply_material
local apply_internal_welds = partStateModule.apply_internal_welds
local hexxie = require(script.Parent.HexxieModule)
local hd = hexxie.decode
local he = hexxie.encode
local hexxie_cframe_match = string.rep("(...........)",12)
local hexxie_position_match = string.rep("(...........)",3)
local default_cframe_format = string.rep("%s,",10).."%s"
local id_attach = {}
local compress_cframe_decode = function(s)
if not s then return nil end
local a,b,c,d,e,f,g,h,i,j,k,l = s:match(hexxie_cframe_match)
return cframe_new(hd(a),hd(b),hd(c),hd(d),hd(e),hd(f),hd(g),hd(h),hd(i),hd(j),hd(k),hd(l))
end
local compress_cframe_encode = function(c)
local a,b,c,d,e,f,g,h,i,j,k,l = c:GetComponents()
return concat({he(a),he(b),he(c),he(d),he(e),he(f),he(g),he(h),he(i),he(j),he(k),he(l)})
end
local compress_position_decode = function(s)
local a,b,c = s:match(hexxie_position_match)
return vector3_new(hd(a),hd(b),hd(c))
end
local compress_position_encode = function(v)
return concat({he(v.X),he(v.Y),he(v.Z)})
end
local position
local http = game:GetService("HttpService")
local http_json_encode = http.JSONEncode
local http_json_decode = http.JSONDecode
local json_decode = function(value)
return http_json_decode(http,value)
end
local json_encode = function(value)
return http_json_encode(http,value)
end
local ID_INDEX = 1
local CFRAMES_INDEX = 2
local EDIT_INDEX = 3
saves["4"] = {
decode = function(raw,options)
--print(raw)
repeat wait() until parts_loaded
local final = {errors = {}}
local function err(text)
warn(text)
table.insert(final.errors,text)
end
options = options or {}
local _compress_cframe_decode = compress_cframe_decode
local _compress_position_decode = compress_position_decode
if options.mirror then
_compress_cframe_decode = function(str)
local cf = compress_cframe_decode(str)
local x,y,z = cf:ToEulerAnglesYXZ()
return CFrame.fromEulerAnglesYXZ(x,-y,-z)+Vector3.new(cf.X,-cf.Y,cf.Z)
end
_compress_position_decode = function(str)
local pos = compress_position_decode(str)
return vector3_new(-pos.X,pos.Y,pos.Z)
end
end
local compressed = raw:match("\"compress\"%:true")
if compressed then raw = hexxie.rle_decode(raw) end
local data = _G.Http:JSONDecode(raw)
_G.Union(options,data.options or {})
local physics = options.physics
local template_cframe = Instance.new("CFrameValue")
local weld_template = Instance.new("WeldConstraint")
local built_weld_template = weld_template:Clone()
built_weld_template.Name = "BuiltWeldConstraint"
local model = Instance.new("Model")
final.model = model
local parts = {}
local editActions = {
c = function(edit,part)
local color = Color3.new(unpack(edit))
colorPart(part,color)
end,
h = function(edit,part)
local hinge = part:FindFirstChild("Hinge")
if hinge then hinge.CFrame = compress_cframe_decode(edit) end
end,
r = function(edit,part)
local slider = part:FindFirstChild("Slider")
if slider then slider.CFrame = compress_cframe_decode(edit) end
end,
m = function(edit,part)
if edit then apply_material(part,Enum.Material[edit]) end
end,
s = function(edit,part)
apply_size(part,Vector3.new(unpack(edit)))
end,
S = function(edit,part)
apply_shift(part,edit)
end,
}
local poly_to_internally_weld = {}
local data_cframes = data.cframes
local data_edits = data.edits
for id,object in pairs(data.objects) do
local template = id_parts[object[ID_INDEX]]
if not template then err("Missing part with id "..tostring(id)) continue end
local instance = template:Clone()
instance.Parent = model
parts[id] = instance
local nodes = {}
local transform_ids = object[CFRAMES_INDEX]
-- BEGIN PARITY 4.1 (CAN BE REMOVED LATER)
if typeof(transform_ids) ~= "table" then transform_ids = {transform_ids} end
-- END PARITY 4.1
if #transform_ids == 1 then
insert(nodes,compress_cframe_decode(data_cframes[transform_ids[1]]))
elseif #transform_ids == 2 then
insert(nodes,compress_cframe_decode(data_cframes[transform_ids[1]]))
insert(nodes,hd(data_cframes[transform_ids[2]]))
else
for _,id in ipairs(transform_ids) do
insert(nodes,compress_position_decode(data_cframes[id]))
end
insert(poly_to_internally_weld,instance)
end
apply_nodes(instance,nodes)
-- Edit part
if object[EDIT_INDEX] then
for _,edit_id in pairs(object[EDIT_INDEX]) do
local key,value = unpack(data_edits[edit_id]);
(editActions[key] or function(value,instance)
local edit = instance:FindFirstChild(key)
if not edit then
err(string.format("No key %s for %s",key,instance:GetFullName()))
else
if typeof(value) == "table" then value = _G.Http:JSONEncode(value) end
edit.Value = value
end
end)(value,instance)
end
end
-- Change anchoring
if not options.physics then
if instance:IsA("Model") then
instance.PrimaryPart.Anchored = true
else
instance.Anchored = true
end
end
end
if options.physics then
for _,weld in pairs(data.welds) do
local w = built_weld_template:Clone()
local at,bt = unpack(weld)
local ap = parts[at[1]]
local bp = parts[bt[1]]
w.Parent = ap
if ap:IsA("Model") then
if at[2] then
ap = ap:FindFirstChild(at[2]) or ap.PrimaryPart
else
ap = ap.PrimaryPart
err("Missing subpart reference for weld on model A")
end
end
if bp:IsA("Model") then
if bt[2] then
bp = bp:FindFirstChild(bt[2]) or bp.PrimaryPart
else
bp = bp.PrimaryPart
err("Missing subpart reference for weld on model B")
end
end
w.Part0 = ap
w.Part1 = bp
end
for _,poly in ipairs(poly_to_internally_weld) do
apply_internal_welds(poly)
end
else
for _,part in pairs(model:GetDescendants()) do
if part:IsA("BasePart") then part.CanCollide = false part.Anchored = true end
end
end
local root = parts[1]
if not root then error("Fatal: Missing root part; implying #parts = 0") end
if root:IsA("Model") then root = root.PrimaryPart end
model.PrimaryPart = root
final.model = model
return final
end,
encode = function(root,options)
repeat wait() until parts_loaded
local final = {errors = {}}
local function err(text)
warn(text)
table.insert(final.errors,text)
end
id_module.forEachConnectedObject(root,function(object)
if object:IsA("VehicleSeat") then root = object end
end)
final.root = root
if not options then options = {} end
local origin = cframe_of(root)
if not origin then err("Root CFrame could not be encoded, it is probably broken") return final end
local ids = {}
local cframes = {}
local edits = {}
local welds = {}
local objects = {}
local data = {
options = options,
objects = objects,
cframes = cframes,
edits = edits,
welds = welds
}
local should_optimise_cframe = not not options.optimise_cframe
local cframe_epsilon = options.cframe_epsilon or 0.1
local optimise_edit = {}
local optimise_cframe = {}
local function cframe_id(c)
local existing = optimise_cframe[c]
if not existing then
insert(cframes,c)
local len = #cframes
optimise_cframe[c] = len
return len
else
return existing
end
end
local function edit_id(key,value)
local str
if type(value) == "table" then
str = json_encode(value)
elseif type(value) ~="string" then
str = tostring(value)
else
str = value
end
local tab = optimise_edit[key]
if not tab then tab = {} optimise_edit[key] = tab end
if tab[str] then
return tab[str]
else
insert(edits,{key,value})
tab[str] = #edits
return #edits
end
end
local errors = {}
local index = 0
id_module.forEachConnectedObject(root,function(object)
index = index + 1
ids[object] = index
local id = object:GetAttribute("PartId") or object.Name
if not id then insert(errors,string.format("Part without an ID! %s",ts(object))) end
-- ENCODE TRANSFORM
local transform = get_nodes(object)
if #transform >= 3 then
for index,position in pairs(transform) do
transform[index] = cframe_id(compress_position_encode(origin:PointToObjectSpace(position)))
end
elseif #transform == 2 then
transform[1] = cframe_id(compress_cframe_encode(origin:ToObjectSpace(transform[1])))
transform[2] = cframe_id(he(transform[2]))
elseif #transform == 1 then
transform[1] = cframe_id(compress_cframe_encode(origin:ToObjectSpace(transform[1])))
end
-- ENCODE EDITS
local edits = {}
for index,edit in pairs(object:GetChildren()) do
if edit:HasTag("Edit") or edit:HasTag("EditHidden") then
if edit:HasTag("JSON") then
insert(edits,edit_id(edit.Name,json_decode(edit.Value)))
else
local value = edit.Value
if (edit:GetAttribute("Default") ~= value) then
insert(edits,edit_id(edit.Name,value))
end
end
end
end
-- ENCODE CANDID DISPLACEMENT
local hinge = object:FindFirstChild("Hinge")
if hinge then insert(edits,edit_id("h",compress_cframe_encode(origin:ToObjectSpace(hinge.CFrame)))) end
local slider = object:FindFirstChild("Slider")
if slider then insert(edits,edit_id("r",compress_cframe_encode(origin:ToObjectSpace(slider.CFrame)))) end
-- ENCODE PROPERTIES
local color = object:GetAttribute("Color") -- TODO HEXXIE OPTIMISE COLOUR AND SIZE
if color then insert(edits,edit_id("c",{color.R,color.G,color.B})) end
local material = object:GetAttribute("Material")
if material then insert(edits,edit_id("m",material)) end
local size = object:GetAttribute("Size")
if size then insert(edits,edit_id("s",{size.X,size.Y,size.Z})) end
local shift = object:GetAttribute("Shift")
if shift then insert(edits,edit_id("S",shift)) end
-- INSERT OBJECT
local data = {id,transform}
if #edits > 0 then insert(data,edits) end
insert(objects,data)
end)
-- ENCODE WELDS
for object,id in pairs(ids) do
for _,weld in pairs(object:GetDescendants()) do
if weld_is_build[weld.Name] then
if weld.Part0 and weld.Part1 then
local first = {ids[weld.Part0]}
if not first[1] then
first[1] = ids[weld.Part0.Parent]
first[2] = weld.Part0.Name
if not first[1] then err("There was a stray first weld! "..tostring(weld:GetFullName())) continue end
end
local second = {ids[weld.Part1]}
if not second[1] then
second[1] = ids[weld.Part1.Parent]
second[2] = weld.Part1.Name
if not second[1] then err("There was a stray second weld! "..tostring(weld:GetFullName())) continue end
end
insert(data.welds,{first,second})
else
err("There was a dead weld!"..tostring(weld:GetFullName()))
end
end
end
end
local json = _G.Http:JSONEncode(data)
local result = json
if options.compress then result=hexxie.rle_encode(json) end
local check = hexxie.rle_decode(result)
result = "v4save"..result
final.data = result
return final
end,
}
end
return saves
+143
View File
@@ -0,0 +1,143 @@
local is_client = game.Players.LocalPlayer
if is_client then return {} end
local scale = 20 -- * 10
local generation_folder = game.Workspace.Generation
local function triangle(p1,p2,p3,r1,r2) -- simple function, might work?
local s = 0.01
local bottom = {
b1 = nil,
b2 = nil,
dist = 0,
other = nil
}
local function check(point1,point2,other)
local dist = (point1 - point2).Magnitude
if dist > bottom.dist then
bottom.b1 = point1
bottom.b2 = point2
bottom.other = other
bottom.dist = dist
end
end
check(p1,p2,p3) -- check all of the different options for bottom and get the right one?
check(p1,p3,p2)
check(p2,p3,p1)
local view = CFrame.lookAt(bottom.b1,bottom.b2,(bottom.other-bottom.b1).Unit)
local object = view:PointToObjectSpace(bottom.other)
local length = object.Z
local height = object.Y
r1.Size = Vector3.new(0.25,height-s,-length-s)
r1.CFrame = (view * CFrame.Angles(0,math.rad(180),0)) * CFrame.new(0,height/2+s/2,-length/2+s/2)
r2.Size = Vector3.new(0.25,height-s,-(-bottom.dist-length)-s)
r2.CFrame = view * CFrame.new(0,height/2+s/2,-(bottom.dist-length)/2+s/2)
--[[if r1.Position.Y < 0 then
r1.Color = Color3.new(0.764706, 0.756863, 0.478431)
r1.Material = Enum.Material.Sand
end
if r2.Position.Y < 0 then
r2.Color = Color3.new(0.764706, 0.756863, 0.478431)
r2.Material = Enum.Material.Sand
end]]
return r1,r2
end
local editable_mesh_template = Instance.new("EditableMesh")
local editable_mesh_add_vertex = editable_mesh_template.AddVertex
local editable_mesh_add_triangle = editable_mesh_template.AddTriangle
local ta
local tb
local tc
local function ntri(mesh,a,b,c,d)
ta = editable_mesh_add_vertex(mesh,a)
tb = editable_mesh_add_vertex(mesh,b)
tc = editable_mesh_add_vertex(mesh,c)
editable_mesh_add_triangle(mesh,ta,tb,tc)
ta = editable_mesh_add_vertex(mesh,a)
tb = editable_mesh_add_vertex(mesh,c)
tc = editable_mesh_add_vertex(mesh,d)
editable_mesh_add_triangle(mesh,ta,tb,tc)
end
local function part()
local part = Instance.new("Part")
part.Parent = generation_folder
part.Anchored = true
return part
end
local function tri(mat)
local new = mat:Clone()
new.Parent = generation_folder
return new
end
local fidelity = 20
local tree = _G.Storage.Tree
local mat_sand = game.Workspace:WaitForChild("SandTriangle")
local mat_grass = game.Workspace:WaitForChild("GrassTriangle")
local tri_union_pool = {}
local function unionTable(tbl)
if #tbl > 2 then
local start = tbl[#tbl]
table.remove(tbl,#tbl)
start:UnionAsync(tbl).Parent = game.Workspace
for _,item in pairs(tbl) do
item:Destroy()
end
print("union!")
end
end
local function chunk(sx,sy,ox,oy)
--[[for i=18,2,-4 do
local water = _G.Storage.Water:Clone()
water.Size = Vector3.new(sx,0,sy)*scale*scale
water.Position = Vector3.new(ox*sx+sx/2,-2,oy*sy+sy/2)*scale + Vector3.new(0,i,0)
water.Parent = generation_folder
end]]
--[[local object = Instance.new("MeshPart")
object.Parent = generation_folder
object.Name = "MeshObject"
object.Anchored = true
object.Material = Enum.Material.Grass
object.Color = Color3.new(0.254902, 0.403922, 0.133333)
object.Size = Vector3.new(1,1,1)
local edit = Instance.new("EditableMesh")
edit.Parent = object
edit.Name = "VertexObject"
local rows = {}
local last_column = nil
local sumtris = {}
for x=ox*sx,ox*sy+sx,fidelity do
local columns = {}
table.insert(rows,columns)
local last_index = 1
for y=oy*sy,oy*sy+sy,fidelity do
local biome = math.clamp(math.noise(x/1000,y/1000,-6.432),0,1)*50
local island = math.clamp(math.noise(x/200,y/200,-2.132)-0.3,0,1)*30
local height = math.noise(x/10,y/10,0.123) + math.clamp(math.noise(x/50,y/50,0.123),0.2,1)*3 - (0.2-math.clamp(math.abs(math.noise(x/50,y/50,5.213)),0,0.2))*10 + island - 8 + biome
table.insert(columns,Vector3.new(x,height*2,y))
if last_column and last_index > 1 and height > -5 then
local mat = mat_grass
local current = columns[last_index]*scale
local left = columns[last_index-1]*scale
local up_left = last_column[last_index-1]*scale
local up = last_column[last_index]*scale
--ntri(edit,current,up,up_left,left)
local t1,t2 = triangle(current,up,up_left,tri(mat),tri(mat))
local t3,t4 = triangle(current,left,up_left,tri(mat),tri(mat))
table.insert(sumtris,t1)
table.insert(sumtris,t2)
table.insert(sumtris,t3)
table.insert(sumtris,t4)
end
last_index = last_index + 1
end
last_column = columns
end
task.delay(0,function()unionTable(sumtris)end)]]
end
for x=-6,6 do
for y=-6,6 do
wait()
chunk(100,100,x,y)
end
end
return {}
+3
View File
@@ -0,0 +1,3 @@
local module = {}
return module
+126
View File
@@ -0,0 +1,126 @@
local module = {}
local debugWings = game.Workspace.DebugWings.Value
local space = game.Workspace.Objects
local dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
local ndelta = 1/60
local function drag(v,r)
return v * r
end
local rnd = function(vec) return Vector3.new(math.round(vec.X),math.round(vec.Y),math.round(vec.Z)) end
local a = math.abs
local ln = math.log
local debwings = {}
local function deb(wing,force)
local part = debwings[wing]
if not part then
part = Instance.new("Part")
part.Name = "WingDebug"
part.Parent = game.Workspace.Hidden
part.Color = Color3.new(0.152941, 0.764706, 0.0588235)
part.Material = Enum.Material.Neon
part.Anchored = true
part.CanCollide = false
part.Transparency = 0.8
part.Size = Vector3.new(0.25,0.25,2) -- we determine the wing's strength and do math by its size, the bigger, the more lift
debwings[wing] = part
end
part.Transparency = 0.5
part.CFrame = CFrame.lookAt(wing.Position,wing.Position+force)
part.Size = Vector3.new(0.25,0.25,force.Magnitude)
part.CFrame = part.CFrame + part.CFrame.LookVector * part.Size.Z * 0.5
end
require(game.ReplicatedFirst.ReadyModule)("WingCommands")
local process = true
game.Workspace.DebugWings.Changed:Connect(function()
debugWings = game.Workspace.DebugWings.Value
if not debugWings then
for _,item in pairs(debwings) do
item:Destroy()
end
debwings = {}
end
end)
game.Workspace.CompensateDensity.Changed:Connect(function()
dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
end)
function module.new(this,wing,context)
--local mul = wing:GetAttribute("AeroMultiplier") or 1
this.bounds = {}
this.volume = {}
if wing:IsA("Model") then
this.aero = {}
for _,part in pairs(wing:GetChildren()) do
if part:HasTag("IsAerodynamic") then
table.insert(this.aero,part)
end
end
else
this.aero = {wing}
end
for _,part in pairs(this.aero) do
local s = part.Size
this.bounds[part] = Vector3.new(s.Y*s.Z,s.X*s.Z,s.Y*s.X)-- * mul
this.volume[part] = s.X*s.Y*s.Z
end
return this
end
local visc = 0.75
local commands = require(_G.Modules.CommandsModule)
commands.new("viscosity (.+)","viscosity <number>: change air viscosity coefficient",function(n)
visc = tonumber(n)
end)
module.run = @native
function(this,wing,context)
local bounds = this.bounds
local volume = this.volume
if not process then return true end
if not this.aero then print(this,wing) end -- BECAUSE WHAT THE BLOODY HELL IS THIS!?!??
for _,wing in pairs(this.aero) do
--if not (wing.Parent == space or wing.Parent.Parent == space) and v.player then return end
local s = bounds[wing]
if not s then print(this.aero,bounds,wing,s) end
local p = wing.Position
local v = wing:GetVelocityAtPosition(p) -- velocity of wing
local d = dens * 20
local fmass = 2 -- mass of air generally
--[[if p.Y < 0 then
d = d * 5
fmass = 100 -- mass of water
end]]
v = v - Vector3.new(0,
0--0.15 * v.Magnitude -- implement upthrust due to pressure difference across an airfoil (lift)
,0)
local lv = wing.CFrame.Rotation:PointToObjectSpace(v) -- velocity for each wing face
--mv = lv.Magnitude + 0.0001 -- small number to stop zero error
-- "math", YEAH REALLY? i didnt REALISE! I don't even know what this does...
--lp = Vector3.new(lv.X/(a(scale.X*d*lv.X)+1)-lv.X,lv.Y/(a(scale.Y*d*lv.Y)+1)-lv.Y,lv.Z/(a(scale.Z*d*lv.Z)+1)-lv.Z) -- math
local drag = Vector3.new(lv.X*s.X,lv.Y*s.Y,lv.Z*s.Z)
-- implement anti-viscosity (may the riemann sum gods be with the low end machines
local lp = (drag) * context.delta*d*math.tanh(v.Magnitude*0.0075)
local r = (wing.CFrame.Rotation:PointToWorldSpace(lp))
r = r + Vector3.yAxis * fmass * volume[wing] * 2 * context.delta -- implement upthrust due to pressure difference in a fluid column (floating)
wing:ApplyImpulseAtPosition(r,wing.Position)
if debugWings then deb(wing,r) end
end
return true
end
module.manifest = {
predicate = function(instance)
return instance:HasTag("IsAerodynamic") or instance.Name:match("Sheet") -- LAST CONDITION IS NOT NEEDED
end,
}
return module
+34
View File
@@ -0,0 +1,34 @@
local module = {}
local cool_rate = 0.25
function module.new(this,balloon,context)
this.heat = 0
end
function module.run(this,balloon,context)
this.heat = this.heat * (1 - cool_rate * context.delta) -- wrong but idc
local force = Vector3.new(0,1,0) * (1-(1/(1+this.heat))) * 1000 * context.delta * balloon.Size.Magnitude
balloon:ApplyImpulseAtPosition(force,balloon.Position)
context.replicate(this,balloon,module,this.heat)
return true
end
function module.replicate(this,balloon,context,heat)
this.heat = heat
end
module.update = module.new
module.messages = {
flux = function(this,balloon,context,heat)
this.heat = this.heat + heat
end,
}
module.messages.default = nil
module.manifest = {
"LongBalloon",
"RoundBalloon"
}
return module
+54
View File
@@ -0,0 +1,54 @@
local module = {}
local function burn_effect(this,boiler,context,burn)
for index,item in pairs(this.burning) do
item.Color = Color3.fromRGB(255, 137, 73):Lerp(Color3.fromRGB(115, 61, 33),1-math.abs(burn))
end
end
function module.new(this,boiler,context)
this.burning = {}
this.scale = boiler:GetExtentsSize().Magnitude
this.exhaust = _G.GetInstance(boiler,"Exhaust")
for index,item in pairs(boiler:GetChildren()) do
if item.Name == "Fire" then
table.insert(this.burning,item)
end
end
return this
end
function module.idle(this,boiler) return true end
local begin = tick()
module.run = function(this,boiler,context)
local throttle = this.throttle or context.controls[_G.GetValue(boiler,"Binding","throttle")] or this.Throttle or 0
context.replicate(this,boiler,module,throttle)
local objects = {}
context.connected("ConnectorFume","Exhaust",this.exhaust,function(object)
table.insert(objects,object)
end)
for index,object in pairs(objects) do
context.message(object,"flux",this.scale * math.abs(throttle) * context.delta * (1/#objects))
end
return true
end
module.replication_delta = 0.25
function module.replicate(this,boiler,context,burn)
burn_effect(this,boiler,context,burn)
end
module.messages = {
control = function(this,object,context,value)
this.throttle = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Boiler1",
"Boiler2"
}
return module
+26
View File
@@ -0,0 +1,26 @@
local module = {}
function module.new(this,boombox,context)
this.sound = _G.GetInstance(_G.GetInstance(boombox,"Plank"),"Sound")
end
module.messages = {
source = function(this,speaker,context,value)
this.sound.SoundId = "rbxassetid://"..(tostring(value or "") or "")
end,
default = function(this,speaker,context,value)
if value > 0.5 then
this.sound:Play()
elseif value == 0 then
this.sound:Pause()
elseif value < -0.5 then
this.sound:Stop()
end
end,
}
module.manifest = {
"Boombox"
}
return module
+19
View File
@@ -0,0 +1,19 @@
local module = {}
local partStateModule = require(_G.Modules.PartStateModule)
function module.new(this,cable,context)
end
function module.run(this,cable,context)
if context.frequency(this,"cable_material",1) then cable.Material = Enum.Material.Fabric end
return true
end
module.update = module.new
module.manifest = {
"DataCable"
}
return module
+59
View File
@@ -0,0 +1,59 @@
local module = {}
local programModule = require(_G.Modules.ProgramModule)
function module.new(this,computer,context)
this.value = _G.GetInstance(computer,"Program")
this.led = _G.GetInstance(computer,"LED")
this.program = programModule.open_program(this.value,nil)
this.program:load()
this.inputs = {}
this.output = function(pin,name,value)
context.connected("ConnectorData","ConnectorEnd",computer[pin],function(object)
context.message(object,name,value)
end)
end
this.input = function(name)
return this.inputs[name]
end
this.control = function(name)
return context.controls[name]
end
this.message = function(title,value)
if context.queue(this,"notification",3) then _G.Events.Notification:Fire(title,value) end
end
this.led.Color = Color3.fromRGB(255,255,255)
return this
end
local begin = tick()
module.run = _G.Protect(function(this,computer,context)
if not this.program then this.led.Color = Color3.fromRGB(255,0,0) return true end
this.led.Color = Color3.fromRGB(0,255,0)
this.program.variables = this.variables or {}
this.program:run(this)
context.replicate(this,computer,module,this.program.variables)
return true
end)
function module.remove(this,computer,context)
end
module.replication_delta = 0.25
function module.replicate(this,computer,context,variables)
this.variables = variables
end
module.messages = {
default = function(this,object,context,value)
local pin = object.Name
this.inputs[pin] = value
end
}
module.manifest = {
"Microcontroller"
}
return module
+44
View File
@@ -0,0 +1,44 @@
local module = {}
function module.new(this,disconnector,context)
this.grip = _G.GetInstance(disconnector,"Grip")
this.binding = _G.GetInstance(disconnector,"Binding")
return this
end
module.run = _G.Protect(function(this,disconnector,context)
if this.control or 0 < 0.5 and context.queue(this,module.name,0.4) then
context.replicate(this,disconnector,module)
elseif context.controls[this.binding.Value] or 0 < 0.5 then
context.replicate(this,disconnector,module)
end
return true
end)
module.replication_delta = 0.25
function module.replicate(this,disconnector,context)
if _G.IsClient then return end
for _,weld in pairs(this.grip:GetJoints()) do
if
weld:IsA("WeldConstraint") and
weld.Name == "BuiltWeldConstraint" and
weld.Parent
then
weld:Destroy()
end
end
this.grip.Transparency = 1
task.delay(0.3,function() this.grip.Transparency = 0 end)
end
module.messages = {
control = function(this,disconnector,context,value)
this.control = tonumber(value)
end,
}
module.manifest = {
"Disconnector"
}
return module
+23
View File
@@ -0,0 +1,23 @@
local module = {}
function module.new(this,display,context)
this.screen = _G.GetInstance(display,"Screen")
this.gui = _G.GetInstance(this.screen,"Gui")
this.text = _G.GetInstance(this.gui,"Label")
this.text.Text = "<b>PSx Display Driver 1.0</b>\nStandby for message..."
end
module.messages = {
default = function(this,display,context,value)
this.text.Text = tostring(value)
end,
color = function(this,display,context,value)
pcall(function() this.text.TextColor3 = value end)
end,
}
module.manifest = {
"Display"
}
return module
+41
View File
@@ -0,0 +1,41 @@
local module = {}
function module.new(this,emitter,context)
return this
end
function module.idle(this,engine) return true end
local begin = tick()
module.run = _G.Protect(function(this,emitter,context)
return true
end)
module.replication_delta = 0.25
function module.replicate(this,emitter,context,details)
end
module.messages = {
control = function(this,object,context,value)
this.throttle = value
end,
starter = function(this,object,context,value)
this.starter = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Engine",
"Engine2",
"BigEngine",
"BigRadial",
"Radial",
"Boiler1",
"Boiler2"
}
return module
+121
View File
@@ -0,0 +1,121 @@
local module = {}
local function rpm(rot)
return (rot/(math.pi*2))*60
end
local function rot(hinge)
return hinge.CFrame.Rotation:PointToObjectSpace(hinge.AssemblyAngularVelocity).Magnitude
end
function module.new(this,engine,context)
this.powered = _G.GetValue(engine,"Powered",true)
this.inverted = _G.GetInstance(engine,"Inverted")
this.speed = _G.GetInstance(engine,"Speed")
this.rotor = _G.GetInstance(engine,"Hinge")
this.hinge = _G.GetInstance(engine,"HingeConstraint")
this.rotor_sound = this.rotor:FindFirstChild("RotorSound")
this.starter_sound = this.rotor:FindFirstChild("Starter")
this.starter_ratio = engine:GetAttribute("StarterRatio") or 1
if this.powered then
this.hinge.ActuatorType = Enum.ActuatorType.Motor
else
this.hinge.ActuatorType = Enum.ActuatorType.None
end
this.hinge.LimitsEnabled = false
this.torque = engine:GetScale() * 100000
return this
end
function module.idle(this,engine) return true end
local begin = tick()
module.run = _G.Protect(function(this,engine,context)
local throttle = this.throttle or context.controls[_G.GetValue(engine,"Binding","throttle")] or this.Throttle or 0
if this.inverted.Value then throttle = throttle * -1 end
local starter = math.abs(throttle) > 0.1
local target_speed = this.speed.Value * throttle
local current_speed = rot(this.rotor)
local accel
if math.abs(current_speed) > this.speed.Value * 0.1 then
-- Running
starter = false
accel = 40
else
-- Starting
accel = 40 * this.starter_ratio --this.torque * (math.clamp(math.noise(0.123,0.456,(begin-tick())*0.5),0,1) + 0.5) * 0.2
end
this.hinge.MotorMaxAcceleration = accel
if math.abs(throttle) > 0.05 then
this.hinge.AngularVelocity = target_speed
else
this.hinge.AngularVelocity = 0
end
if not this.torque then print(this) return false end
this.hinge.MotorMaxTorque = this.torque * ((engine:GetAttribute("Health") or 1) / (engine:GetAttribute("MaxHealth") or 1)) * (1/(this.speed.Value + 1)) * 60
context.replicate(this,engine,module,{Throttle = throttle, Starter = starter})
return true
end)
local legal_detail = {
Throttle = true,
Mixture = true,
Temperature = true,
Oil = true,
Air = true,
Fuel = true,
Coolant = true,
Power = true
}
module.replication_delta = 0.25
function module.replicate(this,engine,context,details)
for index,item in pairs(details) do
if legal_detail[index] then
this[index] = item
engine:SetAttribute(index,item)
end
end
local speed = math.abs(rot(this.rotor))
if details.Starter then
-- Starter on
if this.starter_sound then if not this.starter_sound.Playing then this.starter_sound:Play() end end
if this.rotor_sound then if this.rotor_sound.Playing then this.rotor_sound:Pause() end end
elseif speed > this.speed.Value * 0.1 then
-- Running
if this.rotor_sound then
if not this.rotor_sound.Playing then this.rotor_sound:Play() end
_G.Print(engine,this.rotor_sound.PlaybackSpeed,speed,this.speed.Value/60)
this.rotor_sound.PlaybackSpeed = math.clamp(math.abs(speed/150) * 1.5,0.5,1.5)
this.rotor_sound.Volume = math.clamp(math.abs(speed/150) * 0.05 + 0.05,0,0.1)
end
if this.starter_sound then if this.starter_sound.Playing then this.starter_sound:Stop() end end
else
if this.rotor_sound then if this.rotor_sound.Playing then this.rotor_sound:Stop() end end
if this.starter_sound then if this.starter_sound.Playing then this.starter_sound:Stop() end end
end
end
module.messages = {
control = function(this,object,context,value)
this.throttle = value
end,
starter = function(this,object,context,value)
this.starter = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Engine",
"Engine2",
"BigEngine",
"BigRadial",
"Radial",
"Jet",
"Motor",
"Motor1",
"BigSpinWheel",
"SpinWheel"
}
return module
+37
View File
@@ -0,0 +1,37 @@
local module = {}
local limit = 4
local bullet = require(_G.Modules.BulletModule)
function module.new(this,gun,context)
this.barrel = _G.GetInstance(gun,"Barrel")
this.binding = _G.GetInstance(gun,"Binding")
this.rate = _G.GetValue(gun,"Rate",0.15)
this.last = tick()
end
module.run = _G.Protect(function(this,gun,context)
if (this.control or context.controls[this.binding.Value] or 0) < 0.5 then return true end
if tick() - this.last < this.rate then return true end
-- Fire
bullet.fire(this.barrel)
this.last = tick()
this.barrel:ApplyImpulse(this.barrel.CFrame.RightVector * -500)
return true
end)
module.fire_rate = 0.05
module.messages = {
control = function(this,gun,context,value)
this.control = value
end,
}
module.manifest = {
"Gun",
"Cannon"
}
return module
+34
View File
@@ -0,0 +1,34 @@
local module = {}
function module.new(this,hinge,context)
this.hinge = _G.GetInstance(hinge,"HingeConstraint")
local function update()
if this.limited then this.hinge.LimitsEnabled = this.limited.Value end
if this.lower then this.hinge.LowerAngle = this.lower.Value end
if this.upper then this.hinge.UpperAngle = this.upper.Value end
end
if _G.IsServer then
this.limited = hinge:FindFirstChild("Limited")
this.upper = hinge:FindFirstChild("Upper")
this.lower = hinge:FindFirstChild("Lower")
if this.limited then this.limited.Changed:Connect(update) end
if this.lower then this.lower.Changed:Connect(update) end
if this.upper then this.upper.Changed:Connect(update) end
end
return this
end
module.manifest = {
"FlatServoRoll",
"FlatServoPitch",
"Servo",
"ControlSheet",
"NanoControlSheet",
"MetalServo",
"Spinner",
"Hinge",
"Rotator",
"RotatorPlate"
}
return module
+45
View File
@@ -0,0 +1,45 @@
local module = {}
function position(motor,phase,phases,target,origin)
motor.C0 = origin
local alpha = (phase)/(phases-1)
motor.C1 = CFrame.new():Lerp(target,alpha)
end
function module.new(this,lever,context)
this.motor = _G.GetInstance(lever,"Motor")
this.phase = _G.GetInstance(lever,"Phase")
this.phases = _G.GetInstance(lever,"Phases")
this.hold = _G.GetInstance(lever,"Hold")
this.click = _G.GetInstance(this.hold,"ClickDetector")
this.target = lever:GetAttribute("Target") or error("Needs target attribute.")
this.origin = lever:GetAttribute("Origin") or error("Needs target attribute.")
if _G.IsServer then
module.update(this,lever,context)
this.click.MouseClick:Connect(function()
if this.phase.Value == this.phases.Value - 1 then
this.phase.Value = 0
else
this.phase.Value += 1
end
print(this.phase.Value)
module.update(this,lever,context)
context.connected("ConnectorData",nil,lever.PrimaryPart,function(object)
print("firing",this.phase.Value)
context.message(object,nil,this.phase.Value/(this.phases.Value-1))
end)
end)
end
end
function module.update(this,lever,context)
if _G.IsServer then
position(this.motor,this.phase.Value,this.phases.Value,this.target,this.origin)
end
end
module.manifest = {
"Lever"
}
return module
+26
View File
@@ -0,0 +1,26 @@
local module = {}
local function setup(this,mesh,context)
this.mesh.MeshId = string.format("rbxassetid://%i",this.meshid.Value)
this.mesh.TextureId = string.format("rbxassetid://%i",this.textureid.Value)
this.mesh.Scale = mesh.Size * this.scale.Value
end
function module.new(this,mesh,context)
this.meshid = _G.GetInstance(mesh,"MeshId")
this.textureid = _G.GetInstance(mesh,"TextureId")
this.scale = _G.GetInstance(mesh,"Scale")
this.mesh = _G.GetInstance(mesh,"Mesh")
setup(this,mesh,context)
return this
end
function module.update(this,mesh,context)
setup(this,mesh,context)
end
module.manifest = {
"Prop"
}
return module
+40
View File
@@ -0,0 +1,40 @@
local module = {}
function module.new(this,piston,context)
this.servo = _G.GetInstance(piston,"Servo")
this.extension = _G.GetInstance(piston,"Extension")
this.binding = _G.GetInstance(piston,"Binding")
this.inverted = _G.GetInstance(piston,"Inverted")
this.force = _G.GetInstance(piston,"Force")
return this
end
local begin = tick()
module.run = _G.Protect(function(this,servo,context)
local control = (this.control or context.controls[this.binding.Value] or 0)
if this.inverted.Value then control = 1 - control end
this.servo.TargetPosition = servo.Extension.Value * control * servo:GetScale() + (this.offset or 0)
context.replicate(this,servo,module,this.servo.TargetPosition)
end)
module.replication_delta = 0.25
function module.replicate(this,servo,context,value)
servo.ServoMaxForce = this.force.Value * servo:GetScale() ^ 2
servo.TargetPosition = value
end
module.messages = {
control = function(this,object,context,value)
this.control = value
end,
offset = function(this,object,context,value)
this.offset = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Piston"
}
return module
+126
View File
@@ -0,0 +1,126 @@
local module = {}
local dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
local bias = 0.2
local propeller_power_mod = 0.1
local propeller_cut_mod = 5/2
local propeller_reaction_mod = 0.05
game.Workspace.CompensateDensity.Changed:Connect(function()
dens = game.Workspace.AirDensity * -game.Workspace.CompensateDensity.Value
end)
_G.Events.Command.Event:Connect(function(message)
local value = message:match("setprop%((.-)%)")
if not value then return end
if tonumber(value) then
game.Workspace.CompensateDensity = tonumber(value)
_G.Events.Notification:Fire("Set Prop Density","PropBias.Value = "..value)
else
_G.Events.Notification:Fire("Set Prop Density","Invalid number.")
end
end)
local function setup(this,propeller,context)
local angle = _G.GetValue(propeller,"Trim",30)
for _,child in pairs(propeller:GetChildren()) do
if child.Name == "Blade" then
local welds = child:GetJoints()
local joint = child:FindFirstChild("Weld")
if not child:FindFirstChild("Weld") then
local c0 = propeller.PrimaryPart.CFrame:ToObjectSpace(child.CFrame)
joint = Instance.new("Motor6D")
joint.Name = "Weld"
joint.Parent = child
joint.Part0 = propeller.PrimaryPart
joint.Part1 = child
joint.C0 = c0
end
local flip = 0
if angle < 0 then flip = 180 end
local axis = (propeller:GetAttribute("Axis") or Vector3.new(0,0,1)) * (math.rad(-angle+flip))
local rotation = CFrame.Angles(axis.X,axis.Y,axis.Z)
joint.C1 = rotation
for _,weld in pairs(welds) do if weld:IsA("WeldConstraint") then weld:Destroy() end end
end
end
end
function module.new(this,propeller,context)
local size = propeller:GetExtentsSize()
this.scale = size.X * size.Z * 1.5
this.length = size.X
this.power = _G.GetValue(propeller,"Force",1000) * this.scale * 0.01
this.direction = _G.GetValue(propeller,"Direction",CFrame.identity)
this.rotor = propeller:FindFirstChild("Spin") or propeller:FindFirstChild("Hinge")
this.trim = _G.GetValue(propeller,"Trim",30)
this.blur = propeller:FindFirstChild("Blur")
if not this.rotor then error("Missing rotor") end
this.force = _G.GetInstance(propeller,"VectorForce")
this.sound = _G.GetInstance(this.rotor,"PropSound")
this.sound:Play()
setup(this,propeller,context)
return this
end
function module.update(this,propeller,context)
print("upd")
setup(this,propeller,context)
end
function module.idle(this,engine) return true end
local a = 300
local function limit(f)
return math.tanh(f/a)*a
end
local begin = tick()
module.run = _G.Protect(function(this,propeller,context)
local angular_velocity = ((this.rotor.CFrame.Rotation * this.direction):PointToObjectSpace(this.rotor.AssemblyAngularVelocity)).X
local linear_velocity = angular_velocity * math.sin(math.rad(this.trim))
local air_velocity = this.rotor.CFrame.Rotation:PointToObjectSpace(this.rotor.AssemblyLinearVelocity).X
local real_velocity = limit((linear_velocity * propeller_cut_mod * this.length) - air_velocity)
local rpm = (angular_velocity/(math.pi*2))*60
local multiplier = (this.sound:GetAttribute("Multiplier") or 1) * 0.01
this.force.Force = Vector3.new(0,0,-real_velocity*this.power*propeller_power_mod*dens)
this.rotor:ApplyAngularImpulse(
(this.rotor.CFrame.Rotation * this.direction).RightVector
* -angular_velocity
* math.cos(math.rad(this.trim))
* propeller_reaction_mod)
context.replicate(this,propeller,module,math.abs(rpm*dens*multiplier))
return true
end)
module.replication_delta = 0.1
function module.replicate(this,propeller,context,value)
this.sound.PlaybackSpeed = math.clamp(value,0.1,1.5)
this.sound.Volume = math.clamp(value,0,this.sound:GetAttribute("Max") or 1)
if this.blur then
this.blur.Transparency = math.clamp(1-value,0.3,1)
end
for _,blade in pairs(propeller:GetChildren()) do
if blade.Name == "Blade" then
blade.Transparency = math.clamp(value,0,0.7)
end
end
end
module.messages = {
trim = function(this,object,context,value)
end
}
module.messages.default = module.messages.trim
module.manifest = {
"Propeller",
"Turbine",
"LargePropeller",
"BiPropeller",
"Jet"
}
return module
+46
View File
@@ -0,0 +1,46 @@
local module = {}
function module.new(this,rocket,context)
this.scale = rocket:GetExtentsSize().Magnitude -- same as with the equivalent statement on the wing! BRRRRRRRRRRR
this.binding = _G.GetInstance(rocket,"Binding")
this.exhaust = _G.GetInstance(rocket,"Exhaust")
this.sound = _G.GetInstance(this.exhaust,"Sound")
this.force = _G.GetInstance(rocket,"Force")
this.inverted = _G.GetInstance(rocket,"Inverted")
return this
end
function module.idle(this,engine) return true end
local begin = tick()
module.run = _G.Protect(function(this,engine,context)
local throttle = this.throttle or context.controls[_G.GetValue(engine,"Binding","throttle")] or this.Throttle or 0
if this.inverted.Value then throttle = -throttle end
context.replicate(this,engine,module,math.clamp(throttle,0,1))
return true
end)
module.replication_delta = 0.25
function module.replicate(this,rocket,context,throttle)
this.exhaust:ApplyImpulseAtPosition(this.exhaust.CFrame.RightVector * throttle * this.scale * this.force.Value * context.delta * -1000, this.exhaust.Position)
this.exhaust.Flame.Rate = throttle * 1000
this.sound.Volume = math.clamp(throttle,0,1)
if _G.IsServer and this.sound.Volume > 0 then this.sound:Play() else this.sound:Pause() end
end
module.messages = {
control = function(this,object,context,value)
this.throttle = value
end,
starter = function(this,object,context,value)
this.starter = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"Rocket",
"BigRocket"
}
return module
+91
View File
@@ -0,0 +1,91 @@
local module = {}
local partStateModule = require(_G.Modules.PartStateModule)
function setup(this,rope,context)
local function new(name,tab)
local part = Instance.new(name)
_G.Union(part,tab)
return part
end
local node1 = rope["1"]
local node2 = rope["2"]
if rope.Name == "Shaft" then -- Make two universal constraints
local constraint1 = new("UniversalConstraint",{
Parent = node1,
Attachment0 = new("Attachment",{
Parent = node1
}),
Attachment1 = new("Attachment",{
Parent = rope,
WorldCFrame = node1.CFrame
})
})
local constraint2 = new("UniversalConstraint",{
Parent = node2,
Attachment0 = new("Attachment",{
Parent = node2
}),
Attachment1 = new("Attachment",{
Parent = rope,
WorldCFrame = node2.CFrame
})
})
elseif rope.Name == "Rope" or rope.Name == "Rod2" then
rope.Transparency = 1
rope.CanCollide = false
rope.Massless = true
local name
if rope.name == "Rope" then name = "RopeConstraint" end
if rope.Name == "Rod2" then name = "RodConstraint" end
local constraint = new(name,{
Parent = rope,
Attachment0 = new("Attachment",{
Parent = node1
}),
Attachment1 = new("Attachment",{
Parent = node2
}),
Visible = true,
Thickness = rope.Size.Y
})
if rope.Length.Value == 0 then rope.Length.Value = constraint.CurrentDistance end
constraint.Length = rope.Length.Value
end
node1.CanCollide = true -- Resize and make nodes non-collide
node2.CanCollide = true
node1.Size = Vector3.one * rope.Size.Y
node2.Size = node1.Size
for index,joint in pairs(rope:GetJoints()) do -- Fix welds in new version
if joint:IsA("WeldConstraint") then
local other = joint.Part0
if joint.Part0 == rope then other = joint.Part1 end
local closest = node1
if (node2.Position-other.Position).Magnitude < (node1.Position-other.Position).Magnitude then closest = node2 end
new("WeldConstraint",{
Parent = rope,
Part0 = closest,
Part1 = other,
Name = "BuiltWeldConstraint"
})
if index > 1 then joint:Destroy() end
end
end
end
function module.new(this,rope,context)
if _G.IsServer then setup(this,rope,context) end
return this
end
module.manifest = {
"Rod2",
"Rope",
"Shaft"
}
return module
+99
View File
@@ -0,0 +1,99 @@
local module = {}
local function targetHinge(target,hinge,stationary,angle,negative)
local attachment = stationary:FindFirstChildWhichIsA("Attachment")
local o = attachment.WorldCFrame:PointToObjectSpace(target)
local x,y,z = CFrame.lookAlong(attachment.WorldCFrame.Position,attachment.WorldCFrame.UpVector,attachment.WorldCFrame.RightVector):ToObjectSpace(
CFrame.lookAt(
attachment.WorldCFrame.Position,
attachment.WorldCFrame:PointToWorldSpace(Vector3.new(0,o.Y,o.Z)*negative)
)):ToEulerAnglesYXZ()
hinge.TargetAngle = -math.deg(y) + angle
end
function module.new(this,servo,context)
this.scale = servo:GetExtentsSize().Magnitude
this.hinge = _G.GetInstance(servo,"HingeConstraint")
this.binding = _G.GetInstance(servo,"Binding")
this.inverted = servo:FindFirstChild("Inverted")
this.offset = servo:FindFirstChild("Offset")
this.range = servo:FindFirstChild("Angle")
this.target = servo:FindFirstChild("Target")
this.torque = servo:FindFirstChild("Torque")
return this
end
local begin = tick()
module.run = _G.Protect(function(this,servo,context)
local shouldInvert = 1
local shouldOffset = 0
local pitchTrim = 0
local angle = 30
if context.seat and context.seat:FindFirstChild("PitchTrim") then pitchTrim = context.seat.PitchTrim.Value end
if this.inverted and this.inverted.Value then shouldInvert = -1 end
if this.offset and this.offset.Value then shouldOffset = this.offset.Value + (this.starter or 0) end
if this.range and this.range.Value then angle = this.range.Value end
if this.torque and this.torque.Value then this.hinge.MotorMaxTorque = this.torque.Value * ((servo:GetAttribute("Health") or 1) / (servo:GetAttribute("MaxHealth") or 1)) end
local control = (this.control or context.controls[this.binding.Value])
if this.target and this.target.Value then
this.hinge.LimitsEnabled = false
if context.cursor then
targetHinge(context.cursor,this.hinge,this.hinge.Attachment1.Parent,this.offset.Value,shouldInvert)
end
elseif this.binding.Value == "auto" and context.seat then
local pos = context.seat:GetPivot():ToObjectSpace(this.hinge.Attachment1.WorldCFrame)
local dir = pos.RightVector
pos = pos.Position.Unit
this.hinge.LimitsEnabled = false
this.hinge.TargetAngle = ( (
-context.controls.pitch*dir.x*pos.Z
-context.controls.yaw*dir.y*pos.Z
+context.controls.roll*dir.x*pos.X
) * angle + pitchTrim*dir.x*pos.Z
) * shouldInvert + shouldOffset
elseif control then
if this.binding.Value == "pitch" then shouldOffset = shouldOffset + pitchTrim end
this.hinge.LimitsEnabled = true
this.hinge.TargetAngle = control * angle * shouldInvert + shouldOffset
end
local distance = (this.hinge.Attachment1.WorldCFrame.Position - this.hinge.Attachment1.WorldCFrame.Position).Magnitude
if distance < 2 then
context.replicate(this,servo,module,this.hinge.TargetAngle,this.hinge.LimitsEnabled)
return true
else
context.replicate(this,servo,module,nil,nil)
return false
end
end)
module.replication_delta = 0.25
function module.replicate(this,servo,context,angle)
if not angle then
this.hinge:Destroy()
else
this.hinge.TargetAngle = angle
end
end
module.messages = {
control = function(this,object,context,value)
this.control = value
end,
offset = function(this,object,context,value)
this.starter = value
end,
}
module.messages.default = module.messages.control
module.manifest = {
"FlatServoRoll",
"FlatServoPitch",
"Servo",
"ControlSheet",
"NanoControlSheet",
"MetalServo"
}
return module
+26
View File
@@ -0,0 +1,26 @@
local module = {}
function module.new(this,speaker,context)
this.main = _G.GetInstance(speaker,"Main")
this.tts = _G.GetInstance(this.main,"AudioTextToSpeech")
end
function module.replicate(this,speaker,context,value)
this.tts.Text = tostring(value or "") or ""
if _G.IsClient then
if this.tts.IsPlaying then return end
this.tts:Play()
end
end
module.messages = {
default = function(this,speaker,context,value)
context.replicate(this,speaker,module,value)
end
}
module.manifest = {
"Speaker"
}
return module
+27
View File
@@ -0,0 +1,27 @@
local module = {}
local spring_modifier = 1
local function setup(this,spring,context)
this.spring.Damping = _G.GetValue(spring,"Dampness",this.spring.Damping)
this.spring.Stiffness = _G.GetValue(spring,"Stiffness",this.spring.Stiffness) * spring_modifier * spring:GetScale()^3
this.spring.Visible = this.spring.Stiffness > 0
end
function module.new(this,spring,context)
this.spring = _G.GetInstance(spring,"SpringConstraint")
setup(this,spring,context)
end
function module.update(this,spring,context)
print("setup")
setup(this,spring,context)
end
module.update = module.new
module.manifest = {
"Coilover",
"Wheel"
}
return module
+38
View File
@@ -0,0 +1,38 @@
local module = {}
local cool_rate = 0.5
function module.new(this,turbine,context)
this.heat = 0
this.hinge = _G.GetInstance(turbine,"HingeConstraint")
this.speed = _G.GetInstance(turbine,"Speed")
this.inverted = _G.GetInstance(turbine,"Inverted")
end
function module.run(this,turbine,context)
this.heat = this.heat * (1 - cool_rate * context.delta) -- wrong but idc
local mul = 1
if this.inverted.Value then mul = -1 end
this.hinge.AngularVelocity = (1-(1/(1 + this.heat))) * this.speed.Value * mul
this.hinge.MotorMaxTorque = 10000 * this.speed.Value
context.replicate(this,turbine,module,this.heat)
return true
end
function module.replicate(this,turbine,context,heat)
this.heat = heat
end
module.update = module.new
module.messages = {
flux = function(this,turbine,context,heat)
this.heat = this.heat + heat
end,
}
module.messages.default = nil
module.manifest = {
"Turbine1"
}
return module