diff --git a/Character/Animate.server.lua b/Character/Animate.server.lua
new file mode 100644
index 0000000..431f879
--- /dev/null
+++ b/Character/Animate.server.lua
@@ -0,0 +1,26 @@
+local Players = game:GetService("Players")
+
+local character = script.Parent
+local player = Players:GetPlayerFromCharacter(character)
+
+local climbing = Instance.new("BoolValue")
+climbing.Name = "Climbing"
+climbing.Parent = character
+
+local setValue = Instance.new("RemoteEvent")
+setValue.Name = "SetValue"
+setValue.Parent = climbing
+
+local function onSetValue(requester, value)
+ if requester ~= player then
+ return
+ end
+
+ if typeof(value) ~= "boolean" then
+ return
+ end
+
+ climbing.Value = value
+end
+
+setValue.OnServerEvent:Connect(onSetValue)
\ No newline at end of file
diff --git a/Character/Bounciness.server.lua b/Character/Bounciness.server.lua
new file mode 100644
index 0000000..0b3e423
--- /dev/null
+++ b/Character/Bounciness.server.lua
@@ -0,0 +1,21 @@
+local Debris = game:GetService("Debris")
+
+local char = script.Parent
+local humanoid = char:WaitForChild("Humanoid")
+local head = char:WaitForChild("Head")
+
+local function onStateChanged(old,new)
+ if new.Name == "Landed" then
+ local velocity = humanoid.Torso.Velocity
+ local power = (-velocity.Y * workspace.Gravity) / 2
+
+ local force = Instance.new("BodyForce")
+ force.Name = "Bounce"
+ force.Force = Vector3.new(0,power,0)
+ force.Parent = head
+
+ Debris:AddItem(force, 1/30)
+ end
+end
+
+humanoid.StateChanged:connect(onStateChanged)
\ No newline at end of file
diff --git a/Character/DropHats/HatPickup.server.lua b/Character/DropHats/HatPickup.server.lua
new file mode 100644
index 0000000..fb4503b
--- /dev/null
+++ b/Character/DropHats/HatPickup.server.lua
@@ -0,0 +1,34 @@
+local handle = script.Parent
+local hat = handle:FindFirstChildWhichIsA("Accoutrement")
+local equipSignal
+
+local function onTouched(hit)
+ local char = hit:FindFirstAncestorWhichIsA("Model")
+
+ if char then
+ local hitHum = char:FindFirstChild("Humanoid")
+
+ if hitHum then
+ local existingHat = char:FindFirstChildWhichIsA("Accoutrement")
+
+ if existingHat == nil or existingHat == hat then
+ if equipSignal then
+ equipSignal:Disconnect()
+ equipSignal = nil
+ end
+
+ hat.Parent = workspace
+ handle.Parent = hat
+
+ handle:SetNetworkOwnershipAuto()
+ hitHum:AddAccessory(hat)
+
+ script:Destroy()
+ else
+ hat.Parent = workspace
+ end
+ end
+ end
+end
+
+equipSignal = handle.Touched:Connect(onTouched)
\ No newline at end of file
diff --git a/Character/DropHats/LocalDropHat.client.lua b/Character/DropHats/LocalDropHat.client.lua
new file mode 100644
index 0000000..853abf9
--- /dev/null
+++ b/Character/DropHats/LocalDropHat.client.lua
@@ -0,0 +1,15 @@
+local UserInputService = game:GetService("UserInputService")
+
+local server = script.Parent
+local dropHat = server:WaitForChild("DropHat")
+
+local function onInputBegan(input, gameProcessed)
+ if not gameProcessed then
+ local keyCode = input.KeyCode.Name
+ if keyCode == "Equals" or keyCode == "DPadDown" then
+ dropHat:FireServer()
+ end
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
\ No newline at end of file
diff --git a/Character/DropHats/init.server.lua b/Character/DropHats/init.server.lua
new file mode 100644
index 0000000..dc0374d
--- /dev/null
+++ b/Character/DropHats/init.server.lua
@@ -0,0 +1,50 @@
+local Players = game:GetService("Players")
+
+local char = script.Parent
+local torso = char:WaitForChild("HumanoidRootPart")
+
+local humanoid = char:WaitForChild("Humanoid")
+local hatPickup = script:WaitForChild("HatPickup")
+
+local dropHat = Instance.new("RemoteEvent")
+dropHat.Name = "DropHat"
+dropHat.Parent = script
+
+local function onDropHat(player)
+ local myPlayer = Players:GetPlayerFromCharacter(char)
+ assert(player == myPlayer, "Cannot drop hats unless it is your character.")
+
+ local dropPos = torso.CFrame * CFrame.new(0, 5.4, -8)
+
+ for _,hat in pairs(humanoid:GetAccessories()) do
+ local handle = hat:FindFirstChild("Handle")
+
+ if handle then
+ local newHandle = handle:Clone()
+
+ for _,joint in pairs(newHandle:GetJoints()) do
+ joint:Destroy()
+ end
+
+ newHandle.CFrame = dropPos
+ newHandle.Anchored = true
+ newHandle.CanCollide = false
+ newHandle.Parent = workspace
+
+ handle:Destroy()
+ hat.Parent = newHandle
+
+ wait(.1)
+
+ newHandle.Anchored = false
+ newHandle.CanCollide = true
+ newHandle:SetNetworkOwner(nil)
+
+ local pickup = hatPickup:Clone()
+ pickup.Parent = newHandle
+ pickup.Disabled = false
+ end
+ end
+end
+
+dropHat.OnServerEvent:Connect(onDropHat)
\ No newline at end of file
diff --git a/Character/EdgeWalking.client.lua b/Character/EdgeWalking.client.lua
new file mode 100644
index 0000000..0d53c76
--- /dev/null
+++ b/Character/EdgeWalking.client.lua
@@ -0,0 +1,45 @@
+local RunService = game:GetService("RunService")
+
+local char = script.Parent
+local rootPart = char:WaitForChild("HumanoidRootPart")
+
+local platform = Instance.new("Part")
+platform.Name = "NoForceField"
+platform.TopSurface = 0
+platform.BottomSurface = 0
+platform.BrickColor = BrickColor.new("Bright orange")
+platform.Size = Vector3.new(5, 1, 2)
+platform.Anchored = true
+platform.Transparency = 1
+
+local down = Vector3.new(0, -100, 0)
+local platformOffset = Vector3.new(0, -.5, 0)
+
+while wait() do
+ local start = rootPart.CFrame
+ local startPos = start.p
+ local startRay = Ray.new(startPos, start.lookVector * 5)
+
+ local hit, pos, norm = workspace:FindPartOnRay(startRay, char)
+ local floorCheckRay
+
+ local pass = false
+
+ if hit and hit.CanCollide and hit:IsGrounded() then
+ if hit:IsA("UnionOperation") or (not hit:IsA("Part") or hit.Shape.Name == "Block") then
+ local floorCheckRay = Ray.new(pos - (norm / 5), down)
+ local floor, floorPos = workspace:FindPartOnRayWithIgnoreList(floorCheckRay, {char, hit})
+
+ if floor and floor.CanCollide and startPos.Y - 2 > floorPos.Y then
+ floorPos = floorPos + platformOffset
+ platform.Parent = char
+ platform.CFrame = CFrame.new(Vector3.new(pos.X + norm.X, floorPos.Y, pos.Z + norm.Z),floorPos)
+ pass = true
+ end
+ end
+ end
+
+ if not pass then
+ platform.Parent = nil
+ end
+end
\ No newline at end of file
diff --git a/Character/FloorDrag.client.lua b/Character/FloorDrag.client.lua
new file mode 100644
index 0000000..2949c9c
--- /dev/null
+++ b/Character/FloorDrag.client.lua
@@ -0,0 +1,58 @@
+local RunService = game:GetService("RunService")
+
+local char = script.Parent
+local humanoid = char:WaitForChild("Humanoid")
+local rootPart = char:WaitForChild("HumanoidRootPart")
+local climbForce = rootPart:WaitForChild("ClimbForce")
+local rayDown = Vector3.new(0, -5000, 0)
+
+local function moveTowards(value, goal, rate)
+ if value < goal then
+ return math.min(goal, value + rate)
+ elseif value > goal then
+ return math.max(goal, value - rate)
+ else
+ return goal
+ end
+end
+
+local lastFloorLevel = 0
+
+local function getFloorLevel()
+ local origin = rootPart.Position
+ local ray = Ray.new(origin, rayDown)
+ local hit, pos = workspace:FindPartOnRay(ray, char)
+ return pos.Y, math.clamp(math.abs(pos.Y - origin.Y), -1, 1)
+end
+
+local lastLevel = getFloorLevel()
+local updateCon
+
+local function update()
+ if humanoid.Health == 0 then
+ updateCon:Disconnect()
+ return
+ end
+
+ local level, dist = getFloorLevel()
+
+ if humanoid.SeatPart then
+ humanoid.HipHeight = 0
+ lastLevel = level
+ return
+ end
+
+ local yVel = rootPart.Velocity.Y
+
+ if math.abs(yVel) > 8 then
+ local goal = math.sign(yVel)
+ humanoid.HipHeight = moveTowards(humanoid.HipHeight, goal, 0.1)
+ elseif lastLevel ~= level then
+ humanoid.HipHeight = math.sign(lastLevel - level) * math.clamp(dist - 3, 0, 1)
+ lastLevel = level
+ else
+ humanoid.HipHeight = humanoid.HipHeight * 0.925
+ end
+end
+
+updateCon = RunService.RenderStepped:Connect(update)
\ No newline at end of file
diff --git a/Character/GoofyBalance.server.lua b/Character/GoofyBalance.server.lua
new file mode 100644
index 0000000..109f10a
--- /dev/null
+++ b/Character/GoofyBalance.server.lua
@@ -0,0 +1,26 @@
+local char = script.Parent
+local humanoid = char:WaitForChild("Humanoid")
+
+local function onStateChanged(old,new)
+ if new == Enum.HumanoidStateType.RunningNoPhysics then
+ humanoid:ChangeState(Enum.HumanoidStateType.Running)
+ elseif new == Enum.HumanoidStateType.FallingDown then
+ humanoid:ChangeState("Ragdoll")
+
+ while wait(0.5) do
+ if humanoid.RootPart then
+ local velocity = humanoid.RootPart.Velocity
+
+ if velocity.Magnitude < 0.1 then
+ wait(2)
+ humanoid:ChangeState("GettingUp")
+ break
+ end
+ else
+ break
+ end
+ end
+ end
+end
+
+humanoid.StateChanged:Connect(onStateChanged)
\ No newline at end of file
diff --git a/Character/GoofyMotion.client.lua b/Character/GoofyMotion.client.lua
new file mode 100644
index 0000000..36d5f21
--- /dev/null
+++ b/Character/GoofyMotion.client.lua
@@ -0,0 +1,60 @@
+local RunService = game:GetService("RunService")
+local GameSettings = UserSettings():GetService("UserGameSettings")
+
+local char = script.Parent
+local humanoid = char:WaitForChild("Humanoid")
+local climbing = char:WaitForChild("Climbing")
+local rootPart = humanoid.RootPart
+
+local c = workspace.CurrentCamera
+local blankV3 = Vector3.new()
+local xz = Vector3.new(1,0,1)
+local bg = rootPart:FindFirstChild("FirstPersonGyro")
+
+local runState = Enum.HumanoidStateType.Running
+
+if not bg then
+ bg = Instance.new("BodyGyro")
+ bg.Name = "FirstPersonGyro"
+ bg.MaxTorque = Vector3.new(0,10e6,0)
+ bg.D = 100
+end
+
+local function toRotation(dir)
+ return CFrame.new(blankV3,dir)
+end
+
+local velocityThreshold = 200
+spawn(function ()
+ local threshold = char:WaitForChild("VelocityThreshold",5)
+ if threshold then
+ velocityThreshold = threshold.Value
+ end
+end)
+
+local function update()
+ local rotationType = GameSettings.RotationType
+ local seatPart = humanoid.SeatPart
+
+ if rotationType.Name == "CameraRelative" and not seatPart then
+ local dir = c.CFrame.lookVector * xz
+ bg.CFrame = toRotation(dir)
+ bg.Parent = rootPart
+ humanoid.AutoRotate = false
+ else
+ local state = humanoid:GetState()
+ local isRunning = (state == runState)
+ local isClimbing = climbing.Value
+ humanoid.AutoRotate = (isRunning or isClimbing)
+ bg.Parent = nil
+ end
+
+ if rootPart.Velocity.Magnitude > velocityThreshold and not seatPart then
+ humanoid:ChangeState("FallingDown")
+ end
+end
+
+humanoid.AutoRotate = false
+humanoid:SetStateEnabled("Climbing",false)
+RunService.RenderStepped:connect(update)
+c.FieldOfView = 65
\ No newline at end of file
diff --git a/Character/InputGateway.server.lua b/Character/InputGateway.server.lua
new file mode 100644
index 0000000..8cbc89d
--- /dev/null
+++ b/Character/InputGateway.server.lua
@@ -0,0 +1,18 @@
+local ServerStorage = game:GetService("ServerStorage")
+local inputGateway = ServerStorage:WaitForChild("InputGateway")
+local char = script.Parent
+
+local function onChildAdded(child)
+ if child:IsA("Tool") and not child:FindFirstChild("InputGateway") then
+ wait(.1)
+ local gateway = inputGateway:Clone()
+ gateway.Parent = child
+ end
+end
+
+local tool = char:FindFirstChildWhichIsA("Tool")
+if tool then
+ onChildAdded(tool)
+end
+
+char.ChildAdded:Connect(onChildAdded)
\ No newline at end of file
diff --git a/Character/JumpLimiter.client.lua b/Character/JumpLimiter.client.lua
new file mode 100644
index 0000000..9890da0
--- /dev/null
+++ b/Character/JumpLimiter.client.lua
@@ -0,0 +1,12 @@
+local char = script.Parent
+local humanoid = char:WaitForChild("Humanoid")
+
+local function onStateChanged(old,new)
+ if old == Enum.HumanoidStateType.Freefall and new == Enum.HumanoidStateType.Landed then
+ humanoid:SetStateEnabled("Jumping",false)
+ wait(0.5)
+ humanoid:SetStateEnabled("Jumping",true)
+ end
+end
+
+humanoid.StateChanged:Connect(onStateChanged)
diff --git a/Character/RetroClimbing.client.lua b/Character/RetroClimbing.client.lua
new file mode 100644
index 0000000..cda33e7
--- /dev/null
+++ b/Character/RetroClimbing.client.lua
@@ -0,0 +1,235 @@
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Setup
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local char = script.Parent
+
+local humanoid = char:WaitForChild("Humanoid")
+humanoid:SetStateEnabled("Climbing", false)
+
+local rootPart = humanoid.RootPart
+local bv = rootPart:FindFirstChild("ClimbForce")
+
+if not bv then
+ bv = Instance.new("BodyVelocity")
+ bv.Name = "ClimbForce"
+ bv.Parent = humanoid.RootPart
+end
+
+bv.MaxForce = Vector3.new()
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Climbing State
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local climbing = char:WaitForChild("Climbing")
+local setValue = climbing:WaitForChild("SetValue")
+
+local function onClimbing(value)
+ setValue:FireServer(value)
+end
+
+climbing.Changed:Connect(onClimbing)
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Debug Visuals
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local Debris = game:GetService("Debris")
+local isDevTest = false
+
+local DEBUG_COLOR_RED = Color3.new(1, 0, 0)
+local DEBUG_COLOR_YLW = Color3.new(1, 1, 0)
+local DEBUG_COLOR_GRN = Color3.new(0, 1, 0)
+
+local debugBox = Instance.new("BoxHandleAdornment")
+debugBox.Adornee = workspace.Terrain
+debugBox.Color3 = DEBUG_COLOR_RED
+debugBox.Visible = false
+debugBox.Parent = script
+
+local debugCylinder = Instance.new("CylinderHandleAdornment")
+debugCylinder.Color = BrickColor.new("Bright violet")
+debugCylinder.Adornee = workspace.Terrain
+debugCylinder.Height = 0.2
+debugCylinder.Radius = 1.0
+debugCylinder.Visible = false
+debugCylinder.Parent = script
+
+local debugSBox = Instance.new("SelectionBox")
+debugSBox.Color3 = DEBUG_COLOR_RED
+debugSBox.Parent = script
+
+local function drawRayIfDebugging(rayStart, look, length, color)
+ if isDevTest then
+ local line = Instance.new("LineHandleAdornment")
+ line.CFrame = CFrame.new(rayStart, rayStart + (look.Unit * length))
+ line.Adornee = workspace.Terrain
+ line.Length = length
+ line.Color3 = color
+ line.Thickness = 4
+ line.Parent = script
+
+ local cone = Instance.new("ConeHandleAdornment")
+ cone.CFrame = CFrame.new(rayStart + (look.Unit * (length - 0.32)), rayStart + (look.Unit * length))
+ cone.Adornee = workspace.Terrain
+ cone.Color3 = color
+ cone.Radius = 1 / 10
+ cone.Height = 1 / 3
+ cone.Parent = script
+
+ Debris:AddItem(line, .5)
+ Debris:AddItem(cone, .5)
+ end
+end
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Main Climbing Logic
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local searchDepth = 0.7
+local maxClimbDist = 2.45
+local sampleSpacing = 1 / 7
+local lowLadderSearch = 2.7
+local stepForwardFrames = 0
+local ladderSearchDist = 2.0
+
+local running = Enum.HumanoidStateType.Running
+local freefall = Enum.HumanoidStateType.Freefall
+
+local function findPartInLadderZone()
+ debug.profilebegin("FastClimbCheck")
+ --
+
+ local cf = rootPart.CFrame
+
+ local top = -humanoid.HipHeight
+ local bottom = -lowLadderSearch + top
+ local radius = 0.5 * ladderSearchDist
+
+ local center = cf.Position + (cf.LookVector * ladderSearchDist * 0.5)
+ local min = Vector3.new(-radius, bottom, -radius)
+ local max = Vector3.new(radius, top, radius)
+
+ local extents = Region3.new(center + min, center + max)
+ local parts = workspace:FindPartsInRegion3(extents, char)
+
+ if isDevTest then
+ if #parts > 0 then
+ debugBox.Visible = false
+ debugSBox.Visible = true
+ debugSBox.Adornee = parts[1]
+ else
+ debugBox.Visible = true
+ debugSBox.Visible = false
+
+ debugBox.Size = extents.Size
+ debugBox.CFrame = extents.CFrame
+
+ debugCylinder.Visible = false
+ end
+ end
+
+ --
+ debug.profileend()
+ return #parts > 0
+end
+
+local function findLadder()
+ if not findPartInLadderZone() then
+ return false
+ end
+
+ debug.profilebegin("ExpensiveClimbCheck")
+
+ local torsoCoord = rootPart.CFrame
+ local torsoLook = torsoCoord.LookVector
+
+ local firstSpace = 0
+ local firstStep = 0
+
+ local lookForSpace = true
+ local lookForStep = false
+
+ local debugColor = DEBUG_COLOR_YLW
+ local topRay = math.floor(lowLadderSearch / sampleSpacing)
+
+ for i = 1, topRay do
+ local distFromBottom = i * sampleSpacing
+ local originOnTorso = Vector3.new(0, -lowLadderSearch + distFromBottom, 0)
+
+ local casterOrigin = torsoCoord.Position + originOnTorso
+ local casterDirection = torsoLook * ladderSearchDist
+
+ local ray = Ray.new(casterOrigin, casterDirection)
+ local hitPrim, hitLoc = workspace:FindPartOnRay(ray, char)
+
+ -- make trusses climbable.
+ if hitPrim and hitPrim:IsA("TrussPart") then
+ return true
+ end
+
+ local mag = (hitLoc - casterOrigin).Magnitude
+
+ if mag < searchDepth then
+ if lookForSpace then
+ debugColor = DEBUG_COLOR_GRN
+ firstSpace = distFromBottom
+
+ lookForSpace = false
+ lookForStep = true
+ end
+ elseif lookForStep then
+ firstStep = distFromBottom - firstSpace
+ debugColor = DEBUG_COLOR_RED
+ lookForStep = false
+ end
+
+ drawRayIfDebugging(casterOrigin, casterDirection, mag, debugColor)
+ end
+
+ local found = (firstSpace < maxClimbDist and firstStep > 0 and firstStep < maxClimbDist)
+ debugCylinder.Visible = isDevTest and found
+
+ if debugCylinder.Visible then
+ local y = Vector3.FromAxis('Y')
+ local pos = torsoCoord.Position + Vector3.new(0, 5, 0)
+ debugCylinder.CFrame = CFrame.new(pos, pos + y)
+ end
+
+ debug.profileend()
+ return found
+end
+
+while wait() do
+ local canClimb = false
+
+ local state = humanoid:GetState()
+ local speed = humanoid.WalkSpeed
+
+ if state == freefall or state == running then
+ canClimb = findLadder()
+ end
+
+ if canClimb then
+ local climbSpeed = speed * 0.7
+ bv.Velocity = Vector3.new(0, climbSpeed, 0)
+ bv.MaxForce = Vector3.new(climbSpeed * 100, 10e6, climbSpeed * 100)
+ else
+ if climbing.Value then
+ stepForwardFrames = 2
+ end
+
+ bv.MaxForce = Vector3.new()
+ end
+
+ if stepForwardFrames > 0 then
+ local cf = rootPart.CFrame
+ humanoid:Move(cf.LookVector)
+ stepForwardFrames = stepForwardFrames - 1
+ end
+
+ climbing.Value = canClimb
+end
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Character/Sound.server.lua b/Character/Sound.server.lua
new file mode 100644
index 0000000..e6ebcdb
--- /dev/null
+++ b/Character/Sound.server.lua
@@ -0,0 +1,68 @@
+-- util
+
+function waitForChild(parent, childName)
+ local child = parent:findFirstChild(childName)
+ if child then return child end
+ while true do
+ child = parent.ChildAdded:wait()
+ if child.Name==childName then return child end
+ end
+end
+
+function newSound(id)
+ local sound = Instance.new("Sound")
+ sound.SoundId = id
+ sound.archivable = false
+ sound.Parent = script.Parent.Head
+ return sound
+end
+
+-- declarations
+
+local sDied = newSound("rbxasset://sounds/uuhhh.wav")
+local sFallingDown = newSound("rbxasset://sounds/splat.wav")
+local sFreeFalling = newSound("rbxasset://sounds/swoosh.wav")
+local sGettingUp = newSound("rbxasset://sounds/hit.wav")
+local sJumping = newSound("rbxasset://sounds/button.wav")
+local sRunning = newSound("rbxasset://sounds/bfsl-minifigfoots1.mp3")
+sRunning.Looped = true
+
+local Figure = script.Parent
+local Head = waitForChild(Figure, "Head")
+local Humanoid = waitForChild(Figure, "Humanoid")
+--local Climbing = Figure:WaitForChild("Climbing")
+
+-- functions
+
+function onDied()
+ sDied:Play()
+end
+
+function onJumping()
+ sJumping:Play()
+ wait(0.2)
+ sJumping:Stop()
+end
+
+function onState(state, sound)
+ sound.TimePosition = 0
+ sound.Playing = state
+end
+
+function onRunning(speed)
+ sRunning.Playing = (speed>0.1)
+end
+
+-- connect up
+
+Humanoid.Died:connect(onDied)
+Humanoid.Running:connect(onRunning)
+Humanoid.Jumping:connect(onJumping)
+Humanoid.GettingUp:connect(function(state) onState(state, sGettingUp) end)
+Humanoid.FreeFalling:connect(function(state)
+ --if not Climbing.Value then
+ onState(state, sFreeFalling)
+ --end
+end)
+
+Humanoid.FallingDown:connect(function(state) onState(state, sFallingDown) end)
diff --git a/Character/TeamColors.server.lua b/Character/TeamColors.server.lua
new file mode 100644
index 0000000..bd8f7fd
--- /dev/null
+++ b/Character/TeamColors.server.lua
@@ -0,0 +1,35 @@
+local CollectionService = game:GetService("CollectionService")
+local Players = game:GetService("Players")
+
+local char = script.Parent
+local player = Players:GetPlayerFromCharacter(char)
+local teamListener = player:GetPropertyChangedSignal("TeamColor")
+local bodyColors = char:WaitForChild("BodyColors")
+
+local teamColors = Instance.new("BodyColors")
+teamColors.Name = "TeamColors"
+teamColors.HeadColor = BrickColor.new("Bright yellow")
+teamColors.LeftArmColor = BrickColor.Black()
+teamColors.LeftLegColor = BrickColor.Black()
+teamColors.RightArmColor = BrickColor.Black()
+teamColors.RightLegColor = BrickColor.Black()
+
+CollectionService:AddTag(teamColors, "RespectCharacterAsset")
+
+local function onTeamChanged()
+ local team = player.Team
+ if team then
+ teamColors.TorsoColor = player.TeamColor
+ bodyColors.Parent = nil
+
+ if not CollectionService:HasTag(team, "NoAutoColor") then
+ teamColors.Parent = char
+ end
+ else
+ teamColors.Parent = nil
+ bodyColors.Parent = char
+ end
+end
+
+onTeamChanged()
+teamListener:Connect(onTeamChanged)
\ No newline at end of file
diff --git a/Character/ToolSoundGlitch.server.lua b/Character/ToolSoundGlitch.server.lua
new file mode 100644
index 0000000..92a2d97
--- /dev/null
+++ b/Character/ToolSoundGlitch.server.lua
@@ -0,0 +1,43 @@
+-- This replicates an old sound bug that used to occur with tools back then.
+
+local CollectionService = game:GetService("CollectionService")
+local Debris = game:GetService("Debris")
+
+local char = script.Parent
+local torso = char:WaitForChild("Torso")
+local marked = {}
+
+local function processHandle(handle)
+ for _,child in pairs(handle:GetChildren()) do
+ if child:IsA("Sound") then
+ if not marked[child.SoundId] then
+ marked[child.SoundId] = true
+ else
+ local replica = child:Clone()
+ replica.Name = "ToolSoundGlitch"
+ replica.MaxDistance = 0
+ replica.Parent = torso
+
+ CollectionService:AddTag(replica, "ToolSoundGlitch")
+ replica:Play()
+
+ replica.Ended:connect(function ()
+ Debris:AddItem(replica, 1)
+ end)
+ end
+ end
+ end
+end
+
+local function onChild(child)
+ if child:IsA("Tool") then
+ local handle = child:FindFirstChild("Handle")
+
+ if handle then
+ processHandle(handle)
+ end
+ end
+end
+
+char.ChildAdded:connect(onChild)
+char.ChildRemoved:connect(onChild)
\ No newline at end of file
diff --git a/Client/Animator.client.lua b/Client/Animator.client.lua
new file mode 100644
index 0000000..661e3a6
--- /dev/null
+++ b/Client/Animator.client.lua
@@ -0,0 +1,262 @@
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Services
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local CollectionService = game:GetService("CollectionService")
+local RunService = game:GetService("RunService")
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Animator Data
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local Animators = {}
+
+local function createAnimator(humanoid)
+ local Figure = humanoid.Parent
+ local Torso = Figure:WaitForChild("Torso")
+ local Climbing = Figure:WaitForChild("Climbing")
+
+ local animator = {}
+ animator.Joints = {}
+
+ do
+ local joints =
+ {
+ RightShoulder = Torso:WaitForChild("Right Shoulder", 5);
+ LeftShoulder = Torso:WaitForChild("Left Shoulder", 5);
+ RightHip = Torso:WaitForChild("Right Hip", 5);
+ LeftHip = Torso:WaitForChild("Left Hip", 5);
+ }
+
+ if not (joints.RightShoulder and joints.LeftShoulder) then
+ return
+ end
+
+ if not (joints.RightHip and joints.LeftHip) then
+ return
+ end
+
+ for name, joint in pairs(joints) do
+ local object =
+ {
+ JointObject = joint;
+ MaxVelocity = joint.MaxVelocity;
+ DesiredAngle = joint.DesiredAngle;
+ CurrentAngle = joint.CurrentAngle;
+ }
+
+ animator.Joints[name] = object
+ end
+ end
+
+ local joints = animator.Joints
+
+ local pi = math.pi
+ local sin = math.sin
+
+ local pose = "Standing"
+ local toolAnim = "None"
+ local toolAnimTime = 0
+
+ local RightShoulder = joints.RightShoulder
+ local LeftShoulder = joints.LeftShoulder
+
+ local RightHip = joints.RightHip
+ local LeftHip = joints.LeftHip
+
+ function animator:SetMaxVelocities(value)
+ RightShoulder.MaxVelocity = value
+ LeftShoulder.MaxVelocity = value
+
+ RightHip.MaxVelocity = value
+ LeftHip.MaxVelocity = value
+ end
+
+ function animator:Update()
+ local now = tick()
+
+ if Climbing.Value then
+ pose = "Climbing"
+ else
+ local stateType = humanoid:GetState()
+ pose = stateType.Name
+
+ if pose == "Running" then
+ local speed = humanoid.WalkSpeed
+ local movement = (Torso.Velocity * Vector3.new(1, 0, 1)).Magnitude
+
+ if (speed * movement) < 1 then
+ pose = "Standing"
+ end
+ end
+ end
+
+ if pose == "Jumping" then
+ self:SetMaxVelocities(.5)
+
+ RightShoulder.DesiredAngle = 1
+ LeftShoulder.DesiredAngle = -1
+
+ RightHip.DesiredAngle = 0
+ LeftHip.DesiredAngle = 0
+ elseif pose == "Freefall" then
+ self:SetMaxVelocities(.5)
+
+ RightShoulder.DesiredAngle = pi
+ LeftShoulder.DesiredAngle = -pi
+
+ RightHip.DesiredAngle = 0
+ LeftHip.DesiredAngle = 0
+ elseif pose == "Seated" then
+ self:SetMaxVelocities(.15)
+
+ RightShoulder.DesiredAngle = pi / 2
+ LeftShoulder.DesiredAngle = -pi / 2
+
+ RightHip.DesiredAngle = pi / 2
+ LeftHip.DesiredAngle = -pi / 2
+ else
+ local climbFudge = 0
+ local amplitude = .1
+ local frequency = 1
+
+ if pose == "Running" then
+ self:SetMaxVelocities(0.15)
+ amplitude = 1
+ frequency = 9
+ elseif pose == "Climbing" then
+ self:SetMaxVelocities(0.5)
+ climbFudge = pi
+
+ amplitude = 1
+ frequency = 9
+ end
+
+ local desiredAngle = amplitude * sin(now * frequency)
+
+ RightShoulder.DesiredAngle = desiredAngle + climbFudge
+ LeftShoulder.DesiredAngle = desiredAngle - climbFudge
+
+ RightHip.DesiredAngle = -desiredAngle
+ LeftHip.DesiredAngle = -desiredAngle
+
+ local tool = Figure:FindFirstChildWhichIsA("Tool")
+
+ if tool and tool.RequiresHandle and not CollectionService:HasTag(tool, "Flag") then
+ local animString = tool:FindFirstChild("toolanim")
+
+ if animString and animString:IsA("StringValue") then
+ -- apply tool animation
+ toolAnim = animString.Value
+ toolAnimTime = now + .3
+
+ -- delete event sender
+ animString:Destroy()
+ end
+
+ if now > toolAnimTime then
+ toolAnimTime = 0
+ toolAnim = "None"
+ end
+
+ if toolAnim == "None" then
+ RightShoulder.DesiredAngle = pi / 2
+ elseif toolAnim == "Slash" then
+ RightShoulder.MaxVelocity = 0.5
+ RightShoulder.DesiredAngle = 0
+ elseif toolAnim == "Lunge" then
+ self:SetMaxVelocities(0.5)
+
+ RightShoulder.DesiredAngle = pi / 2
+ RightHip.DesiredAngle = pi / 2
+
+ LeftShoulder.DesiredAngle = 1
+ LeftHip.DesiredAngle = 1
+ end
+ else
+ toolAnim = "None"
+ toolAnimTime = 0
+ end
+ end
+ end
+
+ return animator
+end
+
+local function onAnimatorAdded(humanoid)
+ if humanoid:IsA("Humanoid") then
+ local animator = createAnimator(humanoid)
+ Animators[humanoid] = animator
+ end
+end
+
+local function onAnimatorRemoved(humanoid)
+ if Animators[humanoid] then
+ Animators[humanoid] = nil
+ end
+end
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Collection Handler
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local animTag = "Animator"
+
+local animAdded = CollectionService:GetInstanceAddedSignal(animTag)
+local animRemoved = CollectionService:GetInstanceRemovedSignal(animTag)
+
+for _,humanoid in pairs(CollectionService:GetTagged(animTag)) do
+ spawn(function ()
+ onAnimatorAdded(humanoid)
+ end)
+end
+
+animAdded:Connect(onAnimatorAdded)
+animRemoved:Connect(onAnimatorRemoved)
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Motor Angle Updater
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local desiredFPS = 1 / 30 -- The framerate that would be expected given the MaxVelocity in use.
+local lastUpdate = tick()
+
+local function updateAnimations(deltaTime)
+ local velocityAdjust = (1 / desiredFPS) * deltaTime
+
+ for humanoid, animator in pairs(Animators) do
+ -- Update the motor states
+ animator:Update()
+
+ -- Step the motor angles
+ for name, jointData in pairs(animator.Joints) do
+ local joint = jointData.JointObject
+ local maxVelocity = jointData.MaxVelocity
+
+ local desiredAngle = jointData.DesiredAngle
+ local currentAngle = jointData.CurrentAngle
+
+ -- Adjust the MaxVelocity based on the current framerate
+ maxVelocity = math.abs(maxVelocity * velocityAdjust)
+
+ -- Update the CurrentAngle
+ local delta = (desiredAngle - currentAngle)
+
+ if math.abs(delta) < maxVelocity then
+ currentAngle = desiredAngle
+ elseif delta > 0 then
+ currentAngle = currentAngle + maxVelocity
+ else
+ currentAngle = currentAngle - maxVelocity
+ end
+
+ -- Apply the motor transform
+ joint.Transform = CFrame.Angles(0, 0, currentAngle)
+ jointData.CurrentAngle = currentAngle
+ end
+ end
+end
+
+RunService:BindToRenderStep("UpdateAnimations", 301, updateAnimations)
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Client/Camera/Main.lua b/Client/Camera/Main.lua
new file mode 100644
index 0000000..a53faf4
--- /dev/null
+++ b/Client/Camera/Main.lua
@@ -0,0 +1,1042 @@
+local Players = game:GetService("Players")
+local UserInputService = game:GetService("UserInputService")
+local StarterGui = game:GetService("StarterGui")
+local GuiService = game:GetService("GuiService")
+local ContextActionService = game:GetService("ContextActionService")
+local TeleportService = game:GetService("TeleportService")
+local Debris = game:GetService("Debris")
+
+local LocalPlayer = Players.LocalPlayer
+local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")
+local GameSettings = UserSettings():GetService("UserGameSettings")
+
+local math_abs = math.abs
+local math_asin = math.asin
+local math_atan2 = math.atan2
+local math_floor = math.floor
+local math_min = math.min
+local math_max = math.max
+local math_pi = math.pi
+local math_rad = math.rad
+local Vector2_new = Vector2.new
+local Vector3_new = Vector3.new
+local CFrame_Angles = CFrame.Angles
+local CFrame_new = CFrame.new
+
+local MIN_Y = math_rad(-80)
+local MAX_Y = math_rad(80)
+
+local ZERO_VECTOR2 = Vector2_new()
+local ZERO_VECTOR3 = Vector3_new()
+local UP_VECTOR = Vector3_new(0, 1, 0)
+local XZ_VECTOR = Vector3_new(1, 0, 1)
+
+local TOUCH_SENSITIVTY = Vector2_new(math_pi*2.25, math_pi*2)
+local MOUSE_SENSITIVITY = Vector2_new(math_pi*4, math_pi*1.9)
+
+local THUMBSTICK_DEADZONE = 0.2
+local DEADZONE = 0.1
+local ZOOM_FACTOR = 0.25
+
+local humanoid
+
+local function findPlayerHumanoid(player)
+ local character = player and player.Character
+ if character then
+ if not (humanoid and humanoid.Parent == character) then
+ humanoid = character:FindFirstChildOfClass("Humanoid")
+ end
+ end
+ return humanoid
+end
+
+local function clamp(low, high, num)
+ return (num > high and high or num < low and low or num)
+end
+
+local function findAngleBetweenXZVectors(vec2, vec1)
+ return math_atan2(vec1.X*vec2.Z-vec1.Z*vec2.X, vec1.X*vec2.X + vec1.Z*vec2.Z)
+end
+
+local function IsFinite(num)
+ return num == num and num ~= 1/0 and num ~= -1/0
+end
+
+local function SCurveTranform(t)
+ t = clamp(-1,1,t)
+ if t >= 0 then
+ return (.35*t) / (.35 - t + 1)
+ end
+ return -((.8*-t) / (.8 + t + 1))
+end
+
+local function toSCurveSpace(t)
+ return (1 + DEADZONE) * (2*math.abs(t) - 1) - DEADZONE
+end
+
+local function fromSCurveSpace(t)
+ return t/2 + 0.5
+end
+
+local function gamepadLinearToCurve(thumbstickPosition)
+ local function onAxis(axisValue)
+ local sign = 1
+ if axisValue < 0 then
+ sign = -1
+ end
+ local point = fromSCurveSpace(SCurveTranform(toSCurveSpace(math_abs(axisValue))))
+ point = point * sign
+ return clamp(-1, 1, point)
+ end
+ return Vector2_new(onAxis(thumbstickPosition.x), onAxis(thumbstickPosition.y))
+end
+
+-- Reset the camera look vector when the camera is enabled for the first time
+local SetCameraOnSpawn = true
+local this = {}
+
+local isFirstPerson = false
+local isRightMouseDown = false
+local isMiddleMouseDown = false
+
+this.Enabled = false
+this.RotateInput = ZERO_VECTOR2
+this.DefaultZoom = 10
+this.activeGamepad = nil
+this.PartSubjectHack = nil
+
+function this:GetHumanoid()
+ local player = Players.LocalPlayer
+ return findPlayerHumanoid(player)
+end
+
+function this:GetHumanoidRootPart()
+ local humanoid = this:GetHumanoid()
+ return humanoid and humanoid.Torso
+end
+
+function this:GetSubjectPosition()
+ local camera = workspace.CurrentCamera
+ local result = camera.Focus.p
+
+ local cameraSubject = camera and camera.CameraSubject
+ if cameraSubject then
+ if cameraSubject:IsA("Humanoid") then
+ local char = cameraSubject.Parent
+ if char then
+ local head = char:FindFirstChild("Head")
+ if head and head:IsA("BasePart") then
+ result = head.Position
+ end
+ end
+ if this.PartSubjectHack then
+ this:ZoomCamera(this.PartSubjectHack)
+ this.PartSubjectHack = nil
+ this:UpdateMouseBehavior()
+ end
+ elseif cameraSubject:IsA("BasePart") then
+ result = cameraSubject.Position
+ if not this.PartSubjectHack then
+ this.PartSubjectHack = this:GetCameraZoom()
+ this:ZoomCamera(10)
+ this:UpdateMouseBehavior()
+ end
+ end
+ end
+
+ return result
+end
+
+function this:GetCameraLook()
+ return workspace.CurrentCamera and workspace.CurrentCamera.CFrame.lookVector or Vector3.new(0,0,1)
+end
+
+function this:GetCameraZoom()
+ if this.currentZoom == nil then
+ local player = Players.LocalPlayer
+ this.currentZoom = player and clamp(player.CameraMinZoomDistance, player.CameraMaxZoomDistance, this.DefaultZoom) or this.DefaultZoom
+ end
+ return this.currentZoom
+end
+
+function this:GetCameraActualZoom()
+ local camera = workspace.CurrentCamera
+ if camera then
+ return (camera.CFrame.p - camera.Focus.p).Magnitude
+ end
+end
+
+function this:ViewSizeX()
+ local result = 1024
+ local camera = workspace.CurrentCamera
+ if camera then
+ result = camera.ViewportSize.X
+ end
+ return result
+end
+
+function this:ViewSizeY()
+ local result = 768
+ local camera = workspace.CurrentCamera
+ if camera then
+ result = camera.ViewportSize.Y
+ end
+ return result
+end
+
+function this:ScreenTranslationToAngle(translationVector)
+ local screenX = this:ViewSizeX()
+ local screenY = this:ViewSizeY()
+ local xTheta = (translationVector.x / screenX)
+ local yTheta = (translationVector.y / screenY)
+ return Vector2_new(xTheta, yTheta)
+end
+
+function this:MouseTranslationToAngle(translationVector)
+ local xTheta = (translationVector.x / 1920)
+ local yTheta = (translationVector.y / 1200)
+ return Vector2_new(xTheta, yTheta)
+end
+
+function this:RotateVector(startVector, xyRotateVector)
+ local startCFrame = CFrame_new(ZERO_VECTOR3, startVector)
+ local resultLookVector = (CFrame_Angles(0, -xyRotateVector.x, 0) * startCFrame * CFrame_Angles(-xyRotateVector.y,0,0)).lookVector
+ return resultLookVector, Vector2_new(xyRotateVector.x, xyRotateVector.y)
+end
+
+function this:RotateCamera(startLook, xyRotateVector)
+ local startVertical = math_asin(startLook.y)
+ local yTheta = clamp(-MAX_Y + startVertical, -MIN_Y + startVertical, xyRotateVector.y)
+ return self:RotateVector(startLook, Vector2_new(xyRotateVector.x, yTheta))
+end
+
+function this:IsInFirstPerson()
+ return isFirstPerson
+end
+
+function this:UpdateMouseBehavior()
+ if isFirstPerson or this.PartSubjectHack then
+ GameSettings.RotationType = Enum.RotationType.CameraRelative
+ UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
+ else
+ GameSettings.RotationType = Enum.RotationType.MovementRelative
+ if isRightMouseDown or isMiddleMouseDown then
+ UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
+ else
+ UserInputService.MouseBehavior = Enum.MouseBehavior.Default
+ end
+ end
+end
+
+function this:PlayTick()
+ local now = tick()
+ local lastTickSound = this.LastTickSound
+ if not lastTickSound then
+ lastTickSound = 0
+ end
+
+ if (now - lastTickSound) > .03 then
+ local s = Instance.new("Sound")
+ s.SoundId = "rbxasset://sounds/switch3.wav"
+ s.Parent = script
+ s:Play()
+ Debris:AddItem(s,1)
+ this.LastTickSound = now
+ end
+end
+
+function this:ZoomCamera(desiredZoom)
+ this.currentZoom = clamp(0.25, 400, desiredZoom)
+ isFirstPerson = self:GetCameraZoom() < 1.5
+
+ -- set mouse behavior
+ self:UpdateMouseBehavior()
+ return self:GetCameraZoom()
+end
+
+function this:ZoomCameraBy(input)
+ if TeleportService:GetTeleportSetting("FPSCapTo30") then
+ input = input * 1.5
+ end
+
+ local zoom = this:GetCameraActualZoom()
+ if zoom then
+ if input > 0 then
+ zoom = math.max( 1, zoom / (1 + ZOOM_FACTOR*input))
+ elseif input < 0 then
+ zoom = math.min(5000, zoom * (1 - ZOOM_FACTOR*input))
+ end
+ self:ZoomCamera(zoom)
+ end
+
+ self:PlayTick()
+ return self:GetCameraZoom()
+end
+
+function this:ZoomCameraFixedBy(zoomIncrement)
+ return self:ZoomCamera(self:GetCameraZoom() + zoomIncrement)
+end
+
+------------------------
+---- Input Events ----
+------------------------
+
+do
+ local startPos = nil
+ local lastPos = nil
+ local panBeginLook = nil
+ local lastTapTime = nil
+
+ local fingerTouches = {}
+ local NumUnsunkTouches = 0
+
+ local inputStartPositions = {}
+ local inputStartTimes = {}
+
+ local StartingDiff = nil
+ local pinchBeginZoom = nil
+
+ local dynamicThumbstickFrame = nil
+ local flaggedDynamic = {}
+
+ local function getDynamicThumbstickFrame()
+ if dynamicThumbstickFrame and dynamicThumbstickFrame:IsDescendantOf(game) then
+ return dynamicThumbstickFrame
+ else
+ local touchGui = PlayerGui:FindFirstChild("TouchGui")
+ if not touchGui then return nil end
+
+ local touchControlFrame = touchGui:FindFirstChild("TouchControlFrame")
+ if not touchControlFrame then return nil end
+
+ dynamicThumbstickFrame = touchControlFrame:FindFirstChild("DynamicThumbstickFrame")
+ return dynamicThumbstickFrame
+ end
+ end
+
+ this.ZoomEnabled = true
+ this.PanEnabled = true
+ this.KeyPanEnabled = true
+
+ local function inputIsDynamic(input)
+ if flaggedDynamic[input] ~= nil then
+ return flaggedDynamic[input]
+ end
+
+ if GameSettings.TouchMovementMode ~= Enum.TouchMovementMode.DynamicThumbstick then
+ return false
+ end
+
+ local df = getDynamicThumbstickFrame()
+ if not df then return end
+
+ local pos = input.Position
+ local p0 = df.AbsolutePosition
+ local p1 = p0 + df.AbsoluteSize
+
+ if p0.X <= pos.X and p0.Y <= pos.Y then
+ if pos.X <= p1.X and pos.Y <= p1.Y then
+ flaggedDynamic[input] = true
+ return true
+ end
+ end
+
+ flaggedDynamic[input] = false
+ return false
+ end
+
+ local function OnTouchBegan(input, processed)
+ if not inputIsDynamic(input) then
+ fingerTouches[input] = processed
+ if not processed then
+
+ inputStartPositions[input] = input.Position
+ inputStartTimes[input] = tick()
+ NumUnsunkTouches = NumUnsunkTouches + 1
+ end
+ end
+ end
+
+ local function OnTouchChanged(input, processed)
+ if inputIsDynamic(input) then
+ return
+ end
+
+ if fingerTouches[input] == nil then
+ fingerTouches[input] = processed
+ if not processed then
+ NumUnsunkTouches = NumUnsunkTouches + 1
+ end
+ end
+
+ if NumUnsunkTouches == 1 then
+ if fingerTouches[input] == false then
+ panBeginLook = panBeginLook or this:GetCameraLook()
+ startPos = startPos or input.Position
+ lastPos = lastPos or startPos
+ this.UserPanningTheCamera = true
+
+ local delta = input.Position - lastPos
+
+ delta = Vector2.new(delta.X, delta.Y * GameSettings:GetCameraYInvertValue())
+
+ if this.PanEnabled then
+ local desiredXYVector = this:ScreenTranslationToAngle(delta) * TOUCH_SENSITIVTY
+ this.RotateInput = this.RotateInput + desiredXYVector
+ end
+
+ lastPos = input.Position
+ end
+ else
+ panBeginLook = nil
+ startPos = nil
+ lastPos = nil
+ this.UserPanningTheCamera = false
+ end
+
+ if NumUnsunkTouches == 2 then
+ local unsunkTouches = {}
+ for touch, wasSunk in pairs(fingerTouches) do
+ if not wasSunk then
+ table.insert(unsunkTouches, touch)
+ end
+ end
+ if #unsunkTouches == 2 then
+ local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
+ if StartingDiff and pinchBeginZoom then
+ local scale = difference / math_max(0.01, StartingDiff)
+ local clampedScale = clamp(0.1, 10, scale)
+ if this.ZoomEnabled then
+ this:ZoomCamera(pinchBeginZoom / clampedScale)
+ this:PlayTick()
+ end
+ else
+ StartingDiff = difference
+ pinchBeginZoom = this:GetCameraActualZoom()
+ end
+ end
+ else
+ StartingDiff = nil
+ pinchBeginZoom = nil
+ end
+ end
+
+ local function calcLookBehindRotateInput(torso)
+ if torso then
+ local newDesiredLook = (torso.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
+ local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
+ local vertShift = math_asin(this:GetCameraLook().y) - math_asin(newDesiredLook.y)
+ if not IsFinite(horizontalShift) then
+ horizontalShift = 0
+ end
+ if not IsFinite(vertShift) then
+ vertShift = 0
+ end
+
+ return Vector2.new(horizontalShift, vertShift)
+ end
+ return nil
+ end
+
+ local function OnTouchEnded(input, processed)
+ if fingerTouches[input] == false then
+ if NumUnsunkTouches == 1 then
+ panBeginLook = nil
+ startPos = nil
+ lastPos = nil
+ elseif NumUnsunkTouches == 2 then
+ StartingDiff = nil
+ pinchBeginZoom = nil
+ end
+ end
+
+ if fingerTouches[input] ~= nil and fingerTouches[input] == false then
+ NumUnsunkTouches = NumUnsunkTouches - 1
+ end
+ fingerTouches[input] = nil
+ inputStartPositions[input] = nil
+ inputStartTimes[input] = nil
+ flaggedDynamic[input] = nil
+ end
+
+ local function OnMousePanButtonPressed(input, processed)
+ if processed then return end
+ this:UpdateMouseBehavior()
+ panBeginLook = panBeginLook or this:GetCameraLook()
+ startPos = startPos or input.Position
+ lastPos = lastPos or startPos
+ this.UserPanningTheCamera = true
+ end
+
+ local function OnMousePanButtonReleased(input, processed)
+ this:UpdateMouseBehavior()
+ if not (isRightMouseDown or isMiddleMouseDown) then
+ panBeginLook = nil
+ startPos = nil
+ lastPos = nil
+ this.UserPanningTheCamera = false
+ end
+ end
+
+ local function OnMouse2Down(input, processed)
+ if processed then return end
+
+ isRightMouseDown = true
+ OnMousePanButtonPressed(input, processed)
+ end
+
+ local function OnMouse2Up(input, processed)
+ isRightMouseDown = false
+ OnMousePanButtonReleased(input, processed)
+ end
+
+ local function OnMouse3Down(input, processed)
+ if processed then return end
+
+ isMiddleMouseDown = true
+ OnMousePanButtonPressed(input, processed)
+ end
+
+ local function OnMouse3Up(input, processed)
+ isMiddleMouseDown = false
+ OnMousePanButtonReleased(input, processed)
+ end
+
+ local function OnMouseMoved(input, processed)
+
+ local inputDelta = input.Delta
+ inputDelta = Vector2.new(inputDelta.X, inputDelta.Y * GameSettings:GetCameraYInvertValue())
+
+ if startPos and lastPos and panBeginLook then
+ local currPos = lastPos + input.Delta
+ local totalTrans = currPos - startPos
+ if this.PanEnabled then
+ local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
+ this.RotateInput = this.RotateInput + desiredXYVector
+ end
+ lastPos = currPos
+ elseif (this:IsInFirstPerson() or this.PartSubjectHack) and this.PanEnabled then
+ local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
+ this.RotateInput = this.RotateInput + desiredXYVector
+ end
+ end
+
+ local function OnMouseWheel(input, processed)
+ if not processed then
+ if this.ZoomEnabled then
+ this:ZoomCameraBy(clamp(-1, 1, input.Position.Z))
+ end
+ end
+ end
+
+ local function round(num)
+ return math_floor(num + 0.5)
+ end
+
+ local eight2Pi = math_pi / 4
+
+ local function rotateVectorByAngleAndRound(camLook, rotateAngle, roundAmount)
+ if camLook ~= ZERO_VECTOR3 then
+ camLook = camLook.unit
+ local currAngle = math_atan2(camLook.z, camLook.x)
+ local newAngle = round((math_atan2(camLook.z, camLook.x) + rotateAngle) / roundAmount) * roundAmount
+ return newAngle - currAngle
+ end
+ return 0
+ end
+
+ local function OnKeyDown(input, processed)
+ if processed then return end
+ if this.ZoomEnabled then
+ if input.KeyCode == Enum.KeyCode.I then
+ this:ZoomCameraBy(1)
+ elseif input.KeyCode == Enum.KeyCode.O then
+ this:ZoomCameraBy(-1)
+ end
+ end
+ if panBeginLook == nil and this.KeyPanEnabled then
+ if input.KeyCode == Enum.KeyCode.Left then
+ this.TurningLeft = true
+ elseif input.KeyCode == Enum.KeyCode.Right then
+ this.TurningRight = true
+ elseif input.KeyCode == Enum.KeyCode.Comma then
+ local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), -eight2Pi * (3/4), eight2Pi)
+ if angle ~= 0 then
+ this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
+ this.LastUserPanCamera = tick()
+ this.LastCameraTransform = nil
+ end
+ this:PlayTick()
+ elseif input.KeyCode == Enum.KeyCode.Period then
+ local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), eight2Pi * (3/4), eight2Pi)
+ if angle ~= 0 then
+ this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
+ this.LastUserPanCamera = tick()
+ this.LastCameraTransform = nil
+ end
+ this:PlayTick()
+ elseif input.KeyCode == Enum.KeyCode.PageUp then
+ this.RotateInput = this.RotateInput + Vector2.new(0,math_pi/12)
+ this.LastCameraTransform = nil
+ this:PlayTick()
+ elseif input.KeyCode == Enum.KeyCode.PageDown then
+ this.RotateInput = this.RotateInput + Vector2.new(0,-math_pi/12)
+ this.LastCameraTransform = nil
+ this:PlayTick()
+ end
+ end
+ end
+
+ local function OnKeyUp(input, processed)
+ if input.KeyCode == Enum.KeyCode.Left then
+ this.TurningLeft = false
+ elseif input.KeyCode == Enum.KeyCode.Right then
+ this.TurningRight = false
+ end
+ end
+
+ local lastThumbstickRotate = nil
+ local numOfSeconds = 0.7
+ local currentSpeed = 0
+ local maxSpeed = 6
+ local lastThumbstickPos = Vector2.new(0,0)
+ local ySensitivity = 0.65
+ local lastVelocity = nil
+
+ function this:UpdateGamepad()
+ local gamepadPan = this.GamepadPanningCamera
+ if gamepadPan then
+ gamepadPan = gamepadLinearToCurve(gamepadPan)
+ local currentTime = tick()
+ if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then
+ this.userPanningTheCamera = true
+ elseif gamepadPan == ZERO_VECTOR2 then
+ lastThumbstickRotate = nil
+ if lastThumbstickPos == ZERO_VECTOR2 then
+ currentSpeed = 0
+ end
+ end
+
+ local finalConstant = 0
+
+ if lastThumbstickRotate then
+ local elapsed = (currentTime - lastThumbstickRotate) * 10
+ currentSpeed = currentSpeed + (maxSpeed * ((elapsed*elapsed)/numOfSeconds))
+
+ if currentSpeed > maxSpeed then currentSpeed = maxSpeed end
+
+ if lastVelocity then
+ local velocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
+ local velocityDeltaMag = (velocity - lastVelocity).magnitude
+
+ if velocityDeltaMag > 12 then
+ currentSpeed = currentSpeed * (20/velocityDeltaMag)
+ if currentSpeed > maxSpeed then currentSpeed = maxSpeed end
+ end
+ end
+
+ local gamepadCameraSensitivity = GameSettings.GamepadCameraSensitivity
+ finalConstant = (gamepadCameraSensitivity * currentSpeed)
+ lastVelocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
+ end
+
+ lastThumbstickPos = gamepadPan
+ lastThumbstickRotate = currentTime
+
+ return Vector2_new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * ySensitivity * GameSettings:GetCameraYInvertValue())
+ end
+
+ return ZERO_VECTOR2
+ end
+
+ local InputEvents = {}
+
+ function this:DisconnectInputEvents()
+ -- Disconnect all input events.
+ while true do
+ local signalName = next(InputEvents)
+ if signalName then
+ InputEvents[signalName]:Disconnect()
+ InputEvents[signalName] = nil
+ else
+ break
+ end
+ end
+
+ this.TurningLeft = false
+ this.TurningRight = false
+ this.LastCameraTransform = nil
+ this.UserPanningTheCamera = false
+ this.RotateInput = ZERO_VECTOR2
+ this.GamepadPanningCamera = ZERO_VECTOR2
+
+ -- Reset input states
+ startPos = nil
+ lastPos = nil
+ panBeginLook = nil
+ isRightMouseDown = false
+ isMiddleMouseDown = false
+
+ fingerTouches = {}
+ NumUnsunkTouches = 0
+
+ StartingDiff = nil
+ pinchBeginZoom = nil
+
+ -- Unlock mouse for example if right mouse button was being held down
+ if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
+ UserInputService.MouseBehavior = Enum.MouseBehavior.Default
+ end
+ end
+
+ local function resetInputStates()
+ isRightMouseDown = false
+ isMiddleMouseDown = false
+ OnMousePanButtonReleased() -- this function doesn't seem to actually need parameters
+
+ if UserInputService.TouchEnabled then
+ --[[menu opening was causing serious touch issues
+ this should disable all active touch events if
+ they're active when menu opens.]]
+ for inputObject, value in pairs(fingerTouches) do
+ fingerTouches[inputObject] = nil
+ end
+ panBeginLook = nil
+ startPos = nil
+ lastPos = nil
+ this.UserPanningTheCamera = false
+ StartingDiff = nil
+ pinchBeginZoom = nil
+ NumUnsunkTouches = 0
+ end
+ end
+
+ local function getGamepadPan(name, state, input)
+ if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
+
+ if state == Enum.UserInputState.Cancel then
+ this.GamepadPanningCamera = ZERO_VECTOR2
+ return
+ end
+
+ local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
+ if inputVector.magnitude > THUMBSTICK_DEADZONE then
+ this.GamepadPanningCamera = Vector2_new(input.Position.X, -input.Position.Y)
+ else
+ this.GamepadPanningCamera = ZERO_VECTOR2
+ end
+ end
+ end
+
+ local function doGamepadZoom(name, state, input)
+ if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.ButtonR3 and state == Enum.UserInputState.Begin then
+ if this.ZoomEnabled then
+ if this:GetCameraZoom() > 0.5 then
+ this:ZoomCamera(0)
+ else
+ this:ZoomCamera(10)
+ end
+ end
+ end
+ end
+
+ local function assignActivateGamepad()
+ local connectedGamepads = UserInputService:GetConnectedGamepads()
+ if #connectedGamepads > 0 then
+ for i = 1, #connectedGamepads do
+ if this.activeGamepad == nil then
+ this.activeGamepad = connectedGamepads[i]
+ elseif connectedGamepads[i].Value < this.activeGamepad.Value then
+ this.activeGamepad = connectedGamepads[i]
+ end
+ end
+ end
+
+ if this.activeGamepad == nil then -- nothing is connected, at least set up for gamepad1
+ this.activeGamepad = Enum.UserInputType.Gamepad1
+ end
+ end
+
+ local function onInputBegan(input, processed)
+ if input.UserInputType == Enum.UserInputType.Touch then
+ OnTouchBegan(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
+ OnMouse2Down(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
+ OnMouse3Down(input, processed)
+ end
+ -- Keyboard
+ if input.UserInputType == Enum.UserInputType.Keyboard then
+ OnKeyDown(input, processed)
+ end
+ end
+
+ local function onInputChanged(input, processed)
+ if input.UserInputType == Enum.UserInputType.Touch then
+ OnTouchChanged(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseMovement then
+ OnMouseMoved(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseWheel then
+ OnMouseWheel(input, processed)
+ end
+ end
+
+ local function onInputEnded(input, processed)
+ if input.UserInputType == Enum.UserInputType.Touch then
+ OnTouchEnded(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
+ OnMouse2Up(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
+ OnMouse3Up(input, processed)
+ end
+ -- Keyboard
+ if input.UserInputType == Enum.UserInputType.Keyboard then
+ OnKeyUp(input, processed)
+ end
+ end
+
+ local inputPassCmds =
+ {
+ ZoomIn = Enum.KeyCode.I;
+ ZoomOut = Enum.KeyCode.O;
+ RotateUp = Enum.KeyCode.PageUp;
+ RotateDown = Enum.KeyCode.PageDown;
+ }
+
+ local function onInputPassed(command)
+ local passKey = inputPassCmds[command]
+ if passKey then
+ OnKeyDown({KeyCode = passKey}, false)
+ end
+ end
+
+ local function onGamepadConnected(gamepadEnum)
+ if this.activeGamepad == nil then
+ assignActivateGamepad()
+ end
+ end
+
+ local function onGamepadDisconnected(gamepadEnum)
+ if this.activeGamepad ~= gamepadEnum then return end
+ this.activeGamepad = nil
+ assignActivateGamepad()
+ end
+
+ function this:ConnectInputEvents()
+ local player = Players.LocalPlayer
+ local playerScripts = player:WaitForChild("PlayerScripts")
+ local passCameraEvent = playerScripts:WaitForChild("PassCameraEvent")
+
+ this.RotateInput = ZERO_VECTOR2
+ this.activeGamepad = nil
+
+ InputEvents =
+ {
+ InputBegan = UserInputService.InputBegan:Connect(onInputBegan);
+ InputChanged = UserInputService.InputChanged:Connect(onInputChanged);
+ InputEnded = UserInputService.InputEnded:Connect(onInputEnded);
+ MenuOpened = GuiService.MenuOpened:Connect(resetInputStates);
+ MenuOpenedConn = GuiService.MenuOpened:Connect(resetInputStates);
+ GamepadConnected = UserInputService.GamepadConnected:Connect(onGamepadConnected);
+ GamepadDisconnected = UserInputService.GamepadDisconnected:Connect(onGamepadDisconnected);
+ InputPassed = passCameraEvent.Event:Connect(onInputPassed);
+ }
+
+ ContextActionService:BindAction("RootCamGamepadPan", getGamepadPan, false, Enum.KeyCode.Thumbstick2)
+ ContextActionService:BindAction("RootCamGamepadZoom", doGamepadZoom, false, Enum.KeyCode.ButtonR3)
+
+ assignActivateGamepad()
+
+ -- set mouse behavior
+ self:UpdateMouseBehavior()
+ end
+
+ function this:SetEnabled(newState)
+ if newState ~= self.Enabled then
+ self.Enabled = newState
+ if self.Enabled then
+ self:ConnectInputEvents()
+ else
+ self:DisconnectInputEvents()
+ end
+ end
+ end
+
+ local function OnPlayerAdded(player)
+ player.Changed:Connect(function (prop)
+ if this.Enabled then
+ if prop == "CameraMode" or prop == "CameraMaxZoomDistance" or prop == "CameraMinZoomDistance" then
+ this:ZoomCameraFixedBy(0)
+ end
+ end
+ end)
+
+ local function OnCharacterAdded(newCharacter)
+ local humanoid = findPlayerHumanoid(player)
+ local start = tick()
+ while tick() - start < 0.3 and (humanoid == nil or humanoid.Torso == nil) do
+ wait()
+ humanoid = findPlayerHumanoid(player)
+ end
+
+ if humanoid and humanoid.Torso and player.Character == newCharacter then
+ local newDesiredLook = (humanoid.Torso.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
+ local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
+ local vertShift = math_asin(this:GetCameraLook().y) - math_asin(newDesiredLook.y)
+ if not IsFinite(horizontalShift) then
+ horizontalShift = 0
+ end
+ if not IsFinite(vertShift) then
+ vertShift = 0
+ end
+ this.RotateInput = Vector2.new(horizontalShift, vertShift)
+
+ -- reset old camera info so follow cam doesn't rotate us
+ this.LastCameraTransform = nil
+ end
+
+ -- Need to wait for camera cframe to update before we zoom in
+ -- Not waiting will force camera to original cframe
+ wait()
+ this:ZoomCamera(this.DefaultZoom)
+ end
+
+ player.CharacterAdded:Connect(function (character)
+ if this.Enabled or SetCameraOnSpawn then
+ OnCharacterAdded(character)
+ SetCameraOnSpawn = false
+ end
+ end)
+
+ if player.Character then
+ spawn(function () OnCharacterAdded(player.Character) end)
+ end
+ end
+
+ if Players.LocalPlayer then
+ OnPlayerAdded(Players.LocalPlayer)
+ end
+
+ Players.ChildAdded:Connect(function (child)
+ if child and Players.LocalPlayer == child then
+ OnPlayerAdded(Players.LocalPlayer)
+ end
+ end)
+
+end
+
+------------------------
+---- Main Updater ----
+------------------------
+
+do
+ local tweenAcceleration = math_rad(220)
+ local tweenSpeed = math_rad(0)
+ local tweenMaxSpeed = math_rad(250)
+ local timeBeforeAutoRotate = 2
+
+ local lastUpdate = tick()
+ this.LastUserPanCamera = lastUpdate
+
+
+ function this:Update()
+ local now = tick()
+ local timeDelta = (now - lastUpdate)
+
+ local userPanningTheCamera = (self.UserPanningTheCamera == true)
+ local camera = workspace.CurrentCamera
+ local humanoid = self:GetHumanoid()
+ local cameraSubject = camera and camera.CameraSubject
+ local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')
+ local isOnASkateboard = cameraSubject and cameraSubject:IsA('SkateboardPlatform')
+
+ if isInVehicle and cameraSubject.Occupant == humanoid then
+ cameraSubject = humanoid
+ camera.CameraSubject = humanoid
+ isInVehicle = false
+ end
+
+ if lastUpdate == nil or (now - lastUpdate) > 1 then
+ self.LastCameraTransform = nil
+ end
+
+ if lastUpdate then
+ local gamepadRotation = self:UpdateGamepad()
+
+ -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
+ local delta = math_min(0.1, now - lastUpdate)
+
+ if gamepadRotation ~= ZERO_VECTOR2 then
+ userPanningTheCamera = true
+ self.RotateInput = self.RotateInput + (gamepadRotation * delta)
+ end
+
+ local angle = 0
+ if not (isInVehicle or isOnASkateboard) then
+ angle = angle + (self.TurningLeft and -120 or 0)
+ angle = angle + (self.TurningRight and 120 or 0)
+ end
+
+ if angle ~= 0 then
+ self.RotateInput = self.RotateInput + Vector2.new(math_rad(angle * delta), 0)
+ userPanningTheCamera = true
+ end
+ end
+
+ -- Reset tween speed if user is panning
+ if userPanningTheCamera then
+ tweenSpeed = 0
+ self.LastUserPanCamera = now
+ end
+
+ local userRecentlyPannedCamera = now - self.LastUserPanCamera < timeBeforeAutoRotate
+ local subjectPosition = self:GetSubjectPosition()
+
+ if subjectPosition and camera then
+ local zoom = self:GetCameraZoom()
+ if zoom < 0.25 then
+ zoom = 0.25
+ end
+
+ if TeleportService:GetTeleportSetting("FollowCamera") then
+ if self.LastCameraTransform and not self:IsInFirstPerson() then
+ local lastVec = -(self.LastCameraTransform.p - subjectPosition)
+ local y = findAngleBetweenXZVectors(lastVec, self:GetCameraLook())
+ -- Check for NaNs
+ if IsFinite(y) and math.abs(y) > 0.0001 then
+ self.RotateInput = self.RotateInput + Vector2.new(y, 0)
+ end
+ end
+ end
+
+ camera.Focus = CFrame_new(subjectPosition)
+
+ local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput)
+ self.RotateInput = ZERO_VECTOR2
+
+ if self.LastZoom ~= zoom then
+ self.LastZoom = zoom
+
+ if camera.CameraSubject and camera.CameraSubject:IsA("Humanoid") then
+ -- Flatten the lookVector
+ newLookVector = (newLookVector * XZ_VECTOR).Unit
+
+ -- Apply upwards tilt
+ local upY = -math_min(6, zoom/40)
+ newLookVector = (newLookVector + (UP_VECTOR * upY)).Unit
+ end
+ end
+
+ local newCF = CFrame_new(subjectPosition - (zoom * newLookVector), subjectPosition)
+ camera.CFrame = camera.CFrame:Lerp(newCF,.8)
+ self.LastCameraTransform = camera.CFrame
+ end
+
+ lastUpdate = now
+ end
+
+ GameSettings:SetCameraYInvertVisible()
+ GameSettings:SetGamepadCameraSensitivityVisible()
+end
+
+return this
\ No newline at end of file
diff --git a/Client/Camera/Opacity.lua b/Client/Camera/Opacity.lua
new file mode 100644
index 0000000..ee8a60f
--- /dev/null
+++ b/Client/Camera/Opacity.lua
@@ -0,0 +1,176 @@
+-- SolarCrane
+
+local module = {}
+
+local LastUpdate = tick()
+local TransparencyDirty = false
+local Enabled = false
+local LastTransparency = nil
+
+local DescendantAddedConn, DescendantRemovingConn = nil, nil
+local ToolDescendantAddedConns = {}
+local ToolDescendantRemovingConns = {}
+local CachedParts = {}
+
+local function HasToolAncestor(object)
+ return (object:FindFirstAncestorWhichIsA("Tool")) ~= nil
+end
+
+local function IsValidPartToModify(part)
+ if part:IsA('BasePart') or part:IsA('Decal') then
+ return not HasToolAncestor(part)
+ end
+ return false
+end
+
+local function TeardownTransparency()
+ for child, _ in pairs(CachedParts) do
+ child.LocalTransparencyModifier = 0
+ end
+
+ CachedParts = {}
+ TransparencyDirty = true
+ LastTransparency = nil
+
+ if DescendantAddedConn then
+ DescendantAddedConn:disconnect()
+ DescendantAddedConn = nil
+ end
+
+ if DescendantRemovingConn then
+ DescendantRemovingConn:disconnect()
+ DescendantRemovingConn = nil
+ end
+
+ for object, conn in pairs(ToolDescendantAddedConns) do
+ conn:disconnect()
+ ToolDescendantAddedConns[object] = nil
+ end
+
+ for object, conn in pairs(ToolDescendantRemovingConns) do
+ conn:disconnect()
+ ToolDescendantRemovingConns[object] = nil
+ end
+end
+
+local function SetupTransparency(character)
+ TeardownTransparency()
+
+ if DescendantAddedConn then
+ DescendantAddedConn:Disconnect()
+ end
+
+ DescendantAddedConn = character.DescendantAdded:connect(function (object)
+ -- This is a part we want to invisify
+ if IsValidPartToModify(object) then
+ CachedParts[object] = true
+ TransparencyDirty = true
+ -- There is now a tool under the character
+ elseif object:IsA('Tool') then
+ if ToolDescendantAddedConns[object] then
+ ToolDescendantAddedConns[object]:Disconnect()
+ end
+
+ ToolDescendantAddedConns[object] = object.DescendantAdded:connect(function (toolChild)
+ CachedParts[toolChild] = nil
+ if toolChild:IsA('BasePart') or toolChild:IsA('Decal') then
+ -- Reset the transparency
+ toolChild.LocalTransparencyModifier = 0
+ end
+ end)
+
+ if ToolDescendantRemovingConns[object] then
+ ToolDescendantRemovingConns[object]:Disconnect()
+ end
+
+ ToolDescendantRemovingConns[object] = object.DescendantRemoving:connect(function (formerToolChild)
+ wait() -- wait for new parent
+ if character and formerToolChild and formerToolChild:IsDescendantOf(character) then
+ if IsValidPartToModify(formerToolChild) then
+ CachedParts[formerToolChild] = true
+ TransparencyDirty = true
+ end
+ end
+ end)
+ end
+ end)
+
+ if DescendantRemovingConn then
+ DescendantRemovingConn:Disconnect()
+ end
+
+ DescendantRemovingConn = character.DescendantRemoving:connect(function (object)
+ if CachedParts[object] then
+ CachedParts[object] = nil
+ -- Reset the transparency
+ object.LocalTransparencyModifier = 0
+ end
+ end)
+
+ for _,desc in pairs(character:GetDescendants()) do
+ if IsValidPartToModify(desc) then
+ CachedParts[desc] = true
+ TransparencyDirty = true
+ end
+ end
+end
+
+
+function module:SetEnabled(newState)
+ if Enabled ~= newState then
+ Enabled = newState
+ self:Update()
+ end
+end
+
+function module:SetSubject(subject)
+ local character = nil
+ if subject and subject:IsA("Humanoid") then
+ character = subject.Parent
+ end
+ if subject and subject:IsA("VehicleSeat") and subject.Occupant then
+ character = subject.Occupant.Parent
+ end
+ if character then
+ SetupTransparency(character)
+ else
+ TeardownTransparency()
+ end
+end
+
+function module:Update()
+ local instant = false
+ local now = tick()
+ local currentCamera = workspace.CurrentCamera
+
+ if currentCamera then
+ local transparency = 0
+ if not Enabled then
+ instant = true
+ else
+ local distance = (currentCamera.Focus.p - currentCamera.CFrame.p).magnitude
+ if distance < 2 then
+ transparency = 1
+ elseif distance < 6 then
+ transparency = 0.5
+ else
+ transparency = 0
+ end
+ end
+
+ if TransparencyDirty or LastTransparency ~= transparency then
+ for child in pairs(CachedParts) do
+ if child.ClassName == "Decal" then
+ child.LocalTransparencyModifier = math.floor(transparency)
+ else
+ child.LocalTransparencyModifier = transparency
+ end
+ end
+ TransparencyDirty = false
+ LastTransparency = transparency
+ end
+ end
+ LastUpdate = now
+end
+
+return module
\ No newline at end of file
diff --git a/Client/Camera/Popper.lua b/Client/Camera/Popper.lua
new file mode 100644
index 0000000..cd4d47b
--- /dev/null
+++ b/Client/Camera/Popper.lua
@@ -0,0 +1,129 @@
+-- PopperCam Version 16
+-- OnlyTwentyCharacters
+
+local PopperCam = {} -- Guarantees your players won't see outside the bounds of your map!
+
+-----------------
+--| Constants |--
+-----------------
+
+local POP_RESTORE_RATE = 0.3
+local MIN_CAMERA_ZOOM = 0.5
+
+local VALID_SUBJECTS = {
+ 'Humanoid',
+ 'VehicleSeat',
+ 'SkateboardPlatform',
+}
+
+-----------------
+--| Variables |--
+-----------------
+
+local Players = game:GetService('Players')
+
+local Camera = nil
+local CameraSubjectChangeConn = nil
+
+local SubjectPart = nil
+
+local PlayerCharacters = {} -- For ignoring in raycasts
+local VehicleParts = {} -- Also just for ignoring
+
+local LastPopAmount = 0
+local LastZoomLevel = 0
+local PopperEnabled = true
+
+local CFrame_new = CFrame.new
+
+-----------------------
+--| Local Functions |--
+-----------------------
+
+local math_abs = math.abs
+
+local function OnCharacterAdded(player, character)
+ PlayerCharacters[player] = character
+end
+
+local function OnPlayersChildAdded(child)
+ if child:IsA('Player') then
+ child.CharacterAdded:connect(function(character)
+ OnCharacterAdded(child, character)
+ end)
+ if child.Character then
+ OnCharacterAdded(child, child.Character)
+ end
+ end
+end
+
+local function OnPlayersChildRemoved(child)
+ if child:IsA('Player') then
+ PlayerCharacters[child] = nil
+ end
+end
+
+-------------------------
+--| Exposed Functions |--
+-------------------------
+
+function PopperCam:Update()
+ if PopperEnabled then
+ -- First, prep some intermediate vars
+ local Camera = workspace.CurrentCamera
+ local cameraCFrame = Camera.CFrame
+ local focusPoint = Camera.Focus.p
+
+ if SubjectPart then
+ focusPoint = SubjectPart.CFrame.p
+ end
+
+ local ignoreList = {}
+ for _, character in pairs(PlayerCharacters) do
+ ignoreList[#ignoreList + 1] = character
+ end
+ for i = 1, #VehicleParts do
+ ignoreList[#ignoreList + 1] = VehicleParts[i]
+ end
+
+ -- Get largest cutoff distance
+ local largest = Camera:GetLargestCutoffDistance(ignoreList)
+
+ -- Then check if the player zoomed since the last frame,
+ -- and if so, reset our pop history so we stop tweening
+ local zoomLevel = (cameraCFrame.p - focusPoint).Magnitude
+ if math_abs(zoomLevel - LastZoomLevel) > 0.001 then
+ LastPopAmount = 0
+ end
+
+ -- Finally, zoom the camera in (pop) by that most-cut-off amount, or the last pop amount if that's more
+ local popAmount = largest
+ if LastPopAmount > popAmount then
+ popAmount = LastPopAmount
+ end
+
+ if popAmount > 0 then
+ Camera.CFrame = cameraCFrame + (cameraCFrame.lookVector * popAmount)
+ LastPopAmount = popAmount - POP_RESTORE_RATE -- Shrink it for the next frame
+ if LastPopAmount < 0 then
+ LastPopAmount = 0
+ end
+ end
+
+ LastZoomLevel = zoomLevel
+ end
+end
+
+--------------------
+--| Script Logic |--
+--------------------
+
+
+-- Connect to all Players so we can ignore their Characters
+Players.ChildRemoved:connect(OnPlayersChildRemoved)
+Players.ChildAdded:connect(OnPlayersChildAdded)
+for _, player in pairs(Players:GetPlayers()) do
+ OnPlayersChildAdded(player)
+end
+
+return PopperCam
\ No newline at end of file
diff --git a/Client/Camera/init.client.lua b/Client/Camera/init.client.lua
new file mode 100644
index 0000000..764f7a5
--- /dev/null
+++ b/Client/Camera/init.client.lua
@@ -0,0 +1,60 @@
+local RunService = game:GetService("RunService")
+local UserInputService = game:GetService("UserInputService")
+local Players = game:GetService("Players")
+
+local playerScripts = script.Parent
+local playerModule = require(playerScripts:WaitForChild("PlayerModule"))
+
+local cameraSystem = playerModule:GetCameras()
+
+local main = require(script:WaitForChild("Main"))
+local popper = require(script:WaitForChild("Popper"))
+local opacity = require(script:WaitForChild("Opacity"))
+
+local cameraSubjectChangedConn = nil
+local renderSteppedConn = nil
+
+local function onCameraSubjectChanged()
+ local currentCamera = workspace.CurrentCamera
+ if currentCamera then
+ local newSubject = currentCamera.CameraSubject
+ opacity:SetSubject(newSubject)
+ end
+end
+
+local function onNewCamera()
+ local currentCamera = workspace.CurrentCamera
+ if currentCamera then
+ if cameraSubjectChangedConn then
+ cameraSubjectChangedConn:Disconnect()
+ end
+
+ local cameraSubjectChanged = currentCamera:GetPropertyChangedSignal("CameraSubject")
+ cameraSubjectChangedConn = cameraSubjectChanged:Connect(onCameraSubjectChanged)
+
+ onCameraSubjectChanged()
+ end
+end
+
+-- Initialize cameras.
+local cameraUpdated = workspace:GetPropertyChangedSignal("CurrentCamera")
+cameraUpdated:Connect(onNewCamera)
+
+onNewCamera()
+main:SetEnabled(true)
+opacity:SetEnabled(true)
+
+-- Overload the camera update function.
+function cameraSystem:Update()
+ if cameraSystem.activeCameraController then
+ cameraSystem.activeCameraController:Enable(false)
+ cameraSystem.activeCameraController = nil
+ end
+
+ main:Update()
+ popper:Update()
+ opacity:Update()
+end
+
+playerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Default)
+playerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Default)
\ No newline at end of file
diff --git a/Client/CharacterBevels.client.lua b/Client/CharacterBevels.client.lua
new file mode 100644
index 0000000..4bd288a
--- /dev/null
+++ b/Client/CharacterBevels.client.lua
@@ -0,0 +1,25 @@
+local CollectionService = game:GetService("CollectionService")
+local TeleportService = game:GetService("TeleportService")
+
+local noBevelsTag = "NoCharacterBevels"
+local bevelTracker = CollectionService:GetInstanceAddedSignal(noBevelsTag)
+
+local function safeDestroy(obj)
+ spawn(function ()
+ obj:Destroy()
+ end)
+end
+
+local function onInstanceAdded(inst)
+ if TeleportService:GetTeleportSetting("CharacterBevels") then
+ safeDestroy(inst)
+ end
+end
+
+for _,inst in pairs(CollectionService:GetTagged(noBevelsTag)) do
+ onInstanceAdded(inst)
+end
+
+bevelTracker:Connect(onInstanceAdded)
+
+
diff --git a/Client/ClickToMove.client.lua b/Client/ClickToMove.client.lua
new file mode 100644
index 0000000..183f74b
--- /dev/null
+++ b/Client/ClickToMove.client.lua
@@ -0,0 +1,301 @@
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- @CloneTrooper1019, 2018
+-- ClickToMove
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Constants
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local ContextActionService = game:GetService("ContextActionService")
+local GuiService = game:GetService("GuiService")
+local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+local TeleportService = game:GetService("TeleportService")
+local UserInputService = game:GetService("UserInputService")
+
+local IS_TOUCH = UserInputService.TouchEnabled
+
+local ICON_IDLE = "rbxassetid://334630296"
+local ICON_HOVER = "rbxassetid://1000000"
+local ICON_CLICK = "rbxasset://textures/DragCursor.png"
+
+local DISK_OFFSET = CFrame.Angles(math.pi / 2,0,0)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Character Listener
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local player = game.Players.LocalPlayer
+local character,humanoid
+
+local function onCharacterAdded(char)
+ humanoid = char:WaitForChild("Humanoid")
+ character = char
+end
+
+if player.Character then
+ onCharacterAdded(player.Character)
+end
+
+player.CharacterAdded:Connect(onCharacterAdded)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Gui Focus
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local isMouseHoveringUi = false
+
+local function onInputChanged(input, gameProcessed)
+ local inputType = input.UserInputType.Name
+
+ if inputType == "MouseMovement" then
+ isMouseHoveringUi = gameProcessed
+ end
+end
+
+local function isGuiFocused()
+ return isMouseHoveringUi or GuiService.SelectedObject ~= nil
+end
+
+UserInputService.InputChanged:Connect(onInputChanged)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Movement Goal
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local currentGoal, moveSignal
+
+local function findAngleBetweenXZVectors(vec2, vec1)
+ return math.atan2(vec1.X*vec2.Z-vec1.Z*vec2.X, vec1.X*vec2.X + vec1.Z*vec2.Z)
+end
+
+local function isFinite(num)
+ return num == num and num ~= 1/0 and num ~= -1/0
+end
+
+local function rotateCameraTowardsGoal()
+ local c = workspace.CurrentCamera
+ if c then
+ local cf = c.CFrame
+ local focus = c.Focus
+
+ local desiredAngle = CFrame.new(cf.p,currentGoal).lookVector
+ local currentAngle = cf.lookVector
+
+ local angleBetween = findAngleBetweenXZVectors(desiredAngle,currentAngle)
+
+ if isFinite(angleBetween) then
+ local abs = math.abs(angleBetween)
+ local sign = math.sign(angleBetween)
+ local rotation = math.min(0.01,abs)
+
+ local cfLocal = focus:toObjectSpace(cf)
+ c.CFrame = focus * CFrame.Angles(0,-rotation*sign,0) * cfLocal
+ end
+ end
+end
+
+local function finishGoal()
+ if currentGoal then
+ currentGoal = nil
+ end
+ if moveSignal then
+ moveSignal:Disconnect()
+ moveSignal = nil
+ end
+end
+
+local function clickToMove(goal)
+ finishGoal()
+ currentGoal = goal
+
+ moveSignal = humanoid.MoveToFinished:Connect(finishGoal)
+
+ humanoid:Move(Vector3.new(1,1,1))
+ humanoid:MoveTo(currentGoal)
+end
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Green Disk
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local mouse = player:GetMouse()
+local mouseIcon = script.Parent
+mouse.TargetFilter = workspace.CurrentCamera
+
+local lastTarget
+local lastTargetCanClick = false
+
+local disk = Instance.new("CylinderHandleAdornment")
+disk.Name = "Disk"
+disk.Color3 = Color3.new(0,1,0)
+disk.Radius = 1
+disk.Height = 0.2
+disk.Visible = false
+disk.Adornee = workspace.Terrain
+disk.Parent = script
+
+local goalDisk = disk:Clone()
+goalDisk.Name = "Goal"
+goalDisk.Parent = script
+
+local function hasTool()
+ if character then
+ return character:FindFirstChildOfClass("Tool") ~= nil
+ end
+
+ return false
+end
+
+local function isFirstPerson()
+ if character then
+ local head = character:FindFirstChild("Head")
+ if head then
+ return head.LocalTransparencyModifier == 1
+ end
+ end
+ return false
+end
+
+local function canClickTarget()
+ local target = mouse.Target
+ if target then
+ if target ~= lastTarget then
+ local canClick = false
+ local clickDetector = target:FindFirstChildOfClass("ClickDetector")
+
+ if clickDetector then
+ local dist = player:DistanceFromCharacter(target.Position)
+ if dist <= clickDetector.MaxActivationDistance then
+ canClick = true
+ end
+ end
+
+ lastTarget = target
+ lastTargetCanClick = canClick
+ end
+
+ return lastTargetCanClick
+ end
+end
+
+local function canRenderDisk(rendering)
+ if not TeleportService:GetTeleportSetting("ClickToMove") then
+ return false
+ end
+
+ if rendering and IS_TOUCH then
+ return false
+ end
+
+ if humanoid then
+ local movement = humanoid.MoveDirection
+ if movement.Magnitude == 0 then
+ local pos = mouse.Hit.p
+ local dist = player:DistanceFromCharacter(pos)
+
+ if dist < 32 then
+ local blockers = {hasTool, isFirstPerson, canClickTarget, isGuiFocused}
+
+ for _,blocker in pairs(blockers) do
+ if blocker() then
+ return false
+ end
+ end
+
+ return true
+ end
+ else
+ finishGoal()
+ end
+ end
+
+ return false
+end
+
+local function render3dAdorn()
+ disk.Visible = canRenderDisk(true)
+
+ if disk.Visible then
+ disk.CFrame = CFrame.new(mouse.Hit.p) * DISK_OFFSET
+ mouseIcon.Image = ICON_HOVER
+ elseif canClickTarget() then
+ mouseIcon.Image = ICON_CLICK
+ elseif not hasTool() then
+ mouseIcon.Image = ICON_IDLE
+ end
+
+ if currentGoal then
+ goalDisk.Visible = true
+ goalDisk.CFrame = CFrame.new(currentGoal) * DISK_OFFSET
+ rotateCameraTowardsGoal()
+ else
+ goalDisk.Visible = false
+ end
+end
+
+RunService.Heartbeat:Connect(render3dAdorn)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Click Action
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function onInputBegan(input,gameProcessed)
+ local goal = mouse.Hit.p
+ if not gameProcessed and canRenderDisk() and humanoid then
+ local name = input.UserInputType.Name
+
+ if name == "MouseButton1" then
+ clickToMove(goal)
+ elseif name == "Touch" then
+ wait(.1)
+ if input.UserInputState == Enum.UserInputState.End then
+ clickToMove(goal)
+ end
+ elseif name == "Gamepad1" then
+ if input.KeyCode == Enum.KeyCode.ButtonR2 then
+ clickToMove(goal)
+ end
+ end
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- OBLITERATE the invasive click to move mode that Roblox provides
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+pcall(function ()
+ if IS_TOUCH then
+ local playerScripts = player:WaitForChild("PlayerScripts")
+ local no = function () end
+
+ spawn(function ()
+ local controlScript = playerScripts:WaitForChild("ControlScript", 86400)
+ if controlScript then
+ local masterControl = controlScript:WaitForChild("MasterControl")
+
+ local clickToMove = masterControl:WaitForChild("ClickToMoveController")
+ clickToMove = require(clickToMove)
+
+ clickToMove:Disable()
+ clickToMove.Enable = no
+ end
+ end)
+
+ spawn(function ()
+ local playerModule = playerScripts:WaitForChild("PlayerModule", 86400)
+ if playerModule then
+ local controlModule = playerModule:WaitForChild("ControlModule")
+
+ local clickToMove = controlModule:WaitForChild("ClickToMoveController")
+ clickToMove = require(clickToMove)
+
+ clickToMove:Stop()
+ clickToMove.Enable = no
+ end
+ end)
+ end
+end)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Client/ConsoleTweaks.client.lua b/Client/ConsoleTweaks.client.lua
new file mode 100644
index 0000000..dcfee95
--- /dev/null
+++ b/Client/ConsoleTweaks.client.lua
@@ -0,0 +1,34 @@
+local UserInputService = game:GetService("UserInputService")
+local GuiService = game:GetService("GuiService")
+
+local function addUIScale(obj,scale)
+ local uiScale = Instance.new("UIScale")
+ uiScale.Scale = scale
+ uiScale.Parent = obj
+end
+
+if GuiService:IsTenFootInterface() then
+ local gui = script.Parent
+ local zoomControls = gui:WaitForChild("ZoomControls")
+ zoomControls.Visible = false
+
+ local backpack = gui:WaitForChild("Backpack")
+ backpack.Position = UDim2.new(0, 0, 1, 0)
+
+ local chat = gui:WaitForChild("Chat")
+ addUIScale(chat, 1.5)
+
+ local chatPadding = gui:WaitForChild("ChatPadding", 1)
+ if chatPadding then
+ chatPadding:Destroy()
+ end
+
+ local safeChat = gui:WaitForChild("SafeChat")
+ addUIScale(safeChat, 1.5)
+
+ local health = gui:WaitForChild("Health")
+ addUIScale(health, 1.5)
+end
+
+wait()
+script:Destroy()
\ No newline at end of file
diff --git a/Client/Explosions/ClassicExp.rbxmx b/Client/Explosions/ClassicExp.rbxmx
new file mode 100644
index 0000000..e79d7c2
--- /dev/null
+++ b/Client/Explosions/ClassicExp.rbxmx
@@ -0,0 +1,38 @@
+
+ -
+
+
+ 0
+ 0
+ 0
+
+
+ 0 1 1 1 0 1 0.498039 0.498039 0.498039 0
+ 0
+ 1
+ false
+ 0.3 0.5
+ 1
+ 0
+ false
+ ClassicExp
+ 500
+ 0 0
+ 0 0
+ 0 3 1.1875 0.75 2.62 0 1 0 0
+ 20 25
+
+ 360
+ 360
+
+
+
+ rbxassetid://334788053
+
+ 0 0 0 0.993111 0.1625 0 1 1 0
+ 0
+ 3
+ true
+
+
+
\ No newline at end of file
diff --git a/Client/Explosions/init.client.lua b/Client/Explosions/init.client.lua
new file mode 100644
index 0000000..b559745
--- /dev/null
+++ b/Client/Explosions/init.client.lua
@@ -0,0 +1,50 @@
+local TeleportService = game:GetService("TeleportService")
+
+local classicExp = script:WaitForChild("ClassicExp")
+local c = workspace.CurrentCamera
+
+local baseExpAdorn = Instance.new("UnionOperation")
+baseExpAdorn.Name = "ExplosionAdorn"
+baseExpAdorn.Anchored = true
+baseExpAdorn.CanCollide = false
+baseExpAdorn.Locked = true
+baseExpAdorn.Transparency = 1
+baseExpAdorn.Size = Vector3.new()
+
+local function onDescendantAdded(exp)
+ if exp:IsA("Explosion") then
+ local cf = CFrame.new(exp.Position)
+ local expAdorn = baseExpAdorn:Clone()
+ local lifeTime = 1.5
+ exp.Visible = false
+ if TeleportService:GetTeleportSetting("RetroExplosions") then
+ local expObj = Instance.new("SphereHandleAdornment")
+ expObj.Adornee = expAdorn
+ expObj.Radius = exp.BlastRadius
+ expObj.Color3 = Color3.new(1,0,0)
+ expObj.CFrame = cf
+ expObj.Parent = expAdorn
+ lifeTime = 1
+ if exp.BlastRadius > 1 then
+ lifeTime = lifeTime - (1/exp.BlastRadius)
+ end
+ else
+ spawn(function ()
+ local e = classicExp:Clone()
+ e.Parent = expAdorn
+ expAdorn.CFrame = cf
+ local lessParticles = TeleportService:GetTeleportSetting("ReducedParticles")
+ local count = lessParticles and 25 or 100
+ for i = 1,8 do
+ e:Emit(count)
+ wait(0.125)
+ end
+ end)
+ end
+ expAdorn.Parent = c
+ wait(lifeTime)
+ expAdorn:Destroy()
+ end
+end
+
+workspace.DescendantAdded:Connect(onDescendantAdded)
\ No newline at end of file
diff --git a/Client/ForceFields.client.lua b/Client/ForceFields.client.lua
new file mode 100644
index 0000000..814f3a2
--- /dev/null
+++ b/Client/ForceFields.client.lua
@@ -0,0 +1,55 @@
+local RunService = game:GetService("RunService")
+
+local ffAdorns = workspace:WaitForChild("_ForceFieldAdorns")
+local registry = {}
+local cycleStates = {}
+local cycles = 60
+
+local function onChildAdded(child)
+ if child:IsA("SelectionBox") then
+ spawn(function ()
+ while not child.Adornee do
+ child.Changed:Wait()
+ end
+ registry[child] = child.Adornee
+ end)
+ end
+end
+
+local function onChildRemoved(child)
+ if registry[child] then
+ registry[child] = nil
+ end
+end
+
+local function update()
+ local now = tick()
+ for adorn,adornee in pairs(registry) do
+ local model = adornee:FindFirstAncestorWhichIsA("Model")
+ local key
+ if model then
+ local key = model:GetFullName()
+ local startTime = cycleStates[key]
+ if not startTime then
+ startTime = tick()
+ cycleStates[key] = startTime
+ end
+ local cycle = math.floor(((now-startTime)*2) * cycles) % (cycles*2)
+ if cycle > cycles then
+ cycle = cycles - (cycle - cycles)
+ end
+ local invertCycle = cycles - cycle
+ adorn.Color3 = Color3.new(cycle/cycles, 0, invertCycle/cycles)
+ adorn.Transparency = 0
+ end
+ adorn.Visible = adornee:IsDescendantOf(workspace) and adornee.LocalTransparencyModifier < 1
+ end
+end
+
+for _,v in pairs(ffAdorns:GetChildren()) do
+ onChildAdded(v)
+end
+
+RunService.Heartbeat:Connect(update)
+ffAdorns.ChildAdded:Connect(onChildAdded)
+ffAdorns.ChildRemoved:Connect(onChildRemoved)
\ No newline at end of file
diff --git a/Client/GamepadPatch.client.lua b/Client/GamepadPatch.client.lua
new file mode 100644
index 0000000..2a0cae9
--- /dev/null
+++ b/Client/GamepadPatch.client.lua
@@ -0,0 +1,29 @@
+-- Seriously Roblox?
+
+local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+local UserInputService = game:GetService("UserInputService")
+
+local player = Players.LocalPlayer
+local playerScripts = player:WaitForChild("PlayerScripts")
+
+local playerModule = playerScripts:WaitForChild("PlayerModule")
+local controlModule = playerModule:WaitForChild("ControlModule")
+local gamepad = require(controlModule:WaitForChild("Gamepad"))
+
+playerModule = require(playerModule)
+controlModule = playerModule:GetControls()
+
+local function fixGamepad()
+ local lastInputType = UserInputService:GetLastInputType()
+
+ if lastInputType.Name == "Gamepad1" then
+ local controllers = controlModule.controllers
+
+ if controlModule.activeController ~= controllers[gamepad] then
+ controlModule:SwitchToController(gamepad)
+ end
+ end
+end
+
+RunService:BindToRenderStep("GamepadPatch", 0, fixGamepad)
\ No newline at end of file
diff --git a/Client/HumanoidLabels/Health.rbxmx b/Client/HumanoidLabels/Health.rbxmx
new file mode 100644
index 0000000..a39636c
--- /dev/null
+++ b/Client/HumanoidLabels/Health.rbxmx
@@ -0,0 +1,237 @@
+
+ -
+
+ false
+
[null]
+ true
+
+ true
+ false
+ 0
+ 0
+ -1
+ true
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+ 0
+ INF
+ Health
+ [null]
+ true
+ [null]
+
+ 0
+ 96
+ 0
+ 24
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 1.5
+ 0
+
+
+ 0
+ true
+
+ -
+
+ false
+
+ 0.5
+ 0.75
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ PlayerName
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 10
+ 0
+ 1
+ 0
+
+ 0
+
+ CloneTrooper1019
+
+ 1
+ 1
+ 1
+
+ true
+ 24
+
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+ true
+ 2
+ 1
+ true
+ 1
+ true
+
+
+ -
+
+ false
+
+ 0.5
+ 0.5
+
+
+ true
+
+ 1
+ 0
+ 0
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ RedBar
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.6
+ 0
+ 0.3
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.5058824
+ 0.7725491
+ 0.08627451
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ GreenBar
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Client/HumanoidLabels/init.client.lua b/Client/HumanoidLabels/init.client.lua
new file mode 100644
index 0000000..191d4f8
--- /dev/null
+++ b/Client/HumanoidLabels/init.client.lua
@@ -0,0 +1,139 @@
+local humanoids = {}
+local player = game.Players.LocalPlayer
+local pgui = player:WaitForChild("PlayerGui")
+local healthBase = script:WaitForChild("Health")
+local rs = game:GetService("RunService")
+
+local farStudsOffset = Vector3.new(0,2,0)
+local closeStudsOffset = Vector3.new(0,1,0)
+
+local farSize = UDim2.new(0,50,0,20)
+local closeSize = UDim2.new(0,100,0,30)
+
+local function isFinite(num)
+ return num == num and num ~= -1/0 and num ~= 1/0
+end
+
+local function setupHumanoid(h)
+ local updateCon = nil
+ local currentHealth = nil
+
+ local function onAncestryChanged()
+ if updateCon then
+ updateCon:disconnect()
+ updateCon = nil
+ end
+
+ if currentHealth then
+ currentHealth:Destroy()
+ currentHealth = nil
+ end
+
+ local char = h.Parent
+
+ if char then
+ while not char:FindFirstChild("Head") do
+ if h.Parent ~= char then break end
+ char.ChildAdded:wait()
+ end
+
+ local head = char:FindFirstChild("Head")
+
+ if head then
+ local health = healthBase:Clone()
+ local playerName = health:WaitForChild("PlayerName")
+ local redBar = health:WaitForChild("RedBar")
+ local greenBar = redBar:WaitForChild("GreenBar")
+ local inOverWrite = false
+ local overWriter = nil
+
+ local hPlayer = game.Players:GetPlayerFromCharacter(char)
+ playerName.Text = char.Name
+ health.Adornee = head
+ health.PlayerToHideFrom = hPlayer
+ health.Parent = head
+
+ local c = workspace.CurrentCamera
+
+ local function update()
+ local dist = (c.CFrame.p - head.Position).magnitude
+ local fontSize = 12
+
+ if dist < 20 then
+ fontSize = 24
+ elseif dist < 50 then
+ fontSize = 18
+ end
+
+ local ratio = h.Health / h.MaxHealth
+ redBar.Visible = isFinite(ratio)
+ redBar.BackgroundTransparency = math.floor(ratio)
+ redBar.Size = UDim2.new(0, fontSize * 4, 0, fontSize / 2)
+ greenBar.Size = UDim2.new(ratio, 0, 1, 0)
+
+ local width = fontSize * 4
+ health.Size = UDim2.new(0, width, 0, fontSize)
+ health.Enabled = (dist <= 100 and head.Transparency < 1)
+ health.StudsOffsetWorldSpace = Vector3.new(0, 1.5, 0)
+
+ if hPlayer and game:FindService("Teams") then
+ playerName.TextColor = hPlayer.TeamColor
+ else
+ playerName.TextColor3 = Color3.new(1, 1, 1)
+ end
+
+ local overWriter = char:FindFirstChild("NameOverwrite")
+ if overWriter and overWriter:IsA("StringValue") then
+ playerName.Text = overWriter.Value
+ else
+ playerName.Text = char.Name
+ end
+ end
+
+ updateCon = rs.RenderStepped:Connect(update)
+ currentHealth = health
+ h.DisplayDistanceType = "None"
+ end
+ end
+ end
+ onAncestryChanged()
+ h.AncestryChanged:Connect(onAncestryChanged)
+end
+
+local function recurse(obj)
+ for _,v in pairs(obj:GetChildren()) do
+ if v:IsA("Humanoid") then
+ humanoids[v] = true
+ else
+ recurse(v)
+ end
+ end
+end
+
+recurse(workspace)
+
+for h in pairs(humanoids) do
+ humanoids[h] = true
+
+ spawn(function ()
+ setupHumanoid(h)
+ end)
+end
+
+local function onDescendantAdded(child)
+ if child:IsA("Humanoid") then
+ humanoids[child] = true
+ setupHumanoid(child)
+ end
+end
+
+local function onDescendantRemoved(child)
+ if humanoids[child] then
+ humanoids[child] = nil
+ end
+end
+
+recurse(workspace)
+
+workspace.DescendantAdded:connect(onDescendantAdded)
+workspace.DescendantRemoving:connect(onDescendantRemoved)
\ No newline at end of file
diff --git a/Client/InputGateway.client.lua b/Client/InputGateway.client.lua
new file mode 100644
index 0000000..a61ce51
--- /dev/null
+++ b/Client/InputGateway.client.lua
@@ -0,0 +1,185 @@
+local UserInputService = game:GetService("UserInputService")
+local ContextActionService = game:GetService("ContextActionService")
+local Debris = game:GetService("Debris")
+
+local gateway = script.Parent
+local tool = gateway.Parent
+local remote = gateway:WaitForChild("Gateway")
+local player = game.Players.LocalPlayer
+local mouse = player:GetMouse()
+local isActive = false
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Standard Input
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function activate(active,cf)
+ isActive = active
+ remote:FireServer("SetActive",active,cf)
+ while isActive do
+ wait(.1)
+ remote:FireServer("SetTarget",mouse.Hit)
+ end
+end
+
+local function onKey(input)
+ local keyCode = input.KeyCode.Name
+ local down = (input.UserInputState.Name == "Begin")
+ remote:FireServer("KeyEvent",keyCode,down)
+end
+
+local function onInputBegan(input,gameProcessed)
+ if not gameProcessed then
+ local name = input.UserInputType.Name
+ if name == "MouseButton1" then
+ activate(true,mouse.Hit)
+ elseif name == "Touch" then
+ wait(.1)
+ local state = input.UserInputState.Name
+ if state == "End" or state == "Cancel" then
+ activate(true,mouse.Hit)
+ end
+ elseif name == "Gamepad1" then
+ local keyCode = input.KeyCode.Name
+ if keyCode == "ButtonR2" then
+ activate(true,mouse.Hit)
+ end
+ elseif name == "Keyboard" then
+ onKey(input)
+ end
+ end
+end
+
+local function onInputEnded(input,gameProcessed)
+ if not gameProcessed and isActive then
+ local name = input.UserInputType.Name
+ if name == "MouseButton1" or name == "Touch" or name == "Gamepad1" then
+ activate(false,mouse.Hit)
+ elseif name == "Keyboard" then
+ onKey(input)
+ end
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+UserInputService.InputEnded:Connect(onInputEnded)
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Special case Input
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local mControlScheme = tool:WaitForChild("ControlScheme",5)
+
+if mControlScheme then
+ local controlSchemeData = require(mControlScheme)
+ local controlScheme = controlSchemeData.Buttons
+ local activateContext = controlSchemeData.ActivateContext
+ local keyEvent = tool:WaitForChild("KeyEvent")
+ local callbacks = {}
+
+ local hands = { L = "Left", R = "Right" }
+ local handTypes = {"Bumper","Trigger","Joystick (Press)"}
+
+ local schemeDocs =
+ {
+ Keyboard = {"Hold Left Mouse Button - " .. activateContext};
+ Gamepad = {"Hold Right Trigger - " .. activateContext};
+ }
+
+ for key,data in pairs(controlScheme) do
+ local down = false
+ callbacks[key] = function (actionName,inputState,inputObject)
+ if (inputState.Name == "Begin") and not down then
+ down = true
+ if data.Client then
+ keyEvent:Fire(key,true)
+ else
+ remote:FireServer("KeyEvent",key,true)
+ end
+ elseif (inputState.Name == "End") and down then
+ down = false
+ if data.Client then
+ keyEvent:Fire(key,false)
+ else
+ remote:FireServer("KeyEvent",key,false)
+ end
+ end
+ end
+
+ local xBtn = data.XboxButton:gsub("Button","")
+ if #xBtn == 2 then
+ local handId,hTypeId = xBtn:match("(%u)(%d)")
+ local hand = hands[handId]
+ local hType = handTypes[tonumber(hTypeId)]
+ xBtn = hand .. " " .. hType
+ else
+ xBtn = "(" .. xBtn .. ")"
+ end
+ table.insert(schemeDocs.Keyboard,key .. " - " .. data.Label)
+ table.insert(schemeDocs.Gamepad,xBtn .. " - " .. data.Label)
+ end
+
+ local currentSchemeDocMsg
+
+ local function onLastInputTypeChanged(inputType)
+ if currentSchemeDocMsg and not UserInputService.TouchEnabled and not controlSchemeData.HideControls then
+ local schemeDoc
+ if inputType.Name:find("Gamepad") then
+ schemeDoc = "Gamepad"
+ else
+ schemeDoc = "Keyboard"
+ end
+ currentSchemeDocMsg.Text = schemeDoc .. " Controls:\n\n" .. table.concat(schemeDocs[schemeDoc],"\n")
+ end
+ end
+
+ local diedCon
+ local equipped = false
+
+ local function onUnequipped()
+ if equipped then
+ equipped = false
+ for key,data in pairs(controlScheme) do
+ ContextActionService:UnbindAction(data.Label)
+ end
+ currentSchemeDocMsg:Destroy()
+ currentSchemeDocMsg = nil
+ end
+ end
+
+ local function onEquipped()
+ if not equipped then
+ equipped = true
+ for key,data in pairs(controlScheme) do
+ ContextActionService:BindAction(data.Label,callbacks[key],true,Enum.KeyCode[data.XboxButton])
+ ContextActionService:SetTitle(data.Label,data.Label)
+ end
+ if UserInputService.TouchEnabled then
+ spawn(function ()
+ local playerGui = player:WaitForChild("PlayerGui")
+ local contextActionGui = playerGui:WaitForChild("ContextActionGui")
+ local contextButtonFrame = contextActionGui:WaitForChild("ContextButtonFrame")
+ contextButtonFrame.Size = UDim2.new(3/8,0,3/8,0)
+ contextButtonFrame.AnchorPoint = Vector2.new(1,1)
+ contextButtonFrame.Position = UDim2.new(1,0,1,0)
+ end)
+ end
+ currentSchemeDocMsg = Instance.new("Message")
+ currentSchemeDocMsg.Parent = player
+ onLastInputTypeChanged(UserInputService:GetLastInputType())
+ if not diedCon then
+ local char = tool.Parent
+ if char then
+ local humanoid = char:FindFirstChildWhichIsA("Humanoid")
+ if humanoid then
+ diedCon = humanoid.Died:Connect(onUnequipped)
+ end
+ end
+ end
+ end
+ end
+
+ tool.Equipped:Connect(onEquipped)
+ tool.Unequipped:Connect(onUnequipped)
+ UserInputService.LastInputTypeChanged:Connect(onLastInputTypeChanged)
+end
\ No newline at end of file
diff --git a/Client/Music.client.lua b/Client/Music.client.lua
new file mode 100644
index 0000000..b1fde17
--- /dev/null
+++ b/Client/Music.client.lua
@@ -0,0 +1,5 @@
+local TeleportService = game:GetService("TeleportService")
+local gameMusic = workspace:WaitForChild("GameMusic",10)
+if gameMusic and TeleportService:GetTeleportSetting("AllowMusic") then
+ gameMusic:Play()
+end
\ No newline at end of file
diff --git a/Client/Shared.client.lua b/Client/Shared.client.lua
new file mode 100644
index 0000000..d359e26
--- /dev/null
+++ b/Client/Shared.client.lua
@@ -0,0 +1,9 @@
+local TARGET = script.Name
+
+do
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
+ local client = ReplicatedStorage:WaitForChild("Client")
+ local targetScript = client:WaitForChild(TARGET)
+ local activation = require(targetScript)
+ activation(script)
+end
\ No newline at end of file
diff --git a/Client/ToolSoundGlitch.client.lua b/Client/ToolSoundGlitch.client.lua
new file mode 100644
index 0000000..9a880e5
--- /dev/null
+++ b/Client/ToolSoundGlitch.client.lua
@@ -0,0 +1,12 @@
+local CollectionService = game:GetService("CollectionService")
+local TeleportService = game:GetService("TeleportService")
+
+local function onGlitchSoundAdded(glitchSound)
+ if TeleportService:GetTeleportSetting("SoundEquipBug") then
+ glitchSound.MaxDistance = 10000
+ glitchSound:Play()
+ end
+end
+
+local addSignal = CollectionService:GetInstanceAddedSignal("ToolSoundGlitch")
+addSignal:Connect(onGlitchSoundAdded)
\ No newline at end of file
diff --git a/DataModel/ReplicatedStorage/ChatRemote.rbxmx b/DataModel/ReplicatedStorage/ChatRemote.rbxmx
new file mode 100644
index 0000000..720c5c2
--- /dev/null
+++ b/DataModel/ReplicatedStorage/ChatRemote.rbxmx
@@ -0,0 +1,10 @@
+
+ -
+
+
+ ChatRemote
+
+ true
+
+
+
\ No newline at end of file
diff --git a/DataModel/ReplicatedStorage/RequestCharacter.rbxmx b/DataModel/ReplicatedStorage/RequestCharacter.rbxmx
new file mode 100644
index 0000000..1cfc84b
--- /dev/null
+++ b/DataModel/ReplicatedStorage/RequestCharacter.rbxmx
@@ -0,0 +1,10 @@
+
+ -
+
+
+ RequestCharacter
+
+ true
+
+
+
\ No newline at end of file
diff --git a/DataModel/ReplicatedStorage/ScriptRef[SafeChatTree].txt b/DataModel/ReplicatedStorage/ScriptRef[SafeChatTree].txt
new file mode 100644
index 0000000..8f3a782
--- /dev/null
+++ b/DataModel/ReplicatedStorage/ScriptRef[SafeChatTree].txt
@@ -0,0 +1 @@
+Shared/Safechat
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Badges].txt b/DataModel/ServerScriptService/ScriptRef[Badges].txt
new file mode 100644
index 0000000..fa514df
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Badges].txt
@@ -0,0 +1 @@
+Server/Badges
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Bevels].txt b/DataModel/ServerScriptService/ScriptRef[Bevels].txt
new file mode 100644
index 0000000..a3bc009
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Bevels].txt
@@ -0,0 +1 @@
+Server/Bevels
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[BuildTools].txt b/DataModel/ServerScriptService/ScriptRef[BuildTools].txt
new file mode 100644
index 0000000..1b9f538
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[BuildTools].txt
@@ -0,0 +1 @@
+Server/BuildTools
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[CaptureTheFlag].txt b/DataModel/ServerScriptService/ScriptRef[CaptureTheFlag].txt
new file mode 100644
index 0000000..08d6ad1
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[CaptureTheFlag].txt
@@ -0,0 +1 @@
+Server/CaptureTheFlag
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Characters].txt b/DataModel/ServerScriptService/ScriptRef[Characters].txt
new file mode 100644
index 0000000..d3368f9
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Characters].txt
@@ -0,0 +1 @@
+Server/Characters
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Chat].txt b/DataModel/ServerScriptService/ScriptRef[Chat].txt
new file mode 100644
index 0000000..6b38a1b
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Chat].txt
@@ -0,0 +1 @@
+Server/Chat
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Cylinders].txt b/DataModel/ServerScriptService/ScriptRef[Cylinders].txt
new file mode 100644
index 0000000..d8fc0a5
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Cylinders].txt
@@ -0,0 +1 @@
+Server/Cylinders
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Explosions].txt b/DataModel/ServerScriptService/ScriptRef[Explosions].txt
new file mode 100644
index 0000000..9a83a88
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Explosions].txt
@@ -0,0 +1 @@
+Server/Explosions
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[ForceFields].txt b/DataModel/ServerScriptService/ScriptRef[ForceFields].txt
new file mode 100644
index 0000000..73b7a11
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[ForceFields].txt
@@ -0,0 +1 @@
+Server/ForceFields
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[HatGranter].txt b/DataModel/ServerScriptService/ScriptRef[HatGranter].txt
new file mode 100644
index 0000000..2a748f4
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[HatGranter].txt
@@ -0,0 +1 @@
+Server/HatGranter
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Heads].txt b/DataModel/ServerScriptService/ScriptRef[Heads].txt
new file mode 100644
index 0000000..41e6d52
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Heads].txt
@@ -0,0 +1 @@
+Server/Heads
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Leaderboard].txt b/DataModel/ServerScriptService/ScriptRef[Leaderboard].txt
new file mode 100644
index 0000000..0e2f085
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Leaderboard].txt
@@ -0,0 +1 @@
+Server/Leaderboard
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[LoadTools].txt b/DataModel/ServerScriptService/ScriptRef[LoadTools].txt
new file mode 100644
index 0000000..a6ef3fb
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[LoadTools].txt
@@ -0,0 +1 @@
+Server/LoadTools
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Parts].txt b/DataModel/ServerScriptService/ScriptRef[Parts].txt
new file mode 100644
index 0000000..53e206d
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Parts].txt
@@ -0,0 +1 @@
+Server/Parts
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Regeneration].txt b/DataModel/ServerScriptService/ScriptRef[Regeneration].txt
new file mode 100644
index 0000000..5e0880b
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Regeneration].txt
@@ -0,0 +1 @@
+Server/Regeneration
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[SessionTracker].txt b/DataModel/ServerScriptService/ScriptRef[SessionTracker].txt
new file mode 100644
index 0000000..a777492
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[SessionTracker].txt
@@ -0,0 +1 @@
+Server/SessionTracker
\ No newline at end of file
diff --git a/DataModel/ServerScriptService/ScriptRef[Time].txt b/DataModel/ServerScriptService/ScriptRef[Time].txt
new file mode 100644
index 0000000..63244ff
--- /dev/null
+++ b/DataModel/ServerScriptService/ScriptRef[Time].txt
@@ -0,0 +1 @@
+Server/Time
\ No newline at end of file
diff --git a/DataModel/ServerStorage/CoreBevelCache/1.00 ~ 0.40 ~ 1.00.rbxmx b/DataModel/ServerStorage/CoreBevelCache/1.00 ~ 0.40 ~ 1.00.rbxmx
new file mode 100644
index 0000000..65ffc6e
--- /dev/null
+++ b/DataModel/ServerStorage/CoreBevelCache/1.00 ~ 0.40 ~ 1.00.rbxmx
@@ -0,0 +1,99 @@
+
+ -
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 0
+ 1.800009
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ true
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 1
+ 0.4
+ 1
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 272
+
+ rbxassetid://3188648073
+
+
+ rbxassetid://3188648073
+
+ 1.00 ~ 0.40 ~ 1.00
+ +KXn2PVkmG8K5+S8Au8qxA==
+
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ 1
+ 0.4
+ 1
+
+ 0
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DataModel/ServerStorage/CoreBevelCache/1.00 ~ 1.00 ~ 4.00.rbxmx b/DataModel/ServerStorage/CoreBevelCache/1.00 ~ 1.00 ~ 4.00.rbxmx
new file mode 100644
index 0000000..e4dfcba
--- /dev/null
+++ b/DataModel/ServerStorage/CoreBevelCache/1.00 ~ 1.00 ~ 4.00.rbxmx
@@ -0,0 +1,99 @@
+
+ -
+
+ false
+
+ -0.5
+ 0.5
+ 3
+ 0
+ -0.5
+ 0.5
+ 3
+ 0
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ true
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 3
+ 0
+
+ 1
+ 1
+ 4
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ false
+ false
+ 272
+
+ rbxassetid://3188498185
+
+
+ rbxassetid://3188498185
+
+ 1.00 ~ 1.00 ~ 4.00
+ +KXn2PVkmG8K5+S8Au8qxA==
+
+ 0
+ 0
+ -0.5
+ 0.5
+ 3
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ 1
+ 1
+ 4
+
+ 0
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DataModel/ServerStorage/CoreBevelCache/2.00 ~ 1.20 ~ 4.00.rbxmx b/DataModel/ServerStorage/CoreBevelCache/2.00 ~ 1.20 ~ 4.00.rbxmx
new file mode 100644
index 0000000..6271c1c
--- /dev/null
+++ b/DataModel/ServerStorage/CoreBevelCache/2.00 ~ 1.20 ~ 4.00.rbxmx
@@ -0,0 +1,99 @@
+
+ -
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 0
+ 11.80001
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ true
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 2
+ 1.2
+ 4
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 272
+
+ rbxassetid://3188159241
+
+
+ rbxassetid://3188159241
+
+ 2.00 ~ 1.20 ~ 4.00
+ +KXn2PVkmG8K5+S8Au8qxA==
+
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ 2
+ 1.2
+ 4
+
+ 0
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DataModel/ServerStorage/GrantHatToUser.rbxmx b/DataModel/ServerStorage/GrantHatToUser.rbxmx
new file mode 100644
index 0000000..fb38727
--- /dev/null
+++ b/DataModel/ServerStorage/GrantHatToUser.rbxmx
@@ -0,0 +1,10 @@
+
+ -
+
+
+ GrantHatToUser
+
+ true
+
+
+
\ No newline at end of file
diff --git a/DataModel/ServerStorage/InputGateway/Gateway.rbxmx b/DataModel/ServerStorage/InputGateway/Gateway.rbxmx
new file mode 100644
index 0000000..55f7415
--- /dev/null
+++ b/DataModel/ServerStorage/InputGateway/Gateway.rbxmx
@@ -0,0 +1,10 @@
+
+ -
+
+
+ Gateway
+
+ true
+
+
+
\ No newline at end of file
diff --git a/DataModel/ServerStorage/InputGateway/ScriptRef[Client].txt b/DataModel/ServerStorage/InputGateway/ScriptRef[Client].txt
new file mode 100644
index 0000000..6ed542c
--- /dev/null
+++ b/DataModel/ServerStorage/InputGateway/ScriptRef[Client].txt
@@ -0,0 +1 @@
+Client/InputGateway
\ No newline at end of file
diff --git a/DataModel/ServerStorage/InputGateway/ScriptRef[Server].txt b/DataModel/ServerStorage/InputGateway/ScriptRef[Server].txt
new file mode 100644
index 0000000..c6b231b
--- /dev/null
+++ b/DataModel/ServerStorage/InputGateway/ScriptRef[Server].txt
@@ -0,0 +1 @@
+Server/InputGateway
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[Animate].txt b/DataModel/StarterCharacterScripts/ScriptRef[Animate].txt
new file mode 100644
index 0000000..2ff7774
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[Animate].txt
@@ -0,0 +1 @@
+Character/Animate
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[BouncinessReplica].txt b/DataModel/StarterCharacterScripts/ScriptRef[BouncinessReplica].txt
new file mode 100644
index 0000000..c50d43d
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[BouncinessReplica].txt
@@ -0,0 +1 @@
+Character/Bounciness
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[DropHats].txt b/DataModel/StarterCharacterScripts/ScriptRef[DropHats].txt
new file mode 100644
index 0000000..e8d2c28
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[DropHats].txt
@@ -0,0 +1 @@
+Character/DropHats
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[EdgeWalking].txt b/DataModel/StarterCharacterScripts/ScriptRef[EdgeWalking].txt
new file mode 100644
index 0000000..ab00f28
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[EdgeWalking].txt
@@ -0,0 +1 @@
+Character/EdgeWalking
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[FloorDrag].txt b/DataModel/StarterCharacterScripts/ScriptRef[FloorDrag].txt
new file mode 100644
index 0000000..679232f
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[FloorDrag].txt
@@ -0,0 +1 @@
+Character/FloorDrag
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[GoofyBalance].txt b/DataModel/StarterCharacterScripts/ScriptRef[GoofyBalance].txt
new file mode 100644
index 0000000..07081df
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[GoofyBalance].txt
@@ -0,0 +1 @@
+Character/GoofyBalance
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[GoofyMotion].txt b/DataModel/StarterCharacterScripts/ScriptRef[GoofyMotion].txt
new file mode 100644
index 0000000..907b0a3
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[GoofyMotion].txt
@@ -0,0 +1 @@
+Character/GoofyMotion
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[InputGateway].txt b/DataModel/StarterCharacterScripts/ScriptRef[InputGateway].txt
new file mode 100644
index 0000000..6e56109
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[InputGateway].txt
@@ -0,0 +1 @@
+Character/InputGateway
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[JumpLimiter].txt b/DataModel/StarterCharacterScripts/ScriptRef[JumpLimiter].txt
new file mode 100644
index 0000000..f33b63b
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[JumpLimiter].txt
@@ -0,0 +1 @@
+Character/JumpLimiter
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[RetroClimbing].txt b/DataModel/StarterCharacterScripts/ScriptRef[RetroClimbing].txt
new file mode 100644
index 0000000..741473f
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[RetroClimbing].txt
@@ -0,0 +1 @@
+Character/RetroClimbing
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[Sound].txt b/DataModel/StarterCharacterScripts/ScriptRef[Sound].txt
new file mode 100644
index 0000000..9cca5fd
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[Sound].txt
@@ -0,0 +1 @@
+Character/Sound
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[TeamColorScript].txt b/DataModel/StarterCharacterScripts/ScriptRef[TeamColorScript].txt
new file mode 100644
index 0000000..d252674
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[TeamColorScript].txt
@@ -0,0 +1 @@
+Character/TeamColors
\ No newline at end of file
diff --git a/DataModel/StarterCharacterScripts/ScriptRef[ToolSoundGlitch].txt b/DataModel/StarterCharacterScripts/ScriptRef[ToolSoundGlitch].txt
new file mode 100644
index 0000000..fd1141e
--- /dev/null
+++ b/DataModel/StarterCharacterScripts/ScriptRef[ToolSoundGlitch].txt
@@ -0,0 +1 @@
+Character/ToolSoundGlitch
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/Backpack/ScriptRef[BackpackScript].txt b/DataModel/StarterGui/UI/RootFrame/Backpack/ScriptRef[BackpackScript].txt
new file mode 100644
index 0000000..a4c82be
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/Backpack/ScriptRef[BackpackScript].txt
@@ -0,0 +1 @@
+Client/Backpack
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/Chat/ScriptRef[ChatScript].txt b/DataModel/StarterGui/UI/RootFrame/Chat/ScriptRef[ChatScript].txt
new file mode 100644
index 0000000..09237ac
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/Chat/ScriptRef[ChatScript].txt
@@ -0,0 +1 @@
+Client/ChatScript
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/ClassicMouse/ScriptRef[ClickToMove].txt b/DataModel/StarterGui/UI/RootFrame/ClassicMouse/ScriptRef[ClickToMove].txt
new file mode 100644
index 0000000..e26dc43
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/ClassicMouse/ScriptRef[ClickToMove].txt
@@ -0,0 +1 @@
+Client/ClickToMove
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/ClassicMouse/ScriptRef[Mouse].txt b/DataModel/StarterGui/UI/RootFrame/ClassicMouse/ScriptRef[Mouse].txt
new file mode 100644
index 0000000..39014e6
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/ClassicMouse/ScriptRef[Mouse].txt
@@ -0,0 +1 @@
+Client/UnivShared
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/Health/ScriptRef[HealthScript].txt b/DataModel/StarterGui/UI/RootFrame/Health/ScriptRef[HealthScript].txt
new file mode 100644
index 0000000..ec49814
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/Health/ScriptRef[HealthScript].txt
@@ -0,0 +1 @@
+Client/Health
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/PlayerList/ScriptRef[PlayerList].txt b/DataModel/StarterGui/UI/RootFrame/PlayerList/ScriptRef[PlayerList].txt
new file mode 100644
index 0000000..cdc323e
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/PlayerList/ScriptRef[PlayerList].txt
@@ -0,0 +1 @@
+Client/PlayerList
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/SafeChat/ScriptRef[SafeChatScript].txt b/DataModel/StarterGui/UI/RootFrame/SafeChat/ScriptRef[SafeChatScript].txt
new file mode 100644
index 0000000..670e36f
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/SafeChat/ScriptRef[SafeChatScript].txt
@@ -0,0 +1 @@
+Client/Safechat
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/ScriptRef[ConsoleTweaks].txt b/DataModel/StarterGui/UI/RootFrame/ScriptRef[ConsoleTweaks].txt
new file mode 100644
index 0000000..fc9aa75
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/ScriptRef[ConsoleTweaks].txt
@@ -0,0 +1 @@
+Client/ConsoleTweaks
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/ScriptRef[MessageScript].txt b/DataModel/StarterGui/UI/RootFrame/ScriptRef[MessageScript].txt
new file mode 100644
index 0000000..ef6c229
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/ScriptRef[MessageScript].txt
@@ -0,0 +1 @@
+Client/Messages
\ No newline at end of file
diff --git a/DataModel/StarterGui/UI/RootFrame/ZoomControls/ScriptRef[ZoomControls].txt b/DataModel/StarterGui/UI/RootFrame/ZoomControls/ScriptRef[ZoomControls].txt
new file mode 100644
index 0000000..535d78f
--- /dev/null
+++ b/DataModel/StarterGui/UI/RootFrame/ZoomControls/ScriptRef[ZoomControls].txt
@@ -0,0 +1 @@
+Client/ZoomControls
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/PassCameraEvent.rbxmx b/DataModel/StarterPlayerScripts/PassCameraEvent.rbxmx
new file mode 100644
index 0000000..aa15364
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/PassCameraEvent.rbxmx
@@ -0,0 +1,10 @@
+
+ -
+
+
+ PassCameraEvent
+
+ true
+
+
+
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[Animator].txt b/DataModel/StarterPlayerScripts/ScriptRef[Animator].txt
new file mode 100644
index 0000000..fd12a68
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[Animator].txt
@@ -0,0 +1 @@
+Client/Animator
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[CharacterBevels].txt b/DataModel/StarterPlayerScripts/ScriptRef[CharacterBevels].txt
new file mode 100644
index 0000000..3cd51b9
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[CharacterBevels].txt
@@ -0,0 +1 @@
+Client/CharacterBevels
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[Explosions].txt b/DataModel/StarterPlayerScripts/ScriptRef[Explosions].txt
new file mode 100644
index 0000000..c44cc0b
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[Explosions].txt
@@ -0,0 +1 @@
+Client/Explosions
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[ForceFields].txt b/DataModel/StarterPlayerScripts/ScriptRef[ForceFields].txt
new file mode 100644
index 0000000..9f61fe8
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[ForceFields].txt
@@ -0,0 +1 @@
+Client/ForceFields
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[FpsCap].txt b/DataModel/StarterPlayerScripts/ScriptRef[FpsCap].txt
new file mode 100644
index 0000000..39014e6
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[FpsCap].txt
@@ -0,0 +1 @@
+Client/UnivShared
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[GamepadPatch].txt b/DataModel/StarterPlayerScripts/ScriptRef[GamepadPatch].txt
new file mode 100644
index 0000000..cb610e4
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[GamepadPatch].txt
@@ -0,0 +1 @@
+Client/GamepadPatch
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[HumanoidLabels].txt b/DataModel/StarterPlayerScripts/ScriptRef[HumanoidLabels].txt
new file mode 100644
index 0000000..d8fbc00
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[HumanoidLabels].txt
@@ -0,0 +1 @@
+Client/HumanoidLabels
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[LensFlare].txt b/DataModel/StarterPlayerScripts/ScriptRef[LensFlare].txt
new file mode 100644
index 0000000..39014e6
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[LensFlare].txt
@@ -0,0 +1 @@
+Client/UnivShared
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[LensFlare]/Moon.rbxmx b/DataModel/StarterPlayerScripts/ScriptRef[LensFlare]/Moon.rbxmx
new file mode 100644
index 0000000..1bfd638
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[LensFlare]/Moon.rbxmx
@@ -0,0 +1,194 @@
+
+ -
+
+ true
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 4
+ 0
+
+ 511.7728
+ 175.8011
+ 482.2249
+ 0.74845
+ -0.2371427
+ 0.6193432
+ 1.490116E-08
+ 0.9338833
+ 0.3575782
+ -0.6631912
+ -0.2676294
+ 0.698965
+
+ false
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Moon
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 1
+
+ 0
+ 0
+ 0
+
+ 1
+ 1
+
+ 100
+ 100
+ 1
+
+ true
+
+
-
+
+ true
+
[null]
+ false
+
+ true
+
+ 800
+ 800
+
+ false
+ true
+ 5
+ 0
+ MoonGui
+ 50
+ true
+ [null]
+ 0
+
+ 0
+ 0
+ 0
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://599112257
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ MoonImage
+
[null]
+ [null]
+ [null]
+ [null]
+
+ -0.5
+ 0
+ -0.5
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 2
+ 0
+ 2
+ 0
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 1
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[Moon].txt b/DataModel/StarterPlayerScripts/ScriptRef[Moon].txt
new file mode 100644
index 0000000..39014e6
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[Moon].txt
@@ -0,0 +1 @@
+Client/UnivShared
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[Moon]/Moon.rbxmx b/DataModel/StarterPlayerScripts/ScriptRef[Moon]/Moon.rbxmx
new file mode 100644
index 0000000..6540e52
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[Moon]/Moon.rbxmx
@@ -0,0 +1,194 @@
+
+ -
+
+ true
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 4
+ 0
+
+ 511.7728
+ 175.8011
+ 482.2249
+ 0.74845
+ -0.2371427
+ 0.6193432
+ 1.490116E-08
+ 0.9338833
+ 0.3575782
+ -0.6631912
+ -0.2676294
+ 0.698965
+
+ false
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Moon
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 1
+
+ 0
+ 0
+ 0
+
+ 1
+ 1
+
+ 80
+ 80
+ 1
+
+ true
+
+
-
+
+ true
+
[null]
+ false
+
+ true
+
+ 800
+ 800
+
+ false
+ true
+ 5
+ 0
+ MoonGui
+ 50
+ true
+ [null]
+ 0
+
+ 0
+ 0
+ 0
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://599112257
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ MoonImage
+
[null]
+ [null]
+ [null]
+ [null]
+
+ -0.5
+ 0
+ -0.5
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 2
+ 0
+ 2
+ 0
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 1
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[Music].txt b/DataModel/StarterPlayerScripts/ScriptRef[Music].txt
new file mode 100644
index 0000000..bff4c2a
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[Music].txt
@@ -0,0 +1 @@
+Client/Music
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[RetroCam].txt b/DataModel/StarterPlayerScripts/ScriptRef[RetroCam].txt
new file mode 100644
index 0000000..3f75c4b
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[RetroCam].txt
@@ -0,0 +1 @@
+Client/Camera
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[Sky].txt b/DataModel/StarterPlayerScripts/ScriptRef[Sky].txt
new file mode 100644
index 0000000..39014e6
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[Sky].txt
@@ -0,0 +1 @@
+Client/UnivShared
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[Sky]/SkyAdorn.rbxmx b/DataModel/StarterPlayerScripts/ScriptRef[Sky]/SkyAdorn.rbxmx
new file mode 100644
index 0000000..385533a
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[Sky]/SkyAdorn.rbxmx
@@ -0,0 +1,186 @@
+
+ -
+
+ true
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 4
+ 0
+
+ 491.4301
+ 0.025031
+ 324.0601
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ false
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ SkyAdorn
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 1
+
+ 0
+ 0
+ 0
+
+ 1
+ 1
+
+ 0.05
+ 0.05
+ 0.05
+
+ true
+
+
-
+
+ false
+
[null]
+ false
+
+ true
+ false
+ 0
+ 0
+ -1
+ true
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+ 1
+ INF
+ Night
+ [null]
+ true
+ [null]
+
+ 0
+ 3000
+ 0
+ 3000
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+ -3000
+
+
+ 0
+ 0
+ 0
+
+
+ 0
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ NightFrame
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[Sky]/Star.rbxmx b/DataModel/StarterPlayerScripts/ScriptRef[Sky]/Star.rbxmx
new file mode 100644
index 0000000..5adffc2
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[Sky]/Star.rbxmx
@@ -0,0 +1,109 @@
+
+ -
+
+ false
+
[null]
+ false
+
+ true
+ false
+ 0
+ 0
+ -1
+ true
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+ 0
+ INF
+ Star
+ [null]
+ true
+ [null]
+
+ 0
+ 3
+ 0
+ 3
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+ 0
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ Frame
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+
+
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[SunRays].txt b/DataModel/StarterPlayerScripts/ScriptRef[SunRays].txt
new file mode 100644
index 0000000..39014e6
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[SunRays].txt
@@ -0,0 +1 @@
+Client/UnivShared
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[SunRays]/Moon.rbxmx b/DataModel/StarterPlayerScripts/ScriptRef[SunRays]/Moon.rbxmx
new file mode 100644
index 0000000..8ec2944
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[SunRays]/Moon.rbxmx
@@ -0,0 +1,194 @@
+
+ -
+
+ true
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 4
+ 0
+
+ 511.7728
+ 175.8011
+ 482.2249
+ 0.74845
+ -0.2371427
+ 0.6193432
+ 1.490116E-08
+ 0.9338833
+ 0.3575782
+ -0.6631912
+ -0.2676294
+ 0.698965
+
+ false
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Moon
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 1
+
+ 0
+ 0
+ 0
+
+ 1
+ 1
+
+ 100
+ 100
+ 1
+
+ true
+
+
-
+
+ true
+
[null]
+ false
+
+ true
+
+ 800
+ 800
+
+ false
+ true
+ 5
+ 0
+ MoonGui
+ 50
+ true
+ [null]
+ 0
+
+ 0
+ 0
+ 0
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://599112257
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ MoonImage
+
[null]
+ [null]
+ [null]
+ [null]
+
+ -0.5
+ 0
+ -0.5
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 2
+ 0
+ 2
+ 0
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 1
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/DataModel/StarterPlayerScripts/ScriptRef[ToolSoundGlitch].txt b/DataModel/StarterPlayerScripts/ScriptRef[ToolSoundGlitch].txt
new file mode 100644
index 0000000..a9edc75
--- /dev/null
+++ b/DataModel/StarterPlayerScripts/ScriptRef[ToolSoundGlitch].txt
@@ -0,0 +1 @@
+Client/ToolSoundGlitch
\ No newline at end of file
diff --git a/Resources/BevelCache/1.00 ~ 0.40 ~ 1.00.rbxmx b/Resources/BevelCache/1.00 ~ 0.40 ~ 1.00.rbxmx
new file mode 100644
index 0000000..65ffc6e
--- /dev/null
+++ b/Resources/BevelCache/1.00 ~ 0.40 ~ 1.00.rbxmx
@@ -0,0 +1,99 @@
+
+ -
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 0
+ 1.800009
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ true
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 1
+ 0.4
+ 1
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 272
+
+ rbxassetid://3188648073
+
+
+ rbxassetid://3188648073
+
+ 1.00 ~ 0.40 ~ 1.00
+ +KXn2PVkmG8K5+S8Au8qxA==
+
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ 1
+ 0.4
+ 1
+
+ 0
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Resources/BevelCache/1.00 ~ 1.00 ~ 4.00.rbxmx b/Resources/BevelCache/1.00 ~ 1.00 ~ 4.00.rbxmx
new file mode 100644
index 0000000..e4dfcba
--- /dev/null
+++ b/Resources/BevelCache/1.00 ~ 1.00 ~ 4.00.rbxmx
@@ -0,0 +1,99 @@
+
+ -
+
+ false
+
+ -0.5
+ 0.5
+ 3
+ 0
+ -0.5
+ 0.5
+ 3
+ 0
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ true
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 3
+ 0
+
+ 1
+ 1
+ 4
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ false
+ false
+ 272
+
+ rbxassetid://3188498185
+
+
+ rbxassetid://3188498185
+
+ 1.00 ~ 1.00 ~ 4.00
+ +KXn2PVkmG8K5+S8Au8qxA==
+
+ 0
+ 0
+ -0.5
+ 0.5
+ 3
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ 1
+ 1
+ 4
+
+ 0
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Resources/BevelCache/2.00 ~ 1.20 ~ 4.00.rbxmx b/Resources/BevelCache/2.00 ~ 1.20 ~ 4.00.rbxmx
new file mode 100644
index 0000000..6271c1c
--- /dev/null
+++ b/Resources/BevelCache/2.00 ~ 1.20 ~ 4.00.rbxmx
@@ -0,0 +1,99 @@
+
+ -
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 0
+ 11.80001
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ true
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 2
+ 1.2
+ 4
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 272
+
+ rbxassetid://3188159241
+
+
+ rbxassetid://3188159241
+
+ 2.00 ~ 1.20 ~ 4.00
+ +KXn2PVkmG8K5+S8Au8qxA==
+
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ 2
+ 1.2
+ 4
+
+ 0
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Resources/CharacterAssets/BodyColors.rbxmx b/Resources/CharacterAssets/BodyColors.rbxmx
new file mode 100644
index 0000000..1935aac
--- /dev/null
+++ b/Resources/CharacterAssets/BodyColors.rbxmx
@@ -0,0 +1,40 @@
+
+ -
+
+
+
+ 0.9607844
+ 0.8039216
+ 0.1882353
+
+
+ 0.9607844
+ 0.8039216
+ 0.1882353
+
+
+ 0.6431373
+ 0.7411765
+ 0.2784314
+
+ BodyColors
+
+ 0.9607844
+ 0.8039216
+ 0.1882353
+
+
+ 0.6431373
+ 0.7411765
+ 0.2784314
+
+
+
+ 0.0509804
+ 0.4117647
+ 0.6745098
+
+ true
+
+
+
\ No newline at end of file
diff --git a/Resources/CharacterAssets/Pants.rbxmx b/Resources/CharacterAssets/Pants.rbxmx
new file mode 100644
index 0000000..9598336
--- /dev/null
+++ b/Resources/CharacterAssets/Pants.rbxmx
@@ -0,0 +1,18 @@
+
+ -
+
+
+
+ 1
+ 1
+ 1
+
+ Pants
+
+ rbxassetid://1110695628
+
+
+ true
+
+
+
\ No newline at end of file
diff --git a/Resources/CharacterAssets/Shirt.rbxmx b/Resources/CharacterAssets/Shirt.rbxmx
new file mode 100644
index 0000000..d002a2b
--- /dev/null
+++ b/Resources/CharacterAssets/Shirt.rbxmx
@@ -0,0 +1,18 @@
+
+ -
+
+
+
+ 1
+ 1
+ 1
+
+ Shirt
+
+ rbxassetid://1110695025
+
+
+ true
+
+
+
\ No newline at end of file
diff --git a/Resources/CharacterAssets/ShirtGraphic.rbxmx b/Resources/CharacterAssets/ShirtGraphic.rbxmx
new file mode 100644
index 0000000..677d710
--- /dev/null
+++ b/Resources/CharacterAssets/ShirtGraphic.rbxmx
@@ -0,0 +1,18 @@
+
+ -
+
+
+
+ 1
+ 1
+ 1
+
+
+
+
+ ShirtGraphic
+
+ true
+
+
+
\ No newline at end of file
diff --git a/Resources/GameJoin/GuiRoot.rbxmx b/Resources/GameJoin/GuiRoot.rbxmx
new file mode 100644
index 0000000..60e1ad3
--- /dev/null
+++ b/Resources/GameJoin/GuiRoot.rbxmx
@@ -0,0 +1,1169 @@
+
+ -
+
+
+ true
+ 0
+ true
+ false
+ GuiRoot
+ false
+
[null]
+
+ 0
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.6980392
+ 0.6980392
+ 0.6980392
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ Topbar
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 80
+ 0
+ -36
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 660
+ 0
+ 27
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+ -
+
+ true
+
+ 0
+ 0
+
+
+ false
+ true
+
+ 0.6980392
+ 0.6980392
+ 0.6980392
+
+ 0.5
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+ Exit
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 528
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+ Exit
+
+ 0.4039216
+ 0.4039216
+ 0.4039216
+
+ true
+ 14
+
+ 0.4039216
+ 0.4039216
+ 0.4039216
+
+ 0.9
+ 0.3
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+ true
+
+ -
+
+
+ 20
+ 1
+ UITextSizeConstraint
+
+ true
+
+
+
+ -
+
+ true
+
+ 0
+ 0
+
+
+ false
+ true
+
+ 0.6980392
+ 0.6980392
+ 0.6980392
+
+ 0.5
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+ Help
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 396
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+ Help...
+
+ 0.4039216
+ 0.4039216
+ 0.4039216
+
+ true
+ 14
+
+ 0.4039216
+ 0.4039216
+ 0.4039216
+
+ 0.9
+ 0.3
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+ true
+
+ -
+
+
+ 20
+ 1
+ UITextSizeConstraint
+
+ true
+
+
+
+ -
+
+ true
+
+ 0
+ 0
+
+
+ false
+ true
+
+ 0.6980392
+ 0.6980392
+ 0.6980392
+
+ 0.5
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+ Fullscreen
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 264
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+ Fullscreen
+
+ 0.4039216
+ 0.4039216
+ 0.4039216
+
+ true
+ 14
+
+ 0.4039216
+ 0.4039216
+ 0.4039216
+
+ 0.9
+ 0.3
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+ true
+
+ -
+
+
+ 20
+ 1
+ UITextSizeConstraint
+
+ true
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ false
+ true
+
+ 0.6980392
+ 0.6980392
+ 0.6980392
+
+ 0.5
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+ Insert
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 132
+ 0
+ 0
+
+ [null]
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+ Insert
+
+ 0.627451
+ 0.627451
+ 0.627451
+
+ true
+ 14
+
+ 0.627451
+ 0.627451
+ 0.627451
+
+ 0.9
+ 0.3
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+ true
+
+ -
+
+
+ 20
+ 1
+ UITextSizeConstraint
+
+ true
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ false
+ true
+
+ 0.6980392
+ 0.6980392
+ 0.6980392
+
+ 0.5
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+ Tools
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+ Tools
+
+ 0.627451
+ 0.627451
+ 0.627451
+
+ true
+ 14
+
+ 0.627451
+ 0.627451
+ 0.627451
+
+ 0.9
+ 0.3
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+ true
+
+ -
+
+
+ 20
+ 1
+ UITextSizeConstraint
+
+ true
+
+
+
+
+ -
+
+ false
+
+ 0.5
+ 0.5
+
+
+ true
+
+ 0.7058824
+ 0.7058824
+ 0.7058824
+
+ 0.5
+
+ 1
+ 1
+ 1
+
+ 0
+ 3
+ false
+ false
+ 0
+ MessageGui
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.6
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ false
+ 1
+ true
+
+ -
+
+ false
+
+ 0
+ 0.5
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ Message
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.125
+ 0
+
+ 0
+
+ Connecting to server...
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 1
+ 1
+ 1
+
+ 0.75
+ 0
+ 0
+ true
+ 2
+ 1
+ true
+ 1
+ true
+
+
+ -
+
+ 3
+ 0
+
+ 1
+ UIAspectRatioConstraint
+
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0.5
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ ExitOverride
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.25
+ 0
+
+ 0
+
+
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 1
+ 1
+ 1
+
+ 0.75
+ 0
+ 0
+ true
+ 2
+ 1
+ false
+ 1
+ true
+
+
+
+ -
+
+ true
+
+ 0.5
+ 0.5
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ true
+
+ rbxassetid://1041546985
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ HelpWindow
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ 1
+ false
+ [null]
+
+ 0.8
+ 0
+ 0.7
+ 0
+
+ 0
+
+
+ 4
+ 30
+
+
+ 304
+ 130
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ false
+ 1
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://1041647615
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ Help
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 4
+ 0
+ 31
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 1
+ -8
+ 1
+ -36
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 1
+ true
+
+ -
+
+ 2.75
+ 0
+
+ 0
+ UIAspectRatioConstraint
+
+ true
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 2
+ 0
+ 1
+ Title
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 5
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.9
+ -10
+ 0
+ 30
+
+ 0
+
+ ROBLOX Help
+
+ 1
+ 1
+ 1
+
+ false
+ 14
+
+ 0.4980392
+ 0.4980392
+ 0.4980392
+
+ 0.6
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 2
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 2
+ 0
+ 1
+ Stroke
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 1
+ 0
+ 1
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+
+ ROBLOX Help
+
+ 0
+ 0
+ 0
+
+ false
+ 14
+
+ 1
+ 1
+ 1
+
+ 1
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 1
+ true
+
+
+
+ -
+
+ true
+
+ 1
+ 0
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+
+
+
+
+ rbxassetid://1041651899
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ true
+ Close
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ -5
+ 0
+ 5
+
+
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 22
+ 0
+ 22
+
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 1
+ true
+
+
+ -
+
+ 2.5
+ 0
+
+ 0
+ UIAspectRatioConstraint
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Resources/GameJoin/GuiRoot/Topbar/Buttons.client.lua b/Resources/GameJoin/GuiRoot/Topbar/Buttons.client.lua
new file mode 100644
index 0000000..c795091
--- /dev/null
+++ b/Resources/GameJoin/GuiRoot/Topbar/Buttons.client.lua
@@ -0,0 +1,22 @@
+local topbar = script.Parent
+
+local function registerButton(btn)
+ if btn:IsA("TextButton") and btn.Active then
+ local function onMouseEnter()
+ btn.BackgroundTransparency = 0
+ end
+
+ local function onMouseLeave()
+ btn.BackgroundTransparency = 0.5
+ end
+
+ btn.MouseEnter:Connect(onMouseEnter)
+ btn.MouseLeave:Connect(onMouseLeave)
+ end
+end
+
+for _,v in pairs(topbar:GetChildren()) do
+ registerButton(v)
+end
+
+topbar.ChildAdded:Connect(registerButton)
\ No newline at end of file
diff --git a/Resources/GameJoin/GuiRoot/Topbar/Exit/Exit.client.lua b/Resources/GameJoin/GuiRoot/Topbar/Exit/Exit.client.lua
new file mode 100644
index 0000000..8d6ec06
--- /dev/null
+++ b/Resources/GameJoin/GuiRoot/Topbar/Exit/Exit.client.lua
@@ -0,0 +1,55 @@
+local TeleportService = game:GetService("TeleportService")
+local GuiService = game:GetService("GuiService")
+local UserInputService = game:GetService("UserInputService")
+GuiService.AutoSelectGuiEnabled = false
+
+local btn = script.Parent
+local topbar = btn.Parent
+local root = topbar.Parent
+local messageGui = root:WaitForChild("MessageGui")
+local message = messageGui:WaitForChild("Message")
+local exitOverride = messageGui:WaitForChild("ExitOverride")
+
+local function onClicked()
+ local visibleSignal = messageGui:GetPropertyChangedSignal("Visible")
+ message.Visible = false
+ exitOverride.Visible = true
+ messageGui.Visible = true
+ TeleportService:Teleport(998374377)
+ visibleSignal:Connect(function ()
+ if not messageGui.Visible then
+ messageGui.Visible = true
+ end
+ end)
+end
+
+if not GuiService:IsTenFootInterface() then
+ btn.MouseButton1Down:Connect(onClicked)
+end
+
+local exitBuffer = "Continue holding down 'Back' to return to the menu.\nExiting in...\n%.1f"
+
+local function onInputBegan(input)
+ if input.KeyCode == Enum.KeyCode.ButtonSelect and not exitOverride.Visible and not messageGui.Visible then
+ messageGui.Visible = true
+ message.Size = exitOverride.Size
+
+ local success = true
+ for i = 3,0,-.1 do
+ if input.UserInputState ~= Enum.UserInputState.Begin then
+ success = false
+ break
+ end
+ message.Text = exitBuffer:format(i)
+ wait(.1)
+ end
+
+ if success then
+ onClicked()
+ else
+ messageGui.Visible = false
+ end
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
\ No newline at end of file
diff --git a/Resources/GameJoin/GuiRoot/Topbar/Fullscreen/Fullscreen.client.lua b/Resources/GameJoin/GuiRoot/Topbar/Fullscreen/Fullscreen.client.lua
new file mode 100644
index 0000000..43c70fb
--- /dev/null
+++ b/Resources/GameJoin/GuiRoot/Topbar/Fullscreen/Fullscreen.client.lua
@@ -0,0 +1,31 @@
+local UserInputService = game:GetService("UserInputService")
+
+local btn = script.Parent
+local gameSettings = UserSettings():GetService("UserGameSettings")
+local player = game.Players.LocalPlayer
+
+local function onClick()
+ if not player:FindFirstChild("FullscreenMsg") then
+ local m = Instance.new("Message")
+ m.Name = "FullscreenMsg"
+ m.Text = "This button is just here for legacy aesthetics, and has no functionality."
+ if UserInputService.MouseEnabled and UserInputService.KeyboardEnabled then
+ m.Text = m.Text .. "\nPress F11 to toggle fullscreen!"
+ end
+ m.Parent = player
+ wait(3)
+ m:Destroy()
+ end
+end
+
+local function update()
+ if gameSettings:InFullScreen() then
+ btn.Text = "\t\tx Fullscreen"
+ else
+ btn.Text = "\t\tFullscreen"
+ end
+end
+
+update()
+gameSettings.FullscreenChanged:connect(update)
+btn.MouseButton1Down:Connect(onClick)
\ No newline at end of file
diff --git a/Resources/GameJoin/GuiRoot/Topbar/Help/Help.client.lua b/Resources/GameJoin/GuiRoot/Topbar/Help/Help.client.lua
new file mode 100644
index 0000000..7f90875
--- /dev/null
+++ b/Resources/GameJoin/GuiRoot/Topbar/Help/Help.client.lua
@@ -0,0 +1,16 @@
+local help = script.Parent
+local topbar = help.Parent
+local root = topbar.Parent
+local window = root:WaitForChild("HelpWindow")
+local close = window:WaitForChild("Close")
+
+local function onOpen()
+ window.Visible = true
+end
+
+local function onClose()
+ window.Visible = false
+end
+
+help.MouseButton1Down:Connect(onOpen)
+close.MouseButton1Down:Connect(onClose)
\ No newline at end of file
diff --git a/Resources/GameJoin/init.client.lua b/Resources/GameJoin/init.client.lua
new file mode 100644
index 0000000..6069745
--- /dev/null
+++ b/Resources/GameJoin/init.client.lua
@@ -0,0 +1,160 @@
+local CollectionService = game:GetService("CollectionService")
+local RunService = game:GetService("RunService")
+local UserInputService = game:GetService("UserInputService")
+local TeleportService = game:GetService("TeleportService")
+local ReplicatedFirst = script.Parent
+local JointsService = game:GetService("JointsService")
+
+do
+ local StarterGui = game:GetService("StarterGui")
+
+ local function setCoreSafe(method,...)
+ while not pcall(StarterGui.SetCore, StarterGui, method,...) do
+ wait()
+ end
+ end
+
+ spawn(function ()
+ setCoreSafe("ResetButtonCallback", false)
+ end)
+
+ setCoreSafe("TopbarEnabled", false)
+end
+
+local player = game.Players.LocalPlayer
+local playerGui = player:WaitForChild("PlayerGui")
+local mouse = player:GetMouse()
+
+if not UserInputService.TouchEnabled then
+ mouse.Icon = "rbxassetid://334630296"
+end
+
+local guiRoot = script:WaitForChild("GuiRoot")
+guiRoot.Parent = playerGui
+
+ReplicatedFirst:RemoveDefaultLoadingScreen()
+
+if playerGui:FindFirstChild("ConnectingGui") then
+ playerGui.ConnectingGui:Destroy()
+end
+
+if RunService:IsStudio() then
+ return
+end
+
+local c = workspace.CurrentCamera
+local IS_PHONE = c.ViewportSize.Y < 600
+
+local topbar = guiRoot:WaitForChild("Topbar")
+
+if IS_PHONE then
+ local uiScale = Instance.new("UIScale")
+ uiScale.Scale = 0.6
+ uiScale.Parent = topbar
+end
+
+local messageGui = guiRoot:WaitForChild("MessageGui")
+local message = messageGui:WaitForChild("Message")
+
+local partWatch = nil
+local partQueue = {}
+
+local bricks = 0
+local connectors = 0
+local messageFormat = "Bricks: %d Connectors: %d"
+
+---------------------------------------------------------------------
+
+local fakeLoadTime = TeleportService:GetTeleportSetting("FakeLoadTime")
+
+local function onDescendantAdded(desc)
+ if desc:IsA("BasePart") and not desc:IsA("Terrain") then
+ if not CollectionService:HasTag(desc, "AxisPart") and desc.Name ~= "__negatepart" then
+ desc.LocalTransparencyModifier = 1
+ partQueue[#partQueue + 1] = desc
+ end
+ elseif desc:IsA("Decal") then
+ desc.LocalTransparencyModifier = 1
+ end
+end
+
+if fakeLoadTime then
+ local descendants = workspace:GetDescendants()
+
+ for _,desc in pairs(descendants) do
+ onDescendantAdded(desc)
+ end
+
+ partWatch = workspace.DescendantAdded:Connect(onDescendantAdded)
+end
+
+---------------------------------------------------------------------
+
+local c = workspace.CurrentCamera
+c.CameraType = "Follow"
+c.CameraSubject = workspace
+
+messageGui.Visible = true
+
+local bricks = 0
+local connectors = 0
+local lastUpdate = 0
+
+local done = false
+
+local function stepBrickConnectorStatus()
+ if fakeLoadTime then
+ wait(math.random() / 4)
+
+ for i = 1, math.random(30, 50) do
+ local part = table.remove(partQueue)
+
+ if part then
+ bricks = bricks + 1
+
+ connectors = connectors + #part:GetJoints()
+ part.LocalTransparencyModifier = 0
+
+ for _,v in pairs(part:GetDescendants()) do
+ if v:IsA("Decal") then
+ v.LocalTransparencyModifier = 0
+ end
+ end
+ end
+ end
+
+ done = (#partQueue == 0)
+ else
+ wait()
+ done = game:IsLoaded()
+ end
+end
+
+while not done do
+ stepBrickConnectorStatus()
+ message.Text = messageFormat:format(bricks, connectors)
+end
+
+if partWatch then
+ partWatch:Disconnect()
+ partWatch = nil
+end
+
+c.CameraSubject = nil
+message.Text = "Requesting character..."
+
+wait(1)
+
+local rep = game:GetService("ReplicatedStorage")
+local requestCharacter = rep:WaitForChild("RequestCharacter")
+
+requestCharacter:FireServer()
+message.Text = "Waiting for character..."
+
+while not player.Character do
+ player.CharacterAdded:Wait()
+ wait()
+end
+
+messageGui.Visible = false
+c.CameraType = "Custom"
\ No newline at end of file
diff --git a/Resources/SafeChat/RawTreeData.lua b/Resources/SafeChat/RawTreeData.lua
new file mode 100644
index 0000000..df39095
--- /dev/null
+++ b/Resources/SafeChat/RawTreeData.lua
@@ -0,0 +1,447 @@
+return [==[
+Hello
+ Hi
+ Hi there!
+ Hi everyone
+ Howdy
+ Howdy partner!
+ Greetings
+ Greetings everyone
+ Greetings robloxians!
+ Seasons greetings!
+ Welcome
+ Welcome to my place
+ Welcome to our base
+ Welcome to my barbecque
+ Hey there!
+ What's up?
+ How are you doing?
+ How's it going?
+ What's new?
+ Good day
+ Good morning
+ Good afternoon
+ Good evening
+ Good night
+ Silly
+ Waaaaaaaz up?!
+ Hullo!
+ Behold greatness, mortals!
+ Holidays
+ Happy New Year!
+ Happy Valentine's Day!
+ Beware the Ides of March!
+ Happy Easter!
+ Happy 4th of July!
+ Happy Thanksgiving!
+ Happy Halloween!
+ Happy Hanukkah!
+ Merry Christmas!
+ Happy Holidays!
+Goodbye
+ Good Night
+ Sweet dreams
+ Go to sleep!
+ Lights out!
+ Bedtime
+ Later
+ See ya later
+ Later gator!
+ See you tomorrow
+ Bye
+ Hasta la bye bye!
+ I'll be right back
+ I have to go
+ Farewell
+ Take care
+ Have a nice day
+ Goodluck!
+ Ta-ta for now!
+ Peace
+ Peace out!
+ Peace dudes!
+ Rest in pieces!
+ Silly
+ To the batcave!
+ Over and out!
+ Happy trails!
+ I've got to book it!
+ Tootles!
+ Smell you later!
+ GG!
+ My house is on fire! gtg.
+Friend
+ Wanna be friends?
+ Follow me
+ Come to my place!
+ Come to my base!
+ Follow me, team!
+ Your place is cool
+ Your place is fun
+ Your place is awesome
+ Your place looks good
+ Thank you
+ Thanks for playing
+ Thanks for visiting
+ Thanks for everything
+ No thank you
+ No problem
+ Don't worry
+ That's ok
+ You are ...
+ You are great!
+ You are good!
+ You are cool!
+ You are funny!
+ You are silly!
+ You are awesome!
+ I like ...
+ I like your name
+ I like your shirt
+ I like your place
+ I like your style
+ Sorry
+ My bad!
+ I'm sorry
+ Whoops!
+ Please forgive me
+ I forgive you
+Questions
+ Who?
+ Who wants to be my friend?
+ Who wants to be on my team?
+ Who made this brilliant game?
+ What?
+ What is your favorite animal?
+ What is your favorite game?
+ What is your favorite movie?
+ What is your favorite TV show?
+ What is your favorite music?
+ What are your hobbies?
+ When?
+ When are you online?
+ When is the new version coming out?
+ When can we play again?
+ When will your place be done?
+ Where?
+ Where do you want to go?
+ Where are you going?
+ Where am I?!
+ How?
+ How are you today?
+ How did you make this cool place?
+ Can I...
+ Can I have a tour?
+ Can I be on your team?
+ Can I be your friend?
+ Can I try something?
+Answers
+ You need help?
+ Check out the news section
+ Check out the help section
+ Read the wiki!
+ All the answers are in the wiki!
+ Some people ...
+ Me
+ Not me
+ You
+ All of us
+ Everyone but you
+ Builderman!
+ Telamon!
+ Time ...
+ In the morning
+ In the afternoon
+ At night
+ Tomorrow
+ This week
+ This month
+ Sometime
+ Sometimes
+ Whenever you want
+ Never
+ Animals
+ Cats
+ Lion
+ Tiger
+ Leopard
+ Cheetah
+ Dogs
+ Wolves
+ Beagle
+ Collie
+ Dalmatian
+ Poodle
+ Spaniel
+ Shepherd
+ Terrier
+ Retriever
+ Horses
+ Ponies
+ Stallions
+ Reptiles
+ Dinosaurs
+ Lizards
+ Snakes
+ Turtles!
+ Hamster
+ Monkey
+ Bears
+ Fish
+ Goldfish
+ Sharks
+ Sea Bass
+ Halibut
+ Birds
+ Eagles
+ Penguins
+ Turkeys
+ Elephants
+ Mythical Beasts
+ Dragons
+ Unicorns
+ Sea Serpents
+ Sphinx
+ Cyclops
+ Minotaurs
+ Goblins
+ Honest Politicians
+ Ghosts
+ Scylla and Charybdis
+ Games
+ Roblox
+ BrickBattle
+ Community Building
+ Roblox Minigames
+ Action
+ Puzzle
+ Strategy
+ Racing
+ RPG
+ Board games
+ Chess
+ Checkers
+ Settlers of Catan
+ Tigris and Euphrates
+ El Grande
+ Stratego
+ Carcassonne
+ Sports
+ Hockey
+ Soccer
+ Football
+ Baseball
+ Basketball
+ Volleyball
+ Tennis
+ Watersports
+ Surfing
+ Swimming
+ Water Polo
+ Winter sports
+ Skiing
+ Snowboarding
+ Sledding
+ Skating
+ Adventure
+ Rock climbing
+ Hiking
+ Fishing
+ Wacky
+ Foosball
+ Calvinball
+ Croquet
+ Cricket
+ Dodgeball
+ Squash
+ Movies/TV
+ Science Fiction
+ Animated
+ Anime
+ Comedy
+ Romantic
+ Action
+ Fantasy
+ Music
+ Country
+ Jazz
+ Rap
+ Hip-hop
+ Techno
+ Classical
+ Pop
+ Rock
+ Hobbies
+ Computers
+ Building computers
+ Videogames
+ Coding
+ Hacking
+ The Internet
+ lol. teh internets!
+ Dance
+ Gynastics
+ Martial Arts
+ Karate
+ Judo
+ Taikwon Do
+ Wushu
+ Street fighting
+ Listening to music
+ Music lessons
+ Playing in my band
+ Playing piano
+ Playing guitar
+ Playing violin
+ Playing drums
+ Playing a weird instrument
+ Arts and crafts
+ Location
+ USA
+ West
+ Alaska
+ Arizona
+ California
+ Colorado
+ Hawaii
+ Idaho
+ Montana
+ Nevada
+ New Mexico
+ Oregon
+ Utah
+ Washington
+ Wyoming
+ Midwest
+ Illinois
+ Indiana
+ Iowa
+ Kansas
+ Michigan
+ Minnesota
+ Missouri
+ Nebraska
+ North Dakota
+ Ohio
+ South Dakota
+ Wisconsin
+ Northeast
+ Connecticut
+ Delaware
+ Maine
+ Maryland
+ Massachusetts
+ New Hampshire
+ New Jersey
+ New York
+ Pennsylvania
+ Rhode Island
+ Vermont
+ Virginia
+ West Virginia
+ South
+ Alabama
+ Arkansas
+ Florida
+ Georgia
+ Kentucky
+ Louisiana
+ Mississippi
+ North Carolina
+ Oklahoma
+ South Carolina
+ Tennessee
+ Texas
+ Canada
+ Alberta
+ British Columbia
+ Manitoba
+ New Brunswick
+ Newfoundland
+ Northwest Territories
+ Nova Scotia
+ Nunavut
+ Ontario
+ Prince Edward Island
+ Quebec
+ Saskatchewan
+ Yukon
+ Mexico
+ Central America
+ Europe
+ Great Britain
+ England
+ Scotland
+ Wales
+ Northern Ireland
+ France
+ Germany
+ Spain
+ Italy
+ Poland
+ Switzerland
+ Greece
+ Romania
+ Asia
+ China
+ India
+ Japan
+ Korea
+ South America
+ Argentina
+ Brazil
+ Africa
+ Eygpt
+ Swaziland
+ Australia
+ Middle East
+ Antarctica
+ Age
+ Rugrat
+ Kid
+ Teen
+ Twenties
+ Old
+ Ancient
+ Mesozoic
+ Mood
+ Good
+ Great!
+ Not bad
+ Sad
+ Hyper
+ Chill
+ Boy
+ Girl
+Game
+ Let's build
+ Let's battle
+ Nice one!
+ So far so good!
+ Lucky shot!
+ Oh man!
+Silly
+ Muahahahaha!
+ 1337
+ i r teh pwnz0r!
+ w00t!
+ z0mg h4x!
+ ub3rR0xXorzage!
+ all your base are belong to me!
+Yes
+ Absolutely!
+ Rock on!
+ Totally!
+ Juice!
+No
+ Ummm. No.
+ ...
+Ok
+ Well... ok
+ Sure
+:-)
+ :-(
+ :D
+ :-O
+ lol
+]==]
\ No newline at end of file
diff --git a/Resources/SafeChat/init.lua b/Resources/SafeChat/init.lua
new file mode 100644
index 0000000..2c7a46a
--- /dev/null
+++ b/Resources/SafeChat/init.lua
@@ -0,0 +1,35 @@
+local safeChatTree =
+{
+ Label = "ROOT";
+ Branches = {};
+}
+
+do
+ local mTreeData = script:WaitForChild("RawTreeData")
+ local treeData = require(mTreeData)
+
+ local stack = {}
+ stack[0] = safeChatTree
+
+ for line in treeData:gmatch("[^\n]+") do
+ if #line > 0 then
+ local stackIndex = 0
+ while line:sub(1,1) == "\t" do
+ stackIndex = stackIndex + 1
+ line = line:sub(2)
+ end
+
+ local tree = stack[stackIndex]
+ assert(tree,"Bad safechat tree setup at depth " .. stackIndex .. ": " .. line)
+
+ local branch = {}
+ branch.Label = line
+ branch.Branches = {}
+ table.insert(tree.Branches,branch)
+
+ stack[stackIndex+1] = branch
+ end
+ end
+end
+
+return safeChatTree
\ No newline at end of file
diff --git a/Server/Badges.server.lua b/Server/Badges.server.lua
new file mode 100644
index 0000000..d865dde
--- /dev/null
+++ b/Server/Badges.server.lua
@@ -0,0 +1,224 @@
+local ServerStorage = game:GetService("ServerStorage")
+local BadgeService = game:GetService("BadgeService")
+local AssetService = game:GetService("AssetService")
+local Players = game:GetService("Players")
+
+local usingLeaderboard = true
+
+if ServerStorage:FindFirstChild("TrackCombatBadges") then
+ usingLeaderboard = ServerStorage.TrackCombatBadges.Value
+elseif ServerStorage:FindFirstChild("LoadLeaderboard") then
+ usingLeaderboard = ServerStorage.LoadLeaderboard.Value
+end
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local playerDataGet = require(ServerStorage:WaitForChild("PlayerDataStore"))
+
+if not playerDataGet.Success then
+ warn("Failed to load PlayerData, badges will not be awarded")
+ return
+end
+
+local playerData = playerDataGet.DataStore
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local placeCount = 0
+
+local function iterPageItems(pages)
+ return coroutine.wrap(function ()
+ local pageNum = 1
+ while true do
+ for _, item in ipairs(pages:GetCurrentPage()) do
+ coroutine.yield(item, pageNum)
+ end
+
+ if pages.IsFinished then
+ break
+ end
+
+ pages:AdvanceToNextPageAsync()
+ pageNum = pageNum + 1
+ end
+ end)
+end
+
+for place in iterPageItems(AssetService:GetGamePlacesAsync()) do
+ if not place.Name:lower():find("devtest") and not place.Name:find("Super Nostalgia Zone") then
+ placeCount = placeCount + 1
+ end
+end
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local badges =
+{
+ CombatInitiation = 1020931358;
+ Warrior = 1020932933;
+ Bloxxer = 1021012898;
+ Inviter = 1021010468;
+ Friendship = 1021024465;
+ Ambassador = 1021056315;
+}
+
+local inviterBadgeStatus = {}
+local lastWipeout = {}
+
+local function giveBadge(player,badge)
+ warn("AWARDING BADGE", badge, "TO", player)
+ if not BadgeService:UserHasBadge(player.UserId,badge) then
+ BadgeService:AwardBadge(player.UserId,badge)
+ end
+end
+
+local function onHumanoidDied(humanoid, victim)
+ local player do
+ local char = humanoid.Parent
+ if char then
+ player = Players:GetPlayerFromCharacter(char)
+ end
+ end
+
+ local myLastWipeout = lastWipeout[victim.Name] or 0
+ local now = tick()
+
+ if (now - myLastWipeout) > 5 then
+ local creator = humanoid:FindFirstChild("creator")
+
+ if creator then
+ local killer = creator.Value
+
+ if killer and killer.UserId > 0 and killer ~= player then
+ local killerData = playerData:GetSaveData(killer)
+ local knockOuts = killerData:Get("Knockouts") or 0
+
+ knockOuts = knockOuts + 1
+ killerData:Set("Knockouts",knockOuts)
+
+ if knockOuts > 250 then
+ local wipeOuts = killerData:Get("Wipeouts") or 0
+ if wipeOuts < knockOuts then
+ giveBadge(killer,badges.Bloxxer)
+ end
+ elseif knockOuts > 100 then
+ giveBadge(killer,badges.Warrior)
+ elseif knockOuts > 10 then
+ giveBadge(killer,badges.CombatInitiation)
+ end
+ end
+ end
+
+ local myData = playerData:GetSaveData(victim)
+ local wipeOuts = myData:Get("Wipeouts") or 0
+
+ myData:Set("Wipeouts", wipeOuts + 1)
+ lastWipeout[victim.Name] = now
+ end
+end
+
+local function onCharacterAdded(char)
+ local player = game.Players:GetPlayerFromCharacter(char)
+ local humanoid = char:WaitForChild("Humanoid")
+
+ humanoid.Died:Connect(function ()
+ onHumanoidDied(humanoid,player)
+ end)
+end
+
+local function handleSocialBadges(player)
+ -- Set up our inviter status from scratch.
+ inviterBadgeStatus[player.Name] =
+ {
+ Counted = 0;
+ Queried = {};
+ }
+
+ -- Check the status of other players, and see if we can give them the inviter badge.
+ local myData = playerData:GetSaveData(player)
+
+ for _,otherPlayer in pairs(Players:GetPlayers()) do
+ if player ~= otherPlayer and player:IsFriendsWith(otherPlayer.UserId) then
+ local theirName = otherPlayer.Name
+ local theirStatus = inviterBadgeStatus[theirName]
+
+ if theirStatus and not theirStatus.Queried[player.Name] then
+ theirStatus.Queried[player.Name] = true
+ theirStatus.Counted = theirStatus.Counted + 1
+ if theirStatus.Counted >= 3 then
+ giveBadge(otherPlayer,badges.Inviter)
+ end
+ end
+
+ -- Also increment the friendship encounters for these two.
+
+ local myFrEncs = myData:Get("FriendEncounters") or 0
+ myFrEncs = myFrEncs + 1
+
+ myData:Set("FriendEncounters",myFrEncs)
+
+ if myFrEncs >= 10 then
+ giveBadge(player,badges.Friendship)
+ end
+
+ local theirData = playerData:GetSaveData(otherPlayer)
+ local theirFrEncs = theirData:Get("FriendEncounters") or 0
+
+ theirFrEncs = theirFrEncs + 1
+ theirData:Set("FriendEncounters",theirFrEncs)
+
+ if theirFrEncs >= 10 then
+ giveBadge(otherPlayer,badges.Friendship)
+ end
+ end
+ end
+end
+
+local function onPlayerAdded(player)
+ if player.UserId > 0 then
+ -- Hook up combat badge listeners
+ if usingLeaderboard then
+ if player.Character and player.Character:IsDescendantOf(workspace) then
+ onCharacterAdded(player.Character)
+ end
+
+ player.CharacterAdded:Connect(onCharacterAdded)
+ end
+
+ -- Handle social badges
+ handleSocialBadges(player)
+
+ -- Handle ambassador badge
+ local myData = playerData:GetSaveData(player)
+ local myPlaceVisits = myData:Get("PlacesVisited")
+
+ if myPlaceVisits == nil then
+ myPlaceVisits =
+ {
+ Count = 0;
+ Record = {};
+ }
+ end
+
+ local placeId = tostring(game.PlaceId)
+
+ if not myPlaceVisits.Record[placeId] then
+ myPlaceVisits.Record[placeId] = true
+ myPlaceVisits.Count = myPlaceVisits.Count + 1
+ end
+
+ if myPlaceVisits.Count >= placeCount then
+ giveBadge(player, badges.Ambassador)
+ end
+
+ myData:Set("PlacesVisited", myPlaceVisits)
+ end
+end
+
+for _,v in pairs(Players:GetPlayers()) do
+ onPlayerAdded(v)
+end
+
+Players.PlayerAdded:Connect(onPlayerAdded)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Server/Bevels.server.lua b/Server/Bevels.server.lua
new file mode 100644
index 0000000..485c5be
--- /dev/null
+++ b/Server/Bevels.server.lua
@@ -0,0 +1,537 @@
+------------------------------------------------------------------------------------------------
+-- Initialization
+------------------------------------------------------------------------------------------------
+
+local CollectionService = game:GetService("CollectionService")
+local Debris = game:GetService("Debris")
+local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+local ServerStorage = game:GetService("ServerStorage")
+
+local function getFlag(name)
+ local flag = ServerStorage:FindFirstChild(name)
+ return (flag and flag:IsA("BoolValue") and flag.Value)
+end
+
+local enableBevels = getFlag("EnableBevels")
+local debugMode = getFlag("DevTestMode")
+
+local bevelCache = ServerStorage:FindFirstChild("BevelCache")
+local bevelsReady = bevelCache:FindFirstChild("BevelsReady")
+
+if not bevelCache then
+ bevelCache = Instance.new("Folder")
+ bevelCache.Name = "BevelCache"
+ bevelCache.Parent = ServerStorage
+end
+
+if not bevelsReady then
+ bevelsReady = Instance.new("BoolValue")
+ bevelsReady.Name = "BevelsReady"
+ bevelsReady.Parent = bevelCache
+ bevelsReady.Archivable = false
+end
+
+if not enableBevels then
+ bevelsReady.Value = true
+ return
+end
+
+do
+ local coreBevelCache = ServerStorage:WaitForChild("CoreBevelCache")
+
+ for _,bevel in pairs(coreBevelCache:GetChildren()) do
+ if not bevelCache:FindFirstChild(bevel.Name) then
+ bevel.Parent = bevelCache
+ bevel.Archivable = false
+ end
+ end
+
+ coreBevelCache:Destroy()
+end
+
+local regen = ServerStorage:FindFirstChild("Regeneration")
+
+if regen then
+ local ready = regen:WaitForChild("Ready")
+
+ while not ready.Value do
+ ready.Changed:Wait()
+ end
+end
+
+local loadBuildTools = ServerStorage:FindFirstChild("LoadBuildTools")
+local hasBuildTools = (loadBuildTools ~= nil)
+
+------------------------------------------------------------------------------------------------
+
+local edgeDepth = 1 / 15
+local cornerDepth = edgeDepth * math.sqrt(8 / 3)
+
+local mirrorProps =
+{
+ "Anchored",
+ "CanCollide",
+ "CastShadow",
+ "CFrame",
+ "CollisionGroupId",
+ "CustomPhysicalProperties",
+ "Color",
+ "Locked",
+ "Material",
+ "Name",
+ "Reflectance",
+ "RotVelocity",
+ "Transparency",
+ "Velocity",
+}
+
+local surfaceProps =
+{
+ "ParamA",
+ "ParamB",
+ "Surface",
+ "SurfaceInput"
+}
+
+local bevelHash = "%.2f ~ %.2f ~ %.2f"
+local isStudio = RunService:IsStudio()
+
+local negateBase = Instance.new("Part")
+negateBase.Name = "__negateplane"
+negateBase.CanCollide = false
+negateBase.BottomSurface = 0
+negateBase.Transparency = 1
+negateBase.Anchored = true
+negateBase.TopSurface = 0
+negateBase.Locked = true
+
+CollectionService:AddTag(negateBase, "NoBevels")
+
+for _,normalId in pairs(Enum.NormalId:GetEnumItems()) do
+ local name = normalId.Name
+ for _,surfaceProp in pairs(surfaceProps) do
+ table.insert(mirrorProps, name .. surfaceProp)
+ end
+end
+
+------------------------------------------------------------------------------------------------
+
+local overload = 0
+local threshold = Vector3.new(30, 30, 30)
+
+if ServerStorage:FindFirstChild("BevelThreshold") then
+ threshold = ServerStorage.BevelThreshold.Value
+end
+
+local function debugPrint(...)
+ if debugMode then
+ warn("[BEVELS DEBUG]:", ...)
+ end
+end
+
+local function isPartOfHumanoid(object)
+ local model = object:FindFirstAncestorOfClass("Model")
+
+ if model then
+ if model:FindFirstChildOfClass("Humanoid") then
+ return true
+ else
+ return isPartOfHumanoid(model)
+ end
+ end
+
+ return false
+end
+
+local function canGiveBevels(part)
+ if part.Parent and part:IsA("Part") and not CollectionService:HasTag(part, "NoBevels") then
+ if not isPartOfHumanoid(part) and not part:FindFirstChildWhichIsA("DataModelMesh") then
+ local inThreshold = false
+ local diff = threshold - part.Size
+
+ if diff.X >= 0 and diff.Y >= 0 and diff.Z >= 0 then
+ inThreshold = true
+ end
+
+ if inThreshold then
+ if CollectionService:HasTag(part, "ForceBevels") then
+ return true
+ else
+ return part.Shape.Name == "Block" and part.Transparency < 1
+ end
+ end
+ end
+ end
+
+ return false
+end
+
+local function createProxyPart(part, name, tag, sizeChange)
+ local proxyPart = Instance.new("Part")
+ proxyPart.Name = name
+ proxyPart.Locked = true
+ proxyPart.TopSurface = 0
+ proxyPart.Massless = true
+ proxyPart.Transparency = 1
+ proxyPart.BottomSurface = 0
+ proxyPart.CanCollide = false
+ proxyPart.CFrame = part.CFrame
+
+ local size = part.Size
+ if sizeChange then
+ size = size + sizeChange
+ end
+
+ local proxyWeld = Instance.new("Weld")
+ proxyWeld.Name = "ProxyWeld"
+ proxyWeld.Part1 = proxyPart
+ proxyWeld.Part0 = part
+
+ if hasBuildTools then
+ local mesh = Instance.new("SpecialMesh")
+ mesh.Scale = size * 20
+ mesh.MeshType = "Brick"
+ mesh.Offset = part.Size
+ mesh.Parent = proxyPart
+
+ proxyPart.Size = Vector3.new(.05, .05, .05)
+ proxyWeld.C0 = CFrame.new(-mesh.Offset)
+ else
+ proxyPart.Size = part.Size
+ end
+
+ CollectionService:AddTag(proxyPart, tag)
+ CollectionService:AddTag(proxyPart, "NoBevels")
+ CollectionService:AddTag(proxyWeld, "GorillaGlue")
+
+ proxyWeld.Parent = proxyPart
+ proxyPart.Parent = part
+
+ return proxyPart
+end
+
+local function createBevels(part, initializing)
+ if not canGiveBevels(part) or isPartOfHumanoid(part) then
+ return
+ end
+
+ local size = part.Size
+ local sx, sy, sz = size.X, size.Y, size.Z
+ local bevelKey = bevelHash:format(sx, sy, sz)
+
+ local debugBox
+
+ if debugMode then
+ debugBox = Instance.new("BoxHandleAdornment")
+
+ debugBox.Color3 = Color3.new(0, 2, 2)
+ debugBox.AlwaysOnTop = true
+ debugBox.Name = "DebugBox"
+ debugBox.Size = size
+ debugBox.ZIndex = 0
+
+ debugBox.Adornee = part
+ debugBox.Parent = part
+ end
+
+ if not bevelCache:FindFirstChild(bevelKey) then
+ local halfSize = size / 2
+
+ local planeScale = math.max(sx, sy, sz)
+ local planes = {}
+
+ local solverPart = part:Clone()
+ solverPart.CFrame = CFrame.new()
+ solverPart.BrickColor = BrickColor.new(-1)
+
+ debugPrint("Solving:", bevelKey)
+
+ for x = -1, 1 do
+ local x0 = (x == 0)
+
+ for y = -1, 1 do
+ local y0 = (y == 0)
+
+ for z = -1, 1 do
+ local z0 = (z == 0)
+
+ local isCenter = (x0 and y0 and z0)
+ local isFace = ((x0 and y0) or (y0 and z0) or (z0 and x0))
+
+ if not (isCenter or isFace) then
+ local isCorner = (not x0 and not y0 and not z0)
+ local depth = isCorner and cornerDepth or edgeDepth
+
+ local offset = Vector3.new(x, y, z)
+ local cornerPos = (halfSize * offset)
+
+ local plane = negateBase:Clone()
+ plane.CFrame = CFrame.new(cornerPos, cornerPos + offset)
+ plane.Size = Vector3.new(planeScale, planeScale, depth)
+ plane.Parent = part
+
+ table.insert(planes, plane)
+ end
+ end
+ end
+ end
+
+ local success, union = pcall(function ()
+ return solverPart:SubtractAsync(planes, "Box")
+ end)
+
+ if success then
+ union.Name = bevelKey
+ union.UsePartColor = true
+ union.Parent = bevelCache
+
+ CollectionService:AddTag(union, "HasBevels")
+
+ if debugBox then
+ debugBox.Color3 = Color3.new(0, 2, 0)
+ end
+ elseif debugBox then
+ debugBox.Color3 = Color3.new(2, 0, 0)
+ end
+
+ for _,plane in pairs(planes) do
+ plane:Destroy()
+ end
+
+ overload = 0
+ else
+ if debugBox then
+ debugBox.Color3 = Color3.new(2, 0, 2)
+ end
+
+ overload = overload + 1
+
+ if overload % 10 == 0 then
+ RunService.Heartbeat:Wait()
+ end
+ end
+
+ local baseUnion = bevelCache:FindFirstChild(bevelKey)
+
+ if baseUnion then
+ local archivable = baseUnion.Archivable
+ baseUnion.Archivable = true
+
+ local union = baseUnion:Clone()
+ baseUnion.Archivable = archivable
+
+ for _,prop in ipairs(mirrorProps) do
+ union[prop] = part[prop]
+ end
+
+ for _,joint in pairs(part:GetJoints()) do
+ if joint:IsA("JointInstance") or joint:IsA("WeldConstraint") then
+ if joint.Part0 == part then
+ joint.Part0 = union
+ elseif joint.Part1 == part then
+ joint.Part1 = union
+ end
+ end
+ end
+
+ for _,child in pairs(part:GetChildren()) do
+ if not child:IsA("TouchTransmitter") and not child:IsA("Texture") then
+ if child:IsA("BaseScript") then
+ child.Disabled = true
+ end
+
+ child.Parent = union
+
+ if child:IsA("BaseScript") then
+ child.Disabled = false
+ end
+ end
+ end
+
+ if not initializing then
+ wait()
+ end
+
+ if CollectionService:HasTag(part, "DoUnlock") then
+ union.Locked = false
+ end
+
+ if part.ClassName ~= "Part" then
+ local holder = Instance.new("Weld")
+ holder.Part0 = part
+ holder.Part1 = union
+ holder.Parent = part
+
+ union.Anchored = false
+ union.Massless = true
+ union.Parent = part
+
+ part.Transparency = 1
+ CollectionService:AddTag(holder, "GorillaGlue")
+ else
+ local parent = part.Parent
+ part:Destroy()
+
+ union.Parent = parent
+ end
+ elseif debugBox then
+ debugBox.Color3 = Color3.new(2, 0, 0)
+ end
+
+ if debugBox then
+ debugBox.Transparency = 0.5
+ Debris:AddItem(debugBox, 2)
+ end
+end
+
+------------------------------------------------------------------------------------------------
+
+do
+ local waitForPlayer = getFlag("BevelsWaitForPlayer")
+
+ if waitForPlayer then
+ -- Wait for a player to spawn
+ local playerSpawned = false
+
+ while not playerSpawned do
+ for _,player in pairs(Players:GetPlayers()) do
+ if player.Character and player.Character:IsDescendantOf(workspace) then
+ playerSpawned = true
+ break
+ end
+ end
+
+ workspace.ChildAdded:Wait()
+ end
+ end
+
+ warn("Solving bevels...")
+
+ -- Collect all blocks currently in the workspace.
+ local initialPass = {}
+ local debugHint
+
+ for _,desc in pairs(workspace:GetDescendants()) do
+ if canGiveBevels(desc) then
+ if not desc.Locked then
+ CollectionService:AddTag(desc, "DoUnlock")
+ desc.Locked = true
+ end
+
+ table.insert(initialPass, desc)
+ end
+ end
+
+ if waitForPlayer then
+ -- Sort the blocks by the sum of their distances from players in the game.
+ local samplePoints = {}
+
+ for _,player in pairs(Players:GetPlayers()) do
+ local char = player.Character
+ if char then
+ local root = char.PrimaryPart
+ if root then
+ local rootPos = root.Position
+ table.insert(samplePoints, rootPos)
+ end
+ end
+ end
+
+ table.sort(initialPass, function (a, b)
+ local distSumA = 0
+ local distSumB = 0
+
+ local posA = a.Position
+ local posB = b.Position
+
+ for _,rootPos in pairs(samplePoints) do
+ local distA = (rootPos - posA).Magnitude
+ distSumA = distSumA + distA
+
+ local distB = (rootPos - posB).Magnitude
+ distSumB = distSumB + distB
+ end
+
+ if distSumA ~= distSumB then
+ return distSumA < distSumB
+ end
+
+ if posA.Y ~= posB.Y then
+ return posA.Y < posB.Y
+ end
+
+ if posA.X ~= posB.X then
+ return posA.X < posB.X
+ end
+
+ if posA.Z ~= posB.Z then
+ return posA.Z < posB.Z
+ end
+
+ return 0
+ end)
+ end
+
+ if debugMode then
+ debugHint = Instance.new("Hint")
+ debugHint.Text = "Generating Bevels..."
+ debugHint.Parent = workspace
+ end
+
+ -- Run through the initial bevel creation phase.
+ for _,block in ipairs(initialPass) do
+ createBevels(block, true)
+ end
+
+ if debugHint then
+ debugHint:Destroy()
+ end
+end
+
+-- Listen for new parts being added.
+workspace.DescendantAdded:Connect(createBevels)
+
+-- Allow regeneration to request bevel solving
+local bevelSolver = bevelCache:FindFirstChild("RequestSolve")
+
+if not bevelSolver then
+ bevelSolver = Instance.new("BindableFunction")
+ bevelSolver.Name = "RequestSolve"
+ bevelSolver.Parent = bevelCache
+ bevelSolver.Archivable = false
+end
+
+function bevelSolver.OnInvoke(inst)
+ for _,desc in pairs(inst:GetDescendants()) do
+ if desc:IsA("Part") then
+ createBevels(desc)
+ end
+ end
+end
+
+if RunService:IsStudio() then
+ local exportBin = Instance.new("Folder")
+ exportBin.Name = "ExportBin"
+ exportBin.Parent = ServerStorage
+
+ for _,v in pairs(bevelCache:GetChildren()) do
+ if v:IsA("TriangleMeshPart") and v.Archivable then
+ v:Clone().Parent = exportBin
+ end
+ end
+
+ wait(.1)
+
+ for _,v in pairs(exportBin:GetChildren()) do
+ if v:FindFirstChild("LOD") then
+ v.LOD:Destroy()
+ end
+ end
+end
+
+-- Ready!
+warn("Bevels ready!")
+bevelsReady.Value = true
+
+------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Server/BuildTools/Dragger.client.lua b/Server/BuildTools/Dragger.client.lua
new file mode 100644
index 0000000..e57e6f9
--- /dev/null
+++ b/Server/BuildTools/Dragger.client.lua
@@ -0,0 +1,256 @@
+local CollectionService = game:GetService("CollectionService")
+local RunService = game:GetService("RunService")
+local UserInputService = game:GetService("UserInputService")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local SoundService = game:GetService("SoundService")
+local Dragger = Instance.new("Dragger")
+
+local tool = script.Parent
+local selection = Instance.new("SelectionBox")
+selection.Parent = tool
+selection.Transparency = 1
+
+local icon = Instance.new("StringValue")
+icon.Name = "IconOverride"
+icon.Parent = tool
+
+local mode = tool.Name
+local draggerService = ReplicatedStorage:WaitForChild("DraggerService")
+local gateway = draggerService:WaitForChild("DraggerGateway")
+local submitUpdate = draggerService:WaitForChild("SubmitUpdate")
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Connections
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local cons = {}
+
+local function addConnections(connections)
+ for event, func in pairs(connections) do
+ local con = event:Connect(func)
+ table.insert(cons, con)
+ end
+end
+
+local function clearCons()
+ while #cons > 0 do
+ local connection = table.remove(cons)
+ connection:Disconnect()
+ end
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Keys
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local keyLocks = {}
+
+local function onInputEnded(input)
+ if keyLocks[input.KeyCode.Name] then
+ keyLocks[input.KeyCode.Name] = nil
+ end
+end
+
+local function isKeyDown(key)
+ if UserInputService:IsKeyDown(key) and not keyLocks[key] then
+ keyLocks[key] = true
+ return true
+ end
+ return false
+end
+
+UserInputService.InputEnded:Connect(onInputEnded)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Tool Style
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local style =
+{
+ GameTool =
+ {
+ Icon = "rbxassetid://1048129653";
+ HoverColor = Color3.fromRGB(25,153,255);
+ Cursors =
+ {
+ Idle = "";
+ Hover = "rbxasset://textures/DragCursor.png";
+ Grab = "rbxasset://textures/GrabRotateCursor.png";
+ };
+ };
+ Clone =
+ {
+ Icon = "rbxasset://textures/Clone.png";
+ HoverColor = Color3.fromRGB(25,153,255);
+ Cursors =
+ {
+ Idle = "rbxasset://textures/CloneCursor.png";
+ Hover = "rbxassetid://1048136830";
+ Grab = "rbxasset://textures/GrabRotateCursor.png";
+ }
+ };
+ Delete =
+ {
+ Icon = "rbxasset://textures/Hammer.png";
+ HoverColor = Color3.new(1,0.5,0);
+ CanShowWithHover = true;
+ Cursors =
+ {
+ Idle = "rbxasset://textures/HammerCursor.png";
+ Hover = "rbxasset://textures/HammerOverCursor.png";
+ }
+ }
+}
+
+local function getIcon(iconType)
+ return style[mode].Cursors[iconType]
+end
+
+tool.TextureId = style[mode].Icon
+selection.Color3 = style[mode].HoverColor
+
+if style[mode].CanShowWithHover then
+ selection.Transparency = 0
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Dragger
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local mouse
+local currentKey
+local down = false
+local debounce = false
+
+local function onIdle()
+ if not down and mouse then
+ local mousePart = mouse.Target
+
+ if mousePart and not mousePart.Locked then
+ selection.Adornee = mousePart
+ icon.Value = getIcon("Hover")
+ return
+ end
+
+ selection.Adornee = nil
+ icon.Value = getIcon("Idle")
+ end
+end
+
+local function draggerRotate(axis)
+ if down then
+ Dragger:AxisRotate(axis)
+ end
+end
+
+local function startDraggerAction(mPart)
+ if mode == "Delete" then
+ gateway:InvokeServer("RequestDelete",mPart)
+ return
+ end
+
+ local pointOnMousePart = mPart.CFrame:ToObjectSpace(mouse.Hit).Position
+ local canDrag, dragKey, mousePart = gateway:InvokeServer("GetKey", mPart, mode == "Clone")
+
+ if canDrag then
+ selection.Adornee = mousePart
+ selection.Transparency = 0
+
+ down = true
+ currentKey = dragKey
+
+ icon.Value = getIcon("Grab")
+ Dragger:MouseDown(mousePart, pointOnMousePart, {mousePart})
+
+ local lastSubmit = 0
+
+ while down do
+ local now = tick()
+ local joints = {}
+
+ for _,joint in pairs(mousePart:GetJoints()) do
+ if CollectionService:HasTag(joint, "GorillaGlue") then
+ joints[joint] = joint.Parent
+ joint.Parent = nil
+ end
+ end
+
+ --local mousePart = selection.Adornee
+ if down then
+ Dragger:MouseMove(mouse.UnitRay)
+ end
+
+ if mousePart and currentKey then
+ if isKeyDown("R") then
+ draggerRotate("Z")
+ elseif isKeyDown("T") then
+ draggerRotate("X")
+ end
+
+ if now - lastSubmit > 0.03 then
+ submitUpdate:FireServer(currentKey, mousePart.CFrame)
+ lastSubmit = now
+ end
+ end
+
+ for joint, parent in pairs(joints) do
+ joint.Parent = parent
+ end
+
+ RunService.Heartbeat:Wait()
+ end
+
+ selection.Transparency = 1
+ gateway:InvokeServer("ClearKey", dragKey)
+
+ currentKey = nil
+ end
+end
+
+local function onButton1Down()
+ if not debounce then
+ debounce = true
+
+ local mousePart = selection.Adornee
+
+ if mousePart and not down then
+ startDraggerAction(mousePart)
+ end
+
+ debounce = false
+ end
+end
+
+local function onButton1Up()
+ if down then
+ down = false
+ Dragger:MouseUp()
+ end
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Tool
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function onEquipped(newMouse)
+ mouse = newMouse
+ addConnections
+ {
+ [mouse.Button1Down] = onButton1Down;
+ [mouse.Button1Up] = onButton1Up;
+ [mouse.Idle] = onIdle;
+ }
+end
+
+local function onUnequipped()
+ onButton1Up()
+ clearCons()
+
+ selection.Adornee = nil
+ mouse = nil
+end
+
+tool.Equipped:Connect(onEquipped)
+tool.Unequipped:Connect(onUnequipped)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Server/BuildTools/init.server.lua b/Server/BuildTools/init.server.lua
new file mode 100644
index 0000000..17fac5c
--- /dev/null
+++ b/Server/BuildTools/init.server.lua
@@ -0,0 +1,341 @@
+local CollectionService = game:GetService("CollectionService")
+local HttpService = game:GetService("HttpService")
+local Players = game:GetService("Players")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+
+local ServerStorage = game:GetService("ServerStorage")
+
+local loadBuildTools = ServerStorage:FindFirstChild("LoadBuildTools")
+if not (loadBuildTools and loadBuildTools.Value) then
+ return
+end
+
+local looseBranches = ServerStorage:FindFirstChild("LooseBranches")
+
+if looseBranches and looseBranches:IsA("BoolValue") then
+ looseBranches = looseBranches.Value
+else
+ looseBranches = false
+end
+
+local toolList = loadBuildTools.Value
+if toolList == true then -- If it's a BoolValue, load all of them.
+ toolList = "GameTool;Clone;Delete"
+end
+
+local DraggerService = Instance.new("Folder")
+DraggerService.Name = "DraggerService"
+DraggerService.Parent = ReplicatedStorage
+
+local draggerGateway = Instance.new("RemoteFunction")
+draggerGateway.Name = "DraggerGateway"
+draggerGateway.Parent = DraggerService
+
+local submitUpdate = Instance.new("RemoteEvent")
+submitUpdate.Name = "SubmitUpdate"
+submitUpdate.Parent = DraggerService
+
+local draggerScript = script:WaitForChild("Dragger")
+
+local activeKeys = {}
+local playerToKey = {}
+local partToKey = {}
+local debounce = {}
+
+local SIMULATE_TAG = "SimulateAfterDrag"
+local NO_BREAK_TAG = "GorillaGlue"
+
+local function assertClass(obj, class)
+ assert(obj)
+ assert(typeof(obj) == "Instance")
+ assert(obj:IsA(class))
+end
+
+local function canGiveKey(player, part)
+ if part.Locked then
+ return false
+ end
+ local playerHasKey = playerToKey[player]
+ if playerHasKey then
+ return false
+ end
+ local partHasKey = partToKey[part]
+ if partHasKey then
+ return false
+ end
+ return true
+end
+
+local function claimAssembly(player, part)
+ if part:CanSetNetworkOwnership() then
+ part:SetNetworkOwner(player)
+ end
+end
+
+local function validJointsOf(part)
+ return coroutine.wrap(function ()
+ for _,joint in pairs(part:GetJoints()) do
+ if not CollectionService:HasTag(joint, NO_BREAK_TAG) then
+ coroutine.yield(joint)
+ end
+ end
+ end)
+end
+
+local function breakJoints(part)
+ for joint in validJointsOf(part) do
+ if not CollectionService:HasTag(joint, NO_BREAK_TAG) then
+ joint:Destroy()
+ end
+ end
+end
+
+local function makeJoints(part)
+ -- Connect this part to a nearby surface
+ workspace:JoinToOutsiders({part}, "Surface")
+end
+
+local function removePartKey(key)
+ local data = activeKeys[key]
+ if data then
+ local player = data.Player
+ if player then
+ playerToKey[player] = nil
+ end
+
+ local part = data.Part
+
+ if part then
+ makeJoints(part)
+
+ if CollectionService:HasTag(part, SIMULATE_TAG) then
+ data.Anchored = false
+ CollectionService:RemoveTag(part, SIMULATE_TAG)
+ end
+
+ part.Anchored = data.Anchored
+ claimAssembly(player, part)
+
+ partToKey[part] = nil
+ end
+
+ activeKeys[key] = nil
+ end
+end
+
+local function restoreJointUpstream(part)
+ local collectedParts = {}
+
+ if part and CollectionService:HasTag(part, SIMULATE_TAG) then
+ CollectionService:RemoveTag(part, SIMULATE_TAG)
+ part.Anchored = false
+
+ makeJoints(part)
+
+ for joint in validJointsOf(part) do
+ local part0 = joint.Part0
+ local part1 = joint.Part1
+
+ if part0 and part ~= part0 then
+ collectedParts[part0] = true
+ restoreJointUpstream(part0)
+ end
+
+ if part1 and part ~= part1 then
+ collectedParts[part1] = true
+ restoreJointUpstream(part1)
+ end
+ end
+ end
+
+ return collectedParts
+end
+
+local function collapseJointUpstream(part)
+ if part and not (part.Locked or CollectionService:HasTag(part, SIMULATE_TAG)) then
+ CollectionService:AddTag(part, SIMULATE_TAG)
+ part.Anchored = true
+
+ for joint in validJointsOf(part) do
+ local part0 = joint.Part0
+ local part1 = joint.Part1
+
+ if part0 and part ~= part0 then
+ collapseJointUpstream(part0)
+ end
+
+ if part1 and part ~= part1 then
+ collapseJointUpstream(part1)
+ end
+ end
+
+ breakJoints(part)
+ end
+end
+
+function draggerGateway.OnServerInvoke(player, request, ...)
+ if request == "GetKey" then
+ local part, asClone = ...
+ assertClass(part, "BasePart")
+
+ if asClone then
+ local newPart = part:Clone()
+ newPart.Parent = workspace
+
+ breakJoints(newPart)
+ newPart.CFrame = CFrame.new(part.Position + Vector3.new(0, part.Size.Y, 0))
+
+ local copySound = Instance.new("Sound")
+ copySound.SoundId = "rbxasset://sounds/electronicpingshort.wav"
+ copySound.PlayOnRemove = true
+ copySound.Parent = newPart
+
+ wait()
+
+ part = newPart
+ copySound:Destroy()
+ end
+
+ if canGiveKey(player, part) then
+ local key = HttpService:GenerateGUID(false)
+ claimAssembly(player, part)
+
+ playerToKey[player] = key
+ partToKey[part] = key
+
+ local collected = restoreJointUpstream(part)
+
+ local anchored = part.Anchored
+ part.Anchored = true
+ breakJoints(part)
+
+ for otherPart in pairs(collected) do
+ if otherPart:IsGrounded() then
+ collapseJointUpstream(otherPart)
+ end
+ end
+
+ activeKeys[key] =
+ {
+ Player = player;
+ Part = part;
+ Anchored = anchored;
+ }
+
+ return true, key, part
+ else
+ return false
+ end
+ elseif request == "ClearKey" then
+ local key = ...
+
+ if not key then
+ key = playerToKey[player]
+ end
+
+ if key then
+ local data = activeKeys[key]
+ if data then
+ local owner = data.Player
+ if player == owner then
+ removePartKey(key)
+ end
+ end
+ end
+ elseif request == "RequestDelete" then
+ if not debounce[player] then
+ local part = ...
+ assertClass(part, "BasePart")
+
+ debounce[player] = true
+
+ if canGiveKey(player, part) then
+ local e = Instance.new("Explosion")
+ e.BlastPressure = 0
+ e.Position = part.Position
+ e.Parent = workspace
+
+ local s = Instance.new("Sound")
+ s.SoundId = "rbxasset://sounds/collide.wav"
+ s.Volume = 1
+ s.PlayOnRemove = true
+ s.Parent = part
+
+ local connectedParts = restoreJointUpstream(part)
+ part:Destroy()
+
+ for otherPart in pairs(connectedParts) do
+ if otherPart:IsGrounded() then
+ collapseJointUpstream(otherPart)
+ end
+ end
+ end
+
+ wait(.1)
+ debounce[player] = false
+ end
+ end
+end
+
+local function onChildAdded(child)
+ if child:IsA("Backpack") then
+ for draggerTool in toolList:gmatch("[^;]+") do
+ local tool = Instance.new("Tool")
+ tool.Name = draggerTool
+ tool.RequiresHandle = false
+
+ local newDragger = draggerScript:Clone()
+ newDragger.Parent = tool
+ newDragger.Disabled = false
+
+ tool.Parent = child
+ end
+ end
+end
+
+local function onPlayerAdded(player)
+ for _, v in pairs(player:GetChildren()) do
+ onChildAdded(v)
+ end
+
+ player.ChildAdded:Connect(onChildAdded)
+end
+
+local function onPlayerRemoved(player)
+ local key = playerToKey[player]
+ if key then
+ removePartKey(key)
+ end
+end
+
+local function onSubmitUpdate(player, key, cframe)
+ local keyData = activeKeys[key]
+ if keyData then
+ local owner = keyData.Player
+ if owner == player then
+ local part = keyData.Part
+ if part and part:IsDescendantOf(workspace) then
+ breakJoints(part)
+ part.CFrame = cframe
+ end
+ end
+ end
+end
+
+for _, player in pairs(game.Players:GetPlayers()) do
+ onPlayerAdded(player)
+end
+
+submitUpdate.OnServerEvent:Connect(onSubmitUpdate)
+Players.PlayerAdded:Connect(onPlayerAdded)
+Players.PlayerRemoving:Connect(onPlayerRemoved)
+
+-- Garbage Collection
+
+while wait(5) do
+ for part, key in pairs(partToKey) do
+ if not part:IsDescendantOf(workspace) then
+ removePartKey(key)
+ end
+ end
+end
\ No newline at end of file
diff --git a/Server/CaptureTheFlag.server.lua b/Server/CaptureTheFlag.server.lua
new file mode 100644
index 0000000..c4351a4
--- /dev/null
+++ b/Server/CaptureTheFlag.server.lua
@@ -0,0 +1,295 @@
+local Players = game:GetService("Players")
+local CollectionService = game:GetService("CollectionService")
+local Teams = game:GetService("Teams")
+
+local FlagInstance = "FlagInstance"
+local FlagStandInstance = "FlagStandInstance"
+
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Flags
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function restoreFlag(flag)
+ local owner = flag:FindFirstChild("FlagStand")
+ local flagStand = owner and owner.Part0
+
+ if owner and flagStand then
+ for _,joint in pairs(flag:GetJoints()) do
+ if joint.Name == "RightGrip" then
+ joint:Destroy()
+ end
+ end
+
+ if flag.Name == "Handle" then
+ local tool = flag.Parent
+ if tool:IsA("Tool") then
+ flag.Name = tool.Name
+ tool.Parent = nil
+ end
+ end
+
+ flag.CFrame = flagStand.CFrame
+ flag.Parent = flagStand.Parent
+
+ wait()
+
+ flag.Velocity = Vector3.new()
+ flag.RotVelocity = Vector3.new()
+
+ owner.Part1 = flag
+ flag.Anchored = false
+ end
+end
+
+local function mountFlagAsTool(flag, humanoid)
+ local owner = flag:FindFirstChild("FlagStand")
+ local teamColor = flag:FindFirstChild("TeamColor")
+
+ if not (owner and teamColor) or flag.Name == "Handle" then
+ return
+ end
+
+ local grip = CFrame.new(0.25, 0, 0) * CFrame.Angles(0, -math.pi / 2, 0)
+
+ local tool = Instance.new("Tool")
+ tool.Name = flag.Name
+ tool.Grip = grip
+
+ local deathCon
+
+ local function onDied()
+ local char = humanoid.Parent
+
+ if char and tool.Parent == char then
+ humanoid:UnequipTools()
+ end
+
+ if deathCon then
+ deathCon:Disconnect()
+ deathCon = nil
+ end
+ end
+
+ local function onUnequipped()
+ if deathCon then
+ deathCon:Disconnect()
+ deathCon = nil
+ end
+
+ if humanoid then
+ local rootPart = humanoid.RootPart
+
+ if rootPart then
+ local cf = rootPart.CFrame * CFrame.new(0, 4, -8)
+ flag.RotVelocity = Vector3.new(1, 1, 1)
+ flag.Position = cf.Position
+ end
+ end
+
+ if flag.Parent == tool then
+ flag.Parent = workspace
+ end
+
+ flag.Name = tool.Name
+
+ spawn(function ()
+ tool:Destroy()
+ end)
+ end
+
+ tool.Unequipped:Connect(onUnequipped)
+ CollectionService:AddTag(tool, "Flag")
+
+ tool.Parent = workspace
+ owner.Part1 = nil
+
+ flag.Name = "Handle"
+ flag.Parent = tool
+
+ humanoid:EquipTool(tool)
+ deathCon = humanoid.Died:Connect(onDied)
+end
+
+local function onFlagAdded(flag)
+ if not flag:IsA("BasePart") then
+ return
+ end
+
+ -- Mount TeamColor
+ local teamColor = flag:FindFirstChild("TeamColor")
+
+ if not teamColor then
+ teamColor = Instance.new("BrickColorValue")
+ teamColor.Value = flag.BrickColor
+ teamColor.Name = "TeamColor"
+ teamColor.Parent = flag
+ end
+
+ -- Mount FlagStand
+ local flagStand, owner
+
+ for _,part in pairs(flag:GetConnectedParts()) do
+ if CollectionService:HasTag(part, FlagStandInstance) then
+ flagStand = part
+ break
+ end
+ end
+
+ if flagStand then
+ owner = Instance.new("Weld")
+ owner.C0 = flagStand.CFrame:ToObjectSpace(flag.CFrame)
+ owner.Name = "FlagStand"
+ owner.Part0 = flagStand
+ owner.Parent = flag
+
+ for _,joint in pairs(flag:GetJoints()) do
+ if joint ~= owner then
+ joint:Destroy()
+ end
+ end
+
+ owner.Part1 = flag
+ CollectionService:AddTag(owner, "GorillaGlue")
+ end
+
+ spawn(function ()
+ -- Try to keep the flag from falling out of the world.
+ local deathPlane = workspace.FallenPartsDestroyHeight
+
+ while flag:IsDescendantOf(workspace) do
+ if flag.Position.Y < deathPlane + 200 then
+ local tool = flag.Parent
+
+ if tool:IsA("Tool") then
+ tool.Parent = workspace
+ wait()
+ end
+
+ restoreFlag(flag)
+ end
+
+ wait()
+ end
+ end)
+
+ local function onTouched(hit)
+ local char = hit.Parent
+ if char then
+ local player = Players:GetPlayerFromCharacter(char)
+ local humanoid = char:FindFirstChildOfClass("Humanoid")
+
+ if player and humanoid then
+ if player.Neutral then
+ return
+ end
+
+ if player.TeamColor == teamColor.Value then
+ if owner.Part1 ~= flag then
+ restoreFlag(flag)
+ end
+ else
+ mountFlagAsTool(flag, humanoid)
+ end
+ end
+ end
+ end
+
+ flag.Touched:Connect(onTouched)
+end
+
+for _,flag in pairs(CollectionService:GetTagged(FlagInstance)) do
+ onFlagAdded(flag)
+end
+
+local flagAdded = CollectionService:GetInstanceAddedSignal(FlagInstance)
+flagAdded:Connect(onFlagAdded)
+
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Flag Stands
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function onFlagStandAdded(flagStand)
+ if not flagStand:IsA("BasePart") then
+ return
+ end
+
+ local debounce = false
+ local teamColor = flagStand:FindFirstChild("TeamColor")
+ local flagCaptured = flagStand:FindFirstChild("FlagCaptured")
+
+ if not teamColor then
+ teamColor = Instance.new("BrickColorValue")
+ teamColor.Value = flagStand.BrickColor
+ teamColor.Name = "TeamColor"
+ teamColor.Parent = flagStand
+ end
+
+ if not flagCaptured then
+ flagCaptured = Instance.new("BindableEvent")
+ flagCaptured.Name = "FlagCaptured"
+ flagCaptured.Parent = flagStand
+ end
+
+ local function onTouched(hit)
+ if debounce then
+ return
+ end
+
+ local char = hit.Parent
+ if char then
+ local player = Players:GetPlayerFromCharacter(char)
+ if player then
+ if player.Neutral then
+ return
+ end
+
+ if player.TeamColor ~= teamColor.Value then
+ return
+ end
+
+ local tool = char:FindFirstChildOfClass("Tool")
+ local handle = tool and tool:FindFirstChild("Handle")
+
+ if handle and CollectionService:HasTag(handle, FlagInstance) then
+ debounce = true
+ print("flag captured!")
+
+ flagCaptured:Fire(player)
+ restoreFlag(handle)
+
+ tool:Destroy()
+
+ wait(1)
+ debounce = false
+ end
+ end
+ end
+ end
+
+ flagStand.Touched:Connect(onTouched)
+end
+
+local function onFlagStandRemoved(flagStand)
+ local teamColor = flagStand:FindFirstChild("TeamColor")
+ local flagCaptured = flagStand:FindFirstChild("FlagCaptured")
+
+ if teamColor then
+ teamColor:Destroy()
+ end
+
+ if flagCaptured then
+ flagCaptured:Destroy()
+ end
+end
+
+for _,flagStand in pairs(CollectionService:GetTagged(FlagStandInstance)) do
+ onFlagStandAdded(flagStand)
+end
+
+local flagStandAdded = CollectionService:GetInstanceAddedSignal(FlagStandInstance)
+flagStandAdded:Connect(onFlagStandAdded)
+
+local flagStandRemoved = CollectionService:GetInstanceRemovedSignal(FlagStandInstance)
+flagStandRemoved:Connect(onFlagStandRemoved)
+
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Server/Characters.server.lua b/Server/Characters.server.lua
new file mode 100644
index 0000000..5305e23
--- /dev/null
+++ b/Server/Characters.server.lua
@@ -0,0 +1,199 @@
+local CollectionService = game:GetService("CollectionService")
+local Players = game:GetService("Players")
+local InsertService = game:GetService("InsertService")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local ServerStorage = game:GetService("ServerStorage")
+
+local hats = ServerStorage:WaitForChild("ServerHatCache")
+local requestCharacter = ReplicatedStorage:WaitForChild("RequestCharacter")
+local assetUtil = require(ReplicatedStorage:WaitForChild("AssetUtil"))
+local itemData = ReplicatedStorage:WaitForChild("ItemData")
+local hatData = require(itemData:WaitForChild("Hat"))
+
+local playerDataGet = { Success = false }
+pcall(function ()
+ playerDataGet = require(ServerStorage:WaitForChild("PlayerDataStore"))
+end)
+
+if not playerDataGet.Success then
+ warn("Failed to get PlayerData. Avatars will not be loaded.")
+end
+
+local playerDataStore = playerDataGet.DataStore
+
+local limbs = {"Head", "Torso", "LeftArm", "RightArm", "LeftLeg", "RightLeg"}
+
+local function preBufferHat(hatId)
+ local hat = hats:FindFirstChild(hatId)
+
+ if not hat then
+ local success, import = assetUtil:SafeCall(InsertService, "LoadAsset", tonumber(hatId))
+
+ if success then
+ hat = import:FindFirstChildWhichIsA("Accoutrement")
+ if hat then
+ hat.Name = hatId
+ hat.Parent = hats
+ end
+ end
+ end
+
+ return hat
+end
+
+local function safeDestroy(obj)
+ spawn(function ()
+ obj:Destroy()
+ end)
+end
+
+local function onCharacterAdded(char)
+ local player = Players:GetPlayerFromCharacter(char)
+
+ local bodyColors = script.BodyColors:Clone()
+ CollectionService:AddTag(bodyColors, "RespectCharacterAsset")
+
+ local graphic = script.ShirtGraphic:Clone()
+
+ local shirt = char:FindFirstChildWhichIsA("Shirt")
+ if not shirt then
+ shirt = script.Shirt:Clone()
+ end
+
+ local pants = char:FindFirstChildWhichIsA("Pants")
+ if not pants then
+ pants = script.Pants:Clone()
+ end
+
+ local faceId = 1104210678
+ local tshirtId = 131792587
+
+ local humanoid = char:WaitForChild("Humanoid")
+ CollectionService:AddTag(humanoid, "Animator")
+
+ local function onDied()
+ if char:FindFirstChild("HumanoidRootPart") then
+ char.HumanoidRootPart:Destroy()
+ end
+
+ wait(5)
+
+ local player = game.Players:GetPlayerFromCharacter(char)
+ if player then
+ player:LoadCharacter()
+ end
+ end
+
+ local function onDescendantAdded(desc)
+ if desc:IsA("CharacterMesh") and not desc.Name:sub(1, 3) == "CL_" then
+ safeDestroy(desc)
+ elseif desc:IsA("Accoutrement") then
+ -- Safe way to deter non-game accessories, since I name them by their AssetId
+ if not tonumber(desc.Name) then
+ safeDestroy(desc)
+ end
+ elseif desc:IsA("SpecialMesh") and desc.Parent.Name == "Head" then
+ if desc.Name ~= "HeadMesh" then
+ wait()
+
+ local override = Instance.new("SpecialMesh")
+ override.Name = "HeadMesh"
+ override.Scale = Vector3.new(1.25, 1.25, 1.25)
+ override.Parent = desc.Parent
+
+ safeDestroy(desc)
+ end
+ elseif desc:IsA("BodyColors") and desc ~= bodyColors and not CollectionService:HasTag(bodyColors, "RespectCharacterAsset") then
+ safeDestroy(desc)
+ bodyColors.Parent = nil
+ wait()
+ bodyColors.Parent = char
+ end
+ end
+
+ for _,v in pairs(char:GetDescendants()) do
+ onDescendantAdded(v)
+ end
+
+ char.DescendantAdded:Connect(onDescendantAdded)
+ humanoid.Died:Connect(onDied)
+
+ if player.UserId > 0 and playerDataStore then
+ local playerData = playerDataStore:GetSaveData(player)
+ local colorData = playerData:Get("BodyColors")
+ if colorData then
+ for _,limb in pairs(limbs) do
+ local num = colorData[limb]
+ if num then
+ bodyColors[limb.."Color"] = BrickColor.new(num)
+ end
+ end
+ end
+
+ local loadout = playerData:Get("Loadout")
+ if loadout then
+ local shirtId = loadout.Shirt
+ if shirtId then
+ shirt.ShirtTemplate = "rbxassetid://" .. shirtId
+ end
+ local pantsId = loadout.Pants
+ if pantsId then
+ pants.PantsTemplate = "rbxassetid://" .. pantsId
+ end
+
+ faceId = loadout.Face or faceId
+ spawn(function ()
+ local hatId = loadout.Hat or 0
+ if hatId > 0 then
+ local hatSrc = preBufferHat(hatId)
+ local hat = hatSrc:Clone()
+ hat.Parent = char
+ end
+ end)
+ end
+
+ tshirtId = playerData:Get("TShirt") or tshirtId
+ end
+
+ if tshirtId > 0 then
+ local success,img = assetUtil:RequestImage(tshirtId)
+ if success and img then
+ graphic.Graphic = img
+ graphic.Parent = char
+ end
+ end
+
+ bodyColors.Parent = char
+ shirt.Parent = char
+ pants.Parent = char
+
+ local head = char:WaitForChild("Head")
+ local face = head:WaitForChild("face")
+ face.Texture = "rbxhttp://Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=420&ht=420&aid=" .. faceId
+end
+
+local function onRequestCharacter(player)
+ if not player.Character then
+ player:LoadCharacter()
+ end
+end
+
+local function onPlayerAdded(player)
+ player.CanLoadCharacterAppearance = false
+ player.CharacterAdded:connect(onCharacterAdded)
+
+ if player.Character then
+ onCharacterAdded(player.Character)
+ end
+
+ if game.JobId == "" then
+ player:LoadCharacter()
+ end
+end
+
+for _,v in pairs(Players:GetPlayers()) do
+ onPlayerAdded(v)
+end
+
+Players.PlayerAdded:connect(onPlayerAdded)
+requestCharacter.OnServerEvent:Connect(onRequestCharacter)
\ No newline at end of file
diff --git a/Server/Chat.server.lua b/Server/Chat.server.lua
new file mode 100644
index 0000000..da2f4c3
--- /dev/null
+++ b/Server/Chat.server.lua
@@ -0,0 +1,66 @@
+local Players = game:GetService("Players")
+local TextService = game:GetService("TextService")
+local Chat = game:GetService("Chat")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+
+local chatRemote = ReplicatedStorage:WaitForChild("ChatRemote")
+local mSafeChatTree = ReplicatedStorage:WaitForChild("SafeChatTree")
+local safeChatTree = require(mSafeChatTree)
+
+local filterCache = {}
+local maxChatLength = 128
+
+local function onServerEvent(player, message)
+ assert(typeof(message) == "string", "bad input passed")
+ assert(#message <= maxChatLength, "Chat message was too long!")
+
+ if message:sub(1,3) == "/sc" then
+ local tree = safeChatTree
+
+ for t in message:gmatch("%d+") do
+ local i = tonumber(t) + 1
+ tree = tree.Branches[i]
+
+ if not tree then
+ break
+ end
+ end
+
+ message = tree and tree.Label or " "
+ end
+
+ local asciiMessage = ""
+
+ for p, c in utf8.codes(message) do
+ if c > 0x1F600 then
+ asciiMessage = asciiMessage .. "??"
+ else
+ asciiMessage = asciiMessage .. utf8.char(c)
+ end
+ end
+
+ message = asciiMessage
+
+ local userId = player.UserId
+ if not filterCache[userId] then
+ filterCache[userId] = {}
+ end
+
+ local filterResult = filterCache[userId][message]
+
+ if not filterResult then
+ filterResult = TextService:FilterStringAsync(message,userId)
+ filterCache[userId][message] = filterResult
+ end
+
+ for _,receiver in pairs(Players:GetPlayers()) do
+ spawn(function ()
+ pcall(function ()
+ local filtered = filterResult:GetChatForUserAsync(receiver.UserId)
+ chatRemote:FireClient(receiver, player, filtered, filtered ~= message)
+ end)
+ end)
+ end
+end
+
+chatRemote.OnServerEvent:Connect(onServerEvent)
\ No newline at end of file
diff --git a/Server/Cylinders/CylinderSurface.rbxmx b/Server/Cylinders/CylinderSurface.rbxmx
new file mode 100644
index 0000000..3238910
--- /dev/null
+++ b/Server/Cylinders/CylinderSurface.rbxmx
@@ -0,0 +1,137 @@
+
+ -
+
+ true
+
[null]
+ false
+
+ true
+
+ 100
+ 100
+
+ false
+ true
+ 1
+ 0
+ CylinderSurface
+ 50
+ true
+ [null]
+ 0
+
+ 0
+ 0
+ 0
+ true
+
+ -
+
+ false
+
+ 0.5
+ 0.5
+
+
+ true
+
+ 0.3254902
+ 0.3254902
+ 0.3254902
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ Frame
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 20
+ 0.7
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+ -
+
+ false
+
+ 0.5
+ 0.5
+
+
+ true
+
+ 0.3254902
+ 0.3254902
+ 0.3254902
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ Frame
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.7
+ 0
+ 0
+ 20
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+
+
\ No newline at end of file
diff --git a/Server/Cylinders/init.server.lua b/Server/Cylinders/init.server.lua
new file mode 100644
index 0000000..5452f2c
--- /dev/null
+++ b/Server/Cylinders/init.server.lua
@@ -0,0 +1,76 @@
+local CollectionService = game:GetService("CollectionService")
+
+local cylinderSurface = script:WaitForChild("CylinderSurface")
+local cylinderListener = CollectionService:GetInstanceAddedSignal("Cylinder")
+
+local min, max = math.min, math.max
+
+local function makeCylinderSurface(part, sizeUpdated)
+ local surface = cylinderSurface:Clone()
+ surface.Parent = part
+ surface.Adornee = part
+ surface.Archivable = false
+
+ local lastSize = Vector3.new()
+
+ local function onSizeUpdated()
+ local size = part.Size
+ if size ~= lastSize then
+ local scale = min(size.Y, size.Z)
+ surface.CanvasSize = Vector2.new(max(100, scale * 100), max(100, scale * 100))
+ lastSize = size
+ end
+ end
+
+ onSizeUpdated()
+ sizeUpdated:Connect(onSizeUpdated)
+
+ return surface
+end
+
+local function bindCylinder(part)
+ if not part:IsA("Part") then
+ return
+ end
+
+ local sizeUpdated = part:GetPropertyChangedSignal("Size")
+ part.Shape = "Ball"
+ part:MakeJoints()
+
+ local mesh = Instance.new("SpecialMesh")
+ mesh.MeshId = "rbxassetid://1009010722"
+ mesh.Archivable = false
+ mesh.Parent = part
+
+ local leftSurface = makeCylinderSurface(part, sizeUpdated)
+ leftSurface.Face = "Left"
+
+ local rightSurface = makeCylinderSurface(part, sizeUpdated)
+ rightSurface.Face = "Right"
+
+ local function onSizeUpdated()
+ local size = part.Size
+ local scale = math.min(size.Y,size.Z)
+ mesh.Scale = Vector3.new(scale,scale,scale)
+ end
+
+ onSizeUpdated()
+ sizeUpdated:Connect(onSizeUpdated)
+end
+
+local function findCylinder(obj)
+ if obj:IsA("Part") and obj.Shape == Enum.PartType.Cylinder then
+ CollectionService:AddTag(obj, "Cylinder")
+ end
+end
+
+for _, obj in pairs(workspace:GetDescendants()) do
+ findCylinder(obj)
+end
+
+for _, cylinder in pairs(CollectionService:GetTagged("Cylinder")) do
+ bindCylinder(cylinder)
+end
+
+workspace.DescendantAdded:Connect(findCylinder)
+cylinderListener:Connect(bindCylinder)
diff --git a/Server/Explosions.server.lua b/Server/Explosions.server.lua
new file mode 100644
index 0000000..262d41d
--- /dev/null
+++ b/Server/Explosions.server.lua
@@ -0,0 +1,185 @@
+local CollectionService = game:GetService("CollectionService")
+local Debris = game:GetService("Debris")
+local ServerStorage = game:GetService("ServerStorage")
+
+local FORCE_GRANULARITY = 2
+local allowTeamDamage = false
+
+local teamDamage = ServerStorage:FindFirstChild("TeamDamage")
+if teamDamage then
+ allowTeamDamage = teamDamage.Value
+end
+
+local function processExplosion(explosion)
+ local BLAST_RADIUS = explosion.BlastRadius
+ local BLAST_PRESSURE = explosion.BlastPressure
+
+ if explosion:FindFirstChild("BLAST_PRESSURE") then
+ BLAST_PRESSURE = explosion.BLAST_PRESSURE.Value
+ end
+
+ if BLAST_PRESSURE > 0 then
+ local damagedPlayerSet = {}
+ local blastCenter = explosion.Position
+
+ local function onExplosionHit(p, dist)
+ if explosion:FindFirstChild("Owner") then
+ local player = explosion.Owner.Value
+ if player then
+ local char = player.Character
+ if char and p:IsDescendantOf(char) then
+ return
+ end
+ end
+ end
+
+ local isInCharacter = false
+
+ if p.Size.Magnitude / 2 < 20 then
+ --world->ticklePrimitive(p, true);
+
+ local doBreakjoints = true
+ local hitCharacter = p:FindFirstAncestorWhichIsA("Model")
+ local hitHumanoid = hitCharacter:FindFirstChild("Humanoid")
+
+ if hitCharacter and hitHumanoid then
+ -- flag as character
+ isInCharacter = true
+
+ -- don't breakjoints characters
+ doBreakjoints = false
+
+ -- work out what damage to do
+ local hitPlayer = game.Players:GetPlayerFromCharacter(hitCharacter)
+ local creatorTag = explosion:FindFirstChild("creator")
+ local myPlayer
+
+ if creatorTag then
+ myPlayer = creatorTag.Value
+ end
+
+ if hitPlayer and not damagedPlayerSet[hitPlayer] then
+ local doDamage = true
+
+ if not allowTeamDamage then
+ if myPlayer and hitPlayer ~= myPlayer then
+ if hitPlayer.Team and myPlayer.Team and hitPlayer.Team == myPlayer.Team then
+ doDamage = false
+ end
+ end
+ end
+
+ if doDamage then
+ -- flag as damaged
+ damagedPlayerSet[hitPlayer] = true
+
+ -- assume the torso is a massless frictionless unit ball in a perfect vaccum
+ dist = math.min(math.max(dist - 0.8, 0), 1)
+
+ -- damage to do
+ local frac = (dist / BLAST_RADIUS)
+
+ -- do damage. See how much damage to do
+ if myPlayer == hitPlayer then
+ hitHumanoid:TakeDamage((BLAST_RADIUS * 20) - (frac * 38))
+ hitHumanoid:ChangeState("Ragdoll")
+ else
+ hitHumanoid:TakeDamage(100)
+ end
+ end
+ end
+ end
+
+ -- breakjoints stuff
+ if doBreakjoints then
+ if not hitHumanoid and p:CanSetNetworkOwnership() then
+ p:SetNetworkOwner(nil)
+ end
+
+ for _,joint in pairs(p:GetJoints()) do
+ if not CollectionService:HasTag(joint, "GorillaGlue") then
+ joint:Destroy()
+ end
+ end
+ end
+
+ --Vector3 delta = (p->getCoordinateFrame().translation - position);
+ local delta = (p.Position - blastCenter)
+
+ --Vector3 normal =
+ -- (delta == Vector3::zero())
+ -- ? Vector3::unitY()
+ -- : delta.direction();
+ local normal = (delta == Vector3.new(0, 0, 0))
+ and Vector3.new(0, 1, 0)
+ or delta.unit
+
+ --float radius = p->getRadius();
+ local radius = p.Size.magnitude / 2
+
+ --float surfaceArea = radius * radius;
+ local surfaceArea = radius * radius
+
+ --Vector3 impulse = normal * blastPressure * surfaceArea * (1.0f / 4560.0f); // normalizing factor
+ local impulse = normal * BLAST_PRESSURE * surfaceArea * (1.0 / 4560.0)
+
+ -- How much force to apply (for characters, ramp it down towards the edge)
+ local frac;
+
+ if isInCharacter then
+ frac = 1 - math.max(0, math.min(1, (dist - 2) / BLAST_RADIUS))
+ else
+ frac = 1
+ end
+
+ --p->getBody()->accumulateLinearImpulse(impulse, p->getCoordinateFrame().translation);
+ local currentVelocity = p.Velocity
+ local deltaVelocity = impulse / p:GetMass() -- m * del-v = F * del-t = Impulse
+ local forceNeeded = workspace.Gravity * p:GetMass() -- F = ma
+
+ local bodyV = Instance.new('BodyVelocity')
+ bodyV.Velocity = currentVelocity + deltaVelocity
+ bodyV.MaxForce = Vector3.new(forceNeeded, forceNeeded, forceNeeded) * 10 * frac
+ bodyV.Parent = p
+
+ Debris:AddItem(bodyV, 0.2 / FORCE_GRANULARITY)
+
+ --p->getBody()->accumulateRotationalImpulse(impulse * 0.5 * radius); // a somewhat arbitrary, but nice torque
+ local rotImpulse = impulse * 0.5 * radius
+ local currentRotVelocity = p.RotVelocity
+
+ local momentOfInertia = (2 * p:GetMass() * radius * radius / 5) -- moment of inertia = 2/5*m*r^2 (assuming roughly spherical)
+ local deltaRotVelocity = rotImpulse / momentOfInertia
+ local torqueNeeded = 20 * momentOfInertia -- torque = r x F, want about alpha = 20 rad/s^2, alpha * P = torque
+
+ local rot = Instance.new('BodyAngularVelocity')
+ rot.MaxTorque = Vector3.new(torqueNeeded, torqueNeeded, torqueNeeded) * 10 * frac
+ rot.AngularVelocity = currentRotVelocity + deltaRotVelocity
+ rot.Parent = p
+
+ Debris:AddItem(rot, 0.2 / FORCE_GRANULARITY)
+ end
+ end
+
+ explosion.Hit:Connect(onExplosionHit)
+ end
+end
+
+local function onDescendantAdded(desc)
+ if desc:IsA("Explosion") then
+ local pressure = desc.BlastPressure
+
+ if pressure > 0 then
+ local blastPressure = Instance.new("NumberValue")
+ blastPressure.Name = "BLAST_PRESSURE"
+ blastPressure.Value = pressure
+ blastPressure.Parent = desc
+
+ desc.BlastPressure = 0
+ end
+
+ processExplosion(desc)
+ end
+end
+
+workspace.DescendantAdded:Connect(onDescendantAdded)
\ No newline at end of file
diff --git a/Server/ForceFields.server.lua b/Server/ForceFields.server.lua
new file mode 100644
index 0000000..94dde20
--- /dev/null
+++ b/Server/ForceFields.server.lua
@@ -0,0 +1,60 @@
+local ffAdorns = Instance.new("Folder")
+ffAdorns.Name = "_ForceFieldAdorns"
+ffAdorns.Parent = workspace
+
+local hide = false
+if game.ServerStorage:FindFirstChild("HideForceFields") then
+ hide = true
+end
+
+local ignoreNames =
+{
+ HumanoidRootPart = true;
+ DebugAdorn = true;
+ NoForceField = true;
+}
+
+local function onDescendantAdded(desc)
+ if desc:IsA("ForceField") then
+ desc.Visible = false
+ if hide then return end
+
+ local adorns = {}
+ local char = desc.Parent
+
+ local function registerAdorn(child)
+ if child:IsA("BasePart") and not ignoreNames[child.Name] then
+ local adorn = Instance.new("SelectionBox")
+ adorn.Transparency = 1
+ adorn.Adornee = child
+ adorn.Parent = ffAdorns
+ table.insert(adorns,adorn)
+ end
+ end
+
+ for _,part in pairs(char:GetDescendants()) do
+ registerAdorn(part)
+ end
+
+ local regSignal = char.DescendantAdded:Connect(registerAdorn)
+
+
+ while desc:IsDescendantOf(workspace) do
+ desc.AncestryChanged:Wait()
+ end
+
+ for _,adorn in pairs(adorns) do
+ adorn:Destroy()
+ end
+
+ adorns = nil
+ regSignal:Disconnect()
+
+ end
+end
+
+for _,v in pairs(workspace:GetDescendants()) do
+ onDescendantAdded(v)
+end
+
+workspace.DescendantAdded:Connect(onDescendantAdded)
\ No newline at end of file
diff --git a/Server/HatGranter.server.lua b/Server/HatGranter.server.lua
new file mode 100644
index 0000000..bb99c84
--- /dev/null
+++ b/Server/HatGranter.server.lua
@@ -0,0 +1,64 @@
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local itemData = ReplicatedStorage:WaitForChild("ItemData")
+local hatData = require(itemData:WaitForChild("Hat"))
+
+local ServerStorage = game:GetService("ServerStorage")
+local grantHatToUser = ServerStorage:WaitForChild("GrantHatToUser")
+
+local authTable =
+{
+ ["1073469644"] = { ["96"] = true };
+
+ ["1081616136"] = { ["97"] = true,
+ ["98"] = true };
+
+ ["2421080323"] = { ["100"] = true };
+
+ ["2471146032"] = { ["101"] = true,
+ ["102"] = true };
+}
+
+local playerDataGet = { Success = false }
+
+pcall(function ()
+ playerDataGet = require(ServerStorage:WaitForChild("PlayerDataStore"))
+end)
+
+if not playerDataGet.Success then
+ warn("Failed to load PlayerData. HatGranter will not work.")
+end
+
+local playerData = playerDataGet.DataStore
+
+local function onGrantHat(player,hatId)
+ local userId = player.UserId
+
+ if userId > 0 then
+ local auth = authTable[tostring(game.PlaceId)]
+
+ if auth then
+ local canGiveHat = auth[tostring(hatId)]
+
+ if canGiveHat and playerData then
+ local hatInfo = hatData[hatId]
+ local hatAsset = hatInfo.AssetId
+
+ local myData = playerData:GetSaveData(player)
+ local items = myData:Get("Items")
+ local loadout = myData:Get("Loadout")
+
+ local id = tostring(hatAsset)
+
+ if not items.Hat[id] then
+ items.Hat[id] = true
+ myData:Set("Items", items)
+ end
+
+ loadout.Hat = hatAsset
+ myData:Set("Loadout", loadout)
+ end
+ end
+ end
+end
+
+grantHatToUser.Event:Connect(onGrantHat)
\ No newline at end of file
diff --git a/Server/Heads.server.lua b/Server/Heads.server.lua
new file mode 100644
index 0000000..3ab94fa
--- /dev/null
+++ b/Server/Heads.server.lua
@@ -0,0 +1,27 @@
+local function processObject(obj)
+ if obj:IsA("SpecialMesh") and obj.MeshType == Enum.MeshType.Head then
+ local head = obj.Parent
+
+ local col = math.min(head.Size.X,head.Size.Z)
+ local thickness = head.Size.Y/col
+
+ if math.abs(thickness-1) <= 0.01 then
+ local face = head:FindFirstChild("face")
+ if face and face.Texture:lower() == "rbxasset://textures/face.png" then
+ face.Texture = "rbxassetid://1104210678"
+ end
+ obj.Name = "MeshHead"
+ obj.MeshId = "rbxassetid://1104623876"
+ obj.Scale = obj.Scale * head.Size.Y
+ for _,surface in pairs(Enum.NormalId:GetEnumItems()) do
+ head[surface.Name .. "Surface"] = 0
+ end
+ end
+ end
+end
+
+for _,desc in pairs(workspace:GetDescendants()) do
+ processObject(desc)
+end
+
+workspace.DescendantAdded:Connect(processObject)
\ No newline at end of file
diff --git a/Server/InputGateway.server.lua b/Server/InputGateway.server.lua
new file mode 100644
index 0000000..0dfbbc8
--- /dev/null
+++ b/Server/InputGateway.server.lua
@@ -0,0 +1,48 @@
+local self = script.Parent
+local remote = self:WaitForChild("Gateway")
+
+local tool = self.Parent
+tool.ManualActivationOnly = true
+
+local keyEvent = Instance.new("BindableEvent")
+keyEvent.Name = "KeyEvent"
+keyEvent.Parent = tool
+
+local function onGatewayReceive(sendingPlayer, request, ...)
+ local char = tool.Parent
+
+ if char and char:IsA("Model") then
+ local humanoid = char:FindFirstChild("Humanoid")
+
+ if humanoid then
+ local player = game.Players:GetPlayerFromCharacter(char)
+ assert(sendingPlayer == player)
+
+ if request == "SetActive" then
+ local down, target = ...
+ assert(typeof(target) == "CFrame","Expected CFrame")
+
+ humanoid.TargetPoint = target.p
+
+ if humanoid.Health > 0 and tool:IsDescendantOf(char) then
+ if down then
+ tool:Activate()
+ else
+ tool:Deactivate()
+ end
+ end
+ elseif request == "SetTarget" then
+ local target = ...
+ assert(typeof(target) == "CFrame","Expected CFrame")
+ humanoid.TargetPoint = target.p
+ elseif request == "KeyEvent" then
+ local key, down = ...
+ assert(typeof(key) == "string","bad key cast")
+ assert(typeof(down) == "boolean","bad down state cast")
+ keyEvent:Fire(key, down)
+ end
+ end
+ end
+end
+
+remote.OnServerEvent:Connect(onGatewayReceive)
\ No newline at end of file
diff --git a/Server/Leaderboard.server.lua b/Server/Leaderboard.server.lua
new file mode 100644
index 0000000..8cbdcd0
--- /dev/null
+++ b/Server/Leaderboard.server.lua
@@ -0,0 +1,194 @@
+print("LinkedLeaderboard script version 5.00 loaded")
+
+local ServerStorage = game:GetService("ServerStorage")
+local loadConfig = ServerStorage:FindFirstChild("LoadLeaderboard")
+local hasLoadConfig = (loadConfig ~= nil)
+
+if hasLoadConfig and not loadConfig.Value then
+ return
+end
+
+if not (ServerStorage:FindFirstChild("LoadTools") or (hasLoadConfig and loadConfig.Value)) then -- Infer we aren't in a brickbattle.
+ return
+end
+
+stands = {}
+CTF_mode = false
+
+function onHumanoidDied(humanoid, player)
+ local stats = player:findFirstChild("leaderstats")
+ if stats ~= nil then
+ local deaths = stats:findFirstChild("Wipeouts")
+ deaths.Value = deaths.Value + 1
+
+ -- do short dance to try and find the killer
+
+ local killer = getKillerOfHumanoidIfStillInGame(humanoid)
+
+ handleKillCount(humanoid, player)
+ end
+end
+
+function onPlayerRespawn(property, player)
+ -- need to connect to new humanoid
+
+ if property == "Character" and player.Character ~= nil then
+ local humanoid = player.Character.Humanoid
+ local deathCon
+ local p = player
+ local h = humanoid
+ deathCon = humanoid.Died:connect(function ()
+ deathCon:Disconnect()
+ onHumanoidDied(h, p)
+ end)
+ end
+end
+
+function getKillerOfHumanoidIfStillInGame(humanoid)
+ -- returns the player object that killed this humanoid
+ -- returns nil if the killer is no longer in the game
+
+ -- check for kill tag on humanoid - may be more than one - todo: deal with this
+ local tag = humanoid:findFirstChild("creator")
+
+ -- find player with name on tag
+ if tag ~= nil then
+
+ local killer = tag.Value
+ if killer.Parent ~= nil then -- killer still in game
+ return killer
+ end
+ end
+
+ return nil
+end
+
+function handleKillCount(humanoid, player)
+ local killer = getKillerOfHumanoidIfStillInGame(humanoid)
+ if killer ~= nil then
+ local stats = killer:findFirstChild("leaderstats")
+ if stats ~= nil then
+ local kills = stats:findFirstChild("KOs")
+ if killer ~= player then
+ kills.Value = kills.Value + 1
+
+ else
+ kills.Value = kills.Value - 1
+
+ end
+ end
+ end
+end
+
+
+-----------------------------------------------
+
+
+
+function findAllFlagStands(root)
+ local c = root:children()
+ for i=1,#c do
+ if (c[i].className == "Model" or c[i].className == "Part") then
+ findAllFlagStands(c[i])
+ end
+ if (c[i].className == "FlagStand") then
+ table.insert(stands, c[i])
+ end
+ end
+end
+
+function hookUpListeners()
+ for i=1,#stands do
+ stands[i].FlagCaptured:connect(onCaptureScored)
+ end
+end
+
+function onPlayerEntered(newPlayer)
+
+ if CTF_mode == true then
+
+ local stats = Instance.new("IntValue")
+ stats.Name = "leaderstats"
+
+ local captures = Instance.new("IntValue")
+ captures.Name = "Captures"
+ captures.Value = 0
+
+
+ captures.Parent = stats
+
+ -- VERY UGLY HACK
+ -- Will this leak threads?
+ -- Is the problem even what I think it is (player arrived before character)?
+ while true do
+ if newPlayer.Character ~= nil then break end
+ wait(5)
+ end
+
+ stats.Parent = newPlayer
+
+ else
+
+ local stats = Instance.new("IntValue")
+ stats.Name = "leaderstats"
+
+ local kills = Instance.new("IntValue")
+ kills.Name = "KOs"
+ kills.Value = 0
+
+ local deaths = Instance.new("IntValue")
+ deaths.Name = "Wipeouts"
+ deaths.Value = 0
+
+ kills.Parent = stats
+ deaths.Parent = stats
+
+ -- VERY UGLY HACK
+ -- Will this leak threads?
+ -- Is the problem even what I think it is (player arrived before character)?
+ while true do
+ if newPlayer.Character ~= nil then break end
+ wait(0.5)
+ end
+
+ local deathCon
+ local humanoid = newPlayer.Character.Humanoid
+
+ deathCon = humanoid.Died:connect(function()
+ deathCon:Disconnect()
+ onHumanoidDied(humanoid, newPlayer)
+ end )
+
+ -- start to listen for new humanoid
+ newPlayer.Changed:connect(function(property) onPlayerRespawn(property, newPlayer) end )
+
+
+ stats.Parent = newPlayer
+
+ end
+
+end
+
+
+function onCaptureScored(player)
+
+ local ls = player:findFirstChild("leaderstats")
+ if ls == nil then return end
+ local caps = ls:findFirstChild("Captures")
+ if caps == nil then return end
+ caps.Value = caps.Value + 1
+
+end
+
+
+findAllFlagStands(game.Workspace)
+hookUpListeners()
+if (#stands > 0) then CTF_mode = true end
+
+for _,v in pairs(game.Players:GetPlayers()) do
+ onPlayerEntered(v)
+end
+
+game.Players.ChildAdded:connect(onPlayerEntered)
+
+
diff --git a/Server/LoadTools.server.lua b/Server/LoadTools.server.lua
new file mode 100644
index 0000000..90b84f1
--- /dev/null
+++ b/Server/LoadTools.server.lua
@@ -0,0 +1,19 @@
+local ServerStorage = game:GetService("ServerStorage")
+local StarterPack = game:GetService("StarterPack")
+local Players = game:GetService("Players")
+
+local standardTools = ServerStorage:WaitForChild("StandardTools")
+local loadTools = ServerStorage:FindFirstChild("LoadTools")
+
+if loadTools then
+ for toolName in loadTools.Value:gmatch("[^;]+") do
+ local tool = standardTools:WaitForChild(toolName)
+ tool:Clone().Parent = StarterPack
+
+ for _,v in pairs(Players:GetPlayers()) do
+ if v:FindFirstChild("Backpack") and not v:FindFirstChild(tool.Name) then
+ tool:Clone().Parent = v.Backpack
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/Server/Parts.server.lua b/Server/Parts.server.lua
new file mode 100644
index 0000000..2f0c951
--- /dev/null
+++ b/Server/Parts.server.lua
@@ -0,0 +1,295 @@
+local CollectionService = game:GetService("CollectionService")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local ServerStorage = game:GetService("ServerStorage")
+local Players = game:GetService("Players")
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local brickColors = require(ReplicatedStorage:WaitForChild("BrickColors"))
+local oldColors = {}
+
+for _,v in ipairs(brickColors) do
+ oldColors[v] = true
+end
+
+local textureCaches = {}
+local colorCaches = {}
+
+local char = string.char
+local floor = math.floor
+local clamp = math.clamp
+
+local function serializeColor3(color)
+ local r = clamp(floor(color.r * 256), 0, 255)
+ local g = clamp(floor(color.g * 256), 0, 255)
+ local b = clamp(floor(color.b * 256), 0, 255)
+ return r .. g .. b
+end
+
+local function computeEpsilon(bc, color)
+ local bColor = bc.Color
+
+ local v0 = Vector3.new(bColor.r, bColor.g, bColor.b)
+ local v1 = Vector3.new(color.r, color.g, color.b)
+
+ return (v1-v0).Magnitude
+end
+
+local function toNearestOldBrickColor(color)
+ local colorKey = serializeColor3(color)
+
+ if not colorCaches[colorKey] then
+ local bestColor, bestEpsilon = nil, math.huge
+
+ for bcName in pairs(oldColors) do
+ local bc = BrickColor.new(bcName)
+ local epsilon = computeEpsilon(bc, color)
+
+ if epsilon < bestEpsilon then
+ bestEpsilon = epsilon
+ bestColor = bc
+ end
+ end
+
+ colorCaches[colorKey] = bestColor
+ end
+
+ return colorCaches[colorKey]
+end
+
+local function onBrickColorUpdated(part)
+ part.BrickColor = toNearestOldBrickColor(part.Color)
+end
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local scale = 1
+
+if ServerStorage:FindFirstChild("StudScale") then
+ scale = ServerStorage.StudScale.Value
+end
+
+local surfaces =
+{
+ TopSurface = "Top";
+ BottomSurface = "Bottom";
+ LeftSurface = "Left";
+ RightSurface = "Right";
+ FrontSurface = "Front";
+ BackSurface = "Back";
+}
+
+local textures =
+{
+ Studs = 3309082834;
+ Inlet = 3307955447;
+ Glue = 3308133326;
+ Weld = 3308005654;
+}
+
+local normalIdToAxis =
+{
+ Left = 'X';
+ Right = 'X';
+ Top = 'Y';
+ Bottom = 'Y';
+ Front = 'Z';
+ Back = 'Z';
+}
+
+local function selectUVSize(part, normalId)
+ if typeof(normalId) == "EnumItem" then
+ normalId = normalId.Name
+ end
+
+ local axis = normalIdToAxis[normalId]
+ local size = part.Size
+
+ if axis == 'X' then
+ return size.Z, size.Y
+ elseif axis == 'Z' then
+ return size.X, size.Y
+ elseif axis == 'Y' then
+ return size.X, size.Z
+ end
+end
+
+local function onSurfaceChanged(part, surface)
+ local surfaceId = part[surface].Name
+
+ if surfaceId ~= "SmoothNoOutlines" then
+ local normalId = surfaces[surface]
+ local textureId = textures[surfaceId]
+
+ local cache = textureCaches[part]
+ local texture = cache[surface]
+
+ if not textureId then
+ if texture then
+ texture.Texture = ""
+ end
+
+ return
+ end
+
+ if not (texture and texture:IsA("Texture")) then
+ texture = Instance.new("Texture")
+ cache[surface] = texture
+
+ texture.StudsPerTileU = 2;
+ texture.StudsPerTileV = 4;
+
+ texture.Face = normalId
+ texture.Name = surface
+
+ texture.Parent = part
+ end
+
+ -- Select the texture id based on the even/odd dimensions of the UV map.
+ local mapId = "AA"
+
+ if part:IsA("MeshPart") then
+ local texU, texV = selectUVSize(part, normalId)
+
+ local alignU = string.format("%i", (texU % 2) + .5)
+ local alignV = string.format("%i", (texV % 2) + .5)
+
+ if alignU == '1' then
+ texture.OffsetStudsU = 0.5
+ end
+
+ if alignV == '1' then
+ texture.OffsetStudsV = 0.5
+ end
+ end
+
+ texture.Texture = "rbxassetid://" .. textureId
+ end
+end
+
+local function onTransparencyChanged(part)
+ local textureCache = textureCaches[part]
+
+ for _,texture in pairs(textureCache) do
+ texture.Transparency = 0.25 + (part.Transparency * 0.75)
+ end
+end
+
+local function onParentChanged(part)
+ local texureCache = textureCaches[part]
+
+ if not part:IsDescendantOf(workspace) then
+ for _,tex in pairs(texureCache) do
+ tex:Destroy()
+ end
+
+ texureCache[part] = nil
+ end
+end
+
+local function registerPartSurfaces(part)
+ local textureCache = {}
+ textureCaches[part] = textureCache
+
+ for surface in pairs(surfaces) do
+ onSurfaceChanged(part, surface)
+ end
+
+ onBrickColorUpdated(part)
+ onTransparencyChanged(part)
+
+ part.Material = "SmoothPlastic"
+end
+
+local function applyCharacter(humanoid)
+ local model = humanoid.Parent
+
+ if not CollectionService:HasTag(humanoid, "Classified") then
+ CollectionService:AddTag(humanoid, "Classified")
+
+ for _,v in pairs(model:GetDescendants()) do
+ if v:IsA("SpecialMesh") and v.MeshType == Enum.MeshType.Brick then
+ v:Destroy()
+ end
+ end
+
+ for _,v in pairs(script:GetChildren()) do
+ if v:IsA("CharacterMesh") then
+ local copy = v:Clone()
+ copy.Parent = model
+ CollectionService:AddTag(copy, "NoCharacterBevels")
+ end
+ end
+
+ delay(1, function ()
+ if not model:FindFirstChildWhichIsA("Shirt") then
+ script.Shirt:Clone().Parent = model
+ end
+
+ if not model:FindFirstChildWhichIsA("Pants") then
+ script.Pants:Clone().Parent = model
+ end
+ end)
+ end
+end
+
+local function onDescendantAdded(desc)
+ if desc:IsA("BasePart") then
+ local model = desc:FindFirstAncestorWhichIsA("Model")
+
+ if model then
+ local humanoid = model:FindFirstChildWhichIsA("Humanoid")
+
+ if humanoid then
+ applyCharacter(humanoid)
+ return
+ end
+
+ local fileMesh = desc:FindFirstChildWhichIsA("FileMesh")
+
+ if fileMesh then
+ local meshType = fileMesh.MeshType.Name
+ if meshType == "Head" or meshType == "FileMesh" then
+ desc.Material = "SmoothPlastic"
+ return
+ end
+ end
+
+ if desc:IsA("VehicleSeat") then
+ desc.HeadsUpDisplay = false
+ end
+
+ if not CollectionService:HasTag(desc, "NoBevels") then
+ registerPartSurfaces(desc)
+ end
+ end
+ elseif desc:IsA("Decal") and desc.Texture:lower() == "rbxasset://textures/spawnlocation.png" then
+ desc.Texture = "rbxassetid://989514407"
+ elseif desc:IsA("Humanoid") then
+ applyCharacter(desc)
+ end
+end
+
+local function onItemChanged(object, descriptor)
+ if textureCaches[object] then
+ if surfaces[descriptor] then
+ onSurfaceChanged(object, descriptor)
+ elseif descriptor == "Transparency" then
+ onTransparencyChanged(object)
+ elseif descriptor == "BrickColor" then
+ onBrickColorUpdated(object)
+ elseif descriptor == "Parent" then
+ onParentChanged(object)
+ end
+ end
+end
+
+for _,desc in pairs(workspace:GetDescendants()) do
+ onDescendantAdded(desc)
+end
+
+workspace.DescendantAdded:Connect(onDescendantAdded)
+game.ItemChanged:Connect(onItemChanged)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
diff --git a/Server/Regeneration.server.lua b/Server/Regeneration.server.lua
new file mode 100644
index 0000000..e3edb83
--- /dev/null
+++ b/Server/Regeneration.server.lua
@@ -0,0 +1,98 @@
+local CollectionService = game:GetService("CollectionService")
+local ServerStorage = game:GetService("ServerStorage")
+
+local regen = ServerStorage:FindFirstChild("Regeneration")
+
+if regen then
+ local REGEN_TIME = 800
+ local REGEN_DELAY = 4
+
+ if regen:FindFirstChild("RegenTime") then
+ REGEN_TIME = regen.RegenTime.Value
+ end
+
+ if regen:FindFirstChild("RegenDelay") then
+ REGEN_DELAY = regen.RegenDelay.Value
+ end
+
+ local ready = Instance.new("BoolValue")
+ ready.Name = "Ready"
+ ready.Parent = regen
+
+ local bevelCache = ServerStorage:WaitForChild("BevelCache")
+ local bevelsReady = bevelCache:WaitForChild("BevelsReady")
+
+ local function isLodPart(part)
+ return part and CollectionService:HasTag(part, "PartLOD")
+ end
+
+ local function setupModelRegen(model, title)
+ local title = title or model.Name
+
+ local parent = model.Parent
+ local backup = model:Clone()
+
+ local message, text
+
+ if typeof(title) == "string" then
+ message = Instance.new("Message")
+ text = "Regenerating " .. title .. "..."
+ end
+
+ spawn(function ()
+ while not bevelsReady.Value do
+ bevelsReady.Changed:Wait()
+ end
+
+ local requestSolve = bevelCache:FindFirstChild("RequestSolve")
+ if requestSolve then
+ requestSolve:Invoke(backup)
+ end
+
+ while wait(REGEN_TIME * (1 - (math.random() * .8))) do
+ local cooldown = 0
+
+ if message then
+ message.Text = text
+ message.Parent = workspace
+ end
+
+ model:Destroy()
+ wait(REGEN_DELAY)
+
+ model = backup:Clone()
+ model.Parent = parent
+
+ for _,inst in pairs(model:GetDescendants()) do
+ if inst:IsA("BasePart") then
+ workspace:JoinToOutsiders({inst}, Enum.JointCreationMode.All)
+ end
+ end
+
+ for _,joint in pairs(model:GetDescendants()) do
+ if joint:IsA("JointInstance") and joint.Name:sub(-12) == "Strong Joint" then
+ if isLodPart(joint.Part0) or isLodPart(joint.Part1) then
+ joint:Destroy()
+ end
+ end
+ end
+
+ if message then
+ message.Parent = nil
+ end
+ end
+ end)
+ end
+
+ for _,v in pairs(regen:GetChildren()) do
+ if v:IsA("ObjectValue") then
+ if v.Name == "" then
+ setupModelRegen(v.Value, true)
+ else
+ setupModelRegen(v.Value, v.Name)
+ end
+ end
+ end
+
+ ready.Value = true
+end
\ No newline at end of file
diff --git a/Server/SessionTracker.server.lua b/Server/SessionTracker.server.lua
new file mode 100644
index 0000000..ab71524
--- /dev/null
+++ b/Server/SessionTracker.server.lua
@@ -0,0 +1,52 @@
+local MessagingService = game:GetService("MessagingService")
+local Players = game:GetService("Players")
+
+local jobId = game.JobId
+local placeId = game.PlaceId
+
+if jobId == "" then
+ return
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local closed = false
+
+local function publishUpdate()
+ local playerCount = #Players:GetPlayers()
+
+ local serverInfo =
+ {
+ JobId = jobId;
+ PlaceId = placeId;
+ Players = playerCount;
+ }
+
+ if closed then
+ serverInfo.Closed = true;
+ end
+
+ pcall(function ()
+ MessagingService:PublishAsync("ServerData", serverInfo)
+ end)
+end
+
+local function onGameClosing()
+ closed = true
+ publishUpdate()
+end
+
+game:BindToClose(onGameClosing)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+while #Players:GetPlayers() < 1 do
+ wait(1)
+end
+
+while not closed do
+ publishUpdate()
+ wait(5)
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Server/Time.server.lua b/Server/Time.server.lua
new file mode 100644
index 0000000..e8c1e5a
--- /dev/null
+++ b/Server/Time.server.lua
@@ -0,0 +1,10 @@
+local Lighting = game:GetService("Lighting")
+local ServerStorage = game:GetService("ServerStorage")
+local loadTime = ServerStorage:FindFirstChild("LoadTime")
+
+if loadTime and loadTime.Value then
+ while wait() do
+ Lighting:SetMinutesAfterMidnight((tick()*5)%1440)
+ end
+end
+
diff --git a/Shared/ReplicatedStorage/AssetUtil.lua b/Shared/ReplicatedStorage/AssetUtil.lua
new file mode 100644
index 0000000..b8cae5e
--- /dev/null
+++ b/Shared/ReplicatedStorage/AssetUtil.lua
@@ -0,0 +1,78 @@
+local MarketplaceService = game:GetService("MarketplaceService")
+local InsertService = game:GetService("InsertService")
+
+local AssetUtil =
+{
+ TextureCache = {};
+ NUM_FETCH_RETRIES = 4;
+}
+
+local assetTypes = {}
+for _,assetType in pairs(Enum.AssetType:GetEnumItems()) do
+ assetTypes[assetType.Value] = assetType.Name
+end
+
+function AssetUtil:SafeCall(class,method,...)
+ local success,response
+ local tries = 0
+ while not success do
+ success,response = pcall(class[method],class,...)
+ if not success then
+ if response:find("400") then
+ success = true
+ response = false
+ else
+ tries = tries + 1
+ if tries > self.NUM_FETCH_RETRIES then
+ return false
+ end
+ end
+ end
+ end
+ return success,response
+end
+
+function AssetUtil:Import(assetId)
+ local success,model = self:SafeCall(InsertService,"LoadAsset",assetId)
+ if success then
+ local objects = model:GetChildren()
+ return true,unpack(objects)
+ end
+ return false
+end
+
+function AssetUtil:RequestImage(assetId)
+ assert(typeof(assetId) == "number")
+ assert(assetId == math.floor(assetId))
+ assert(assetId > 0)
+
+ if self.TextureCache[assetId] == nil then
+ local success,response = self:SafeCall(MarketplaceService,"GetProductInfo",assetId)
+ if success then
+ local result
+ if response then
+ local assetType = assetTypes[response.AssetTypeId]
+ if assetType == "Image" then -- No transformation needed!
+ result = "rbxassetid://" .. assetId
+ elseif assetType == "TeeShirt" then
+ local imported,shirtGraphic = self:Import(assetId)
+ if imported then
+ result = shirtGraphic.Graphic
+ end
+ elseif assetType == "Decal" or assetType == "Face" then
+ local imported,decal = self:Import(assetId)
+ if imported then
+ result = decal.Texture
+ end
+ end
+ else
+ result = ""
+ end
+ self.TextureCache[assetId] = result
+ end
+ end
+
+ return true,self.TextureCache[assetId]
+end
+
+return AssetUtil
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/BrickColors.lua b/Shared/ReplicatedStorage/BrickColors.lua
new file mode 100644
index 0000000..76dc402
--- /dev/null
+++ b/Shared/ReplicatedStorage/BrickColors.lua
@@ -0,0 +1,35 @@
+return
+{
+ "White";
+ "Light stone grey";
+ "Medium stone grey";
+ "Dark stone grey";
+ "Black";
+ "Bright red";
+ "Bright yellow";
+ "Cool yellow";
+ "Bright blue";
+ "Bright bluish green";
+ "Medium blue";
+ "Pastel Blue";
+ "Light blue";
+ "Sand blue";
+ "Bright orange";
+ "Br. yellowish orange";
+ "Earth green";
+ "Dark green";
+ "Bright green";
+ "Br. yellowish green";
+ "Medium green";
+ "Sand green";
+ "Dark orange";
+ "Reddish brown";
+ "Bright violet";
+ "Light reddish violet";
+ "Medium red";
+ "Brick yellow";
+ "Sand red";
+ "Brown";
+ "Nougat";
+ "Light orange";
+}
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/FpsCap.lua b/Shared/ReplicatedStorage/Client/FpsCap.lua
new file mode 100644
index 0000000..00efa60
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/FpsCap.lua
@@ -0,0 +1,17 @@
+return function (script)
+ local RunService = game:GetService("RunService")
+ local TeleportService = game:GetService("TeleportService")
+
+ spawn(function ()
+ while RunService.RenderStepped:Wait() do
+ if TeleportService:GetTeleportSetting("FPSCapTo30") then
+ local t0 = tick()
+ RunService.Heartbeat:Wait()
+ debug.profilebegin("30 FPS Cap")
+ repeat until (t0+0.0325) < tick()
+ debug.profileend()
+ end
+ end
+ end)
+
+end
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/LensFlare.lua b/Shared/ReplicatedStorage/Client/LensFlare.lua
new file mode 100644
index 0000000..a1989b6
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/LensFlare.lua
@@ -0,0 +1,134 @@
+local RunService = game:GetService("RunService")
+local Lighting = game:GetService("Lighting")
+local TeleportService = game:GetService("TeleportService")
+
+local c = workspace.CurrentCamera
+
+local lensFlareNode = c:FindFirstChild("LensFlareNode")
+if not lensFlareNode then
+ lensFlareNode = Instance.new("Part")
+ lensFlareNode.Name = "LensFlareNode"
+ lensFlareNode.Transparency = 1
+ lensFlareNode.Anchored = true
+ lensFlareNode.CanCollide = false
+ lensFlareNode.Locked = true
+ lensFlareNode.Parent = c
+end
+
+local lenses =
+{
+ { Color = Color3.fromRGB(200, 255, 200), Radius = 1.20, Distance = 0.100 };
+ { Color = Color3.fromRGB( 0, 255, 0), Radius = 0.50, Distance = 0.200 };
+ { Color = Color3.fromRGB(255, 0, 0), Radius = 0.30, Distance = 0.225 };
+ { Color = Color3.fromRGB(255, 170, 0), Radius = 1.50, Distance = 0.250 };
+ { Color = Color3.fromRGB(255, 170, 0), Radius = 3.00, Distance = 0.250 };
+ { Color = Color3.fromRGB( 0, 255, 0), Radius = 0.50, Distance = 0.300 };
+ { Color = Color3.fromRGB( 0, 255, 0), Radius = 0.20, Distance = 0.600 };
+ { Color = Color3.fromRGB( 0, 255, 0), Radius = 0.40, Distance = 0.650 };
+ { Color = Color3.fromRGB(255, 0, 0), Radius = 0.20, Distance = 0.780 };
+ { Color = Color3.fromRGB( 0, 255, 0), Radius = 0.25, Distance = 0.900 };
+ { Color = Color3.fromRGB( 23, 17, 0), Radius = 0.15, Distance = 1.200 };
+ { Color = Color3.fromRGB( 23, 17, 0), Radius = 0.15, Distance = 1.500 };
+}
+
+local function projectRay(ray,length)
+ local origin = ray.Origin
+ local direction = ray.Direction
+ return Ray.new(origin,direction.Unit * length)
+end
+
+local function computeSunOcclusion()
+ local sunPos = Lighting:GetSunDirection()
+ local cf = c.CFrame
+
+ if sunPos:Dot(cf.lookVector) > 0 then
+ local sunView = c:WorldToViewportPoint(cf.p + sunPos)
+ local visibility = 0
+ local total = 0
+
+ for dx = -1,1 do
+ for dy = -1,1 do
+ local posX = math.floor(sunView.X + dx * 15)
+ local posY = math.floor(sunView.Y + dy * 15)
+
+ local sunRay = c:ViewportPointToRay(posX, posY)
+ sunRay = projectRay(sunRay,5000)
+
+ local hit,pos = workspace:FindPartOnRay(sunRay,c)
+ if not hit then
+ visibility = visibility + 1
+ end
+ total = total + 1
+ end
+ end
+
+ visibility = visibility / total
+ return (1-visibility),sunView
+ end
+
+ return 0
+end
+
+local function asVector2(v3,...)
+ return Vector2.new(v3.X,v3.Y),...
+end
+
+local function update()
+ if TeleportService:GetTeleportSetting("ClassicSky") then
+ local vpSize = c.ViewportSize
+ local sunDir = Lighting:GetSunDirection()
+ local sunWP = sunDir * 10e6
+ local sunSP,inView = asVector2(c:WorldToViewportPoint(sunWP))
+ local occlusion = inView and computeSunOcclusion() or 1
+ if occlusion < 1 then
+ local invSunSP = vpSize - sunSP
+ local enabled = (inView and occlusion < 1)
+ local flareBrightness = math.sqrt(math.max(0,sunDir.y*4))
+
+ for i,lense in ipairs(lenses) do
+ local radius = lense.Radius / 12
+ if not lense.Beam then
+ local a0 = Instance.new("Attachment")
+ lense.A0 = a0
+ a0.Name = i .. "_A0"
+ a0.Parent = lensFlareNode
+
+ local a1 = Instance.new("Attachment")
+ lense.A1 = a1
+ a1.Name = i .. "_A1"
+ a1.Parent = lensFlareNode
+
+ local beam = Instance.new("Beam")
+ lense.Beam = beam
+
+ beam.Name = i
+ beam.Color = ColorSequence.new(lense.Color)
+ beam.Width0 = radius
+ beam.Width1 = radius
+ beam.TextureSpeed = 0
+ beam.Transparency = NumberSequence.new(0.9)
+ beam.LightEmission = 1
+ beam.Texture = "rbxasset://sky/lensflare.jpg"
+ beam.Attachment0 = a0
+ beam.Attachment1 = a1
+ beam.Parent = lensFlareNode
+ end
+
+ local lenseSP = invSunSP:lerp(sunSP,lense.Distance)
+ local lenseWP = c:ViewportPointToRay(lenseSP.X,lenseSP.Y,1).Origin
+ local lenseCF = CFrame.new(lenseWP,lenseWP - sunDir)
+ lense.A0.CFrame = lenseCF * CFrame.new(-radius/2,0,0)
+ lense.A1.CFrame = lenseCF * CFrame.new(radius/2,0,0)
+ end
+
+ lensFlareNode.Parent = c
+ return
+ end
+ end
+
+ lensFlareNode.Parent = nil
+end
+
+return function (script)
+ RunService:BindToRenderStep("LensFlareUpdate",201,update)
+end
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/Moon.lua b/Shared/ReplicatedStorage/Client/Moon.lua
new file mode 100644
index 0000000..b40a7f1
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/Moon.lua
@@ -0,0 +1,24 @@
+return function (script)
+ local RunService = game:GetService("RunService")
+ local Lighting = game:GetService("Lighting")
+ local TeleportService = game:GetService("TeleportService")
+
+ local c = workspace.CurrentCamera
+ local moon = script:WaitForChild("Moon")
+ moon.Locked = true
+ moon.Size = Vector3.new(50,50,1)
+
+ local function moonUpdate()
+ if TeleportService:GetTeleportSetting("ClassicSky") then
+ local pos = Lighting:GetMoonDirection() * 900
+ local origin = c.CFrame.p
+ moon.Parent = c
+ moon.CFrame = CFrame.new(origin+pos, origin)
+ else
+ moon.Parent = nil
+ end
+ end
+
+ RunService:BindToRenderStep("MoonUpdate",201,moonUpdate)
+ return 1
+end
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/Moon/Moon.rbxmx b/Shared/ReplicatedStorage/Client/Moon/Moon.rbxmx
new file mode 100644
index 0000000..7d5a855
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/Moon/Moon.rbxmx
@@ -0,0 +1,194 @@
+
+ -
+
+ true
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 4
+ 0
+
+ 175.2431
+ 225.3089
+ -54.92519
+ 0.74845
+ -0.2371427
+ 0.6193432
+ 1.490116E-08
+ 0.9338833
+ 0.3575782
+ -0.6631912
+ -0.2676294
+ 0.698965
+
+ false
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ true
+ false
+ 256
+ Moon
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 1
+
+ 0
+ 0
+ 0
+
+ 1
+ 1
+
+ 80
+ 80
+ 1
+
+ true
+
+
-
+
+ true
+
[null]
+ false
+
+ true
+
+ 800
+ 800
+
+ false
+ true
+ 5
+ 0
+ MoonGui
+ 50
+ true
+ [null]
+ 0
+
+ 0
+ 0
+ 0
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://599112257
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ MoonImage
+
[null]
+ [null]
+ [null]
+ [null]
+
+ -0.5
+ 0
+ -0.5
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 2
+ 0
+ 2
+ 0
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 1
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/Mouse.lua b/Shared/ReplicatedStorage/Client/Mouse.lua
new file mode 100644
index 0000000..975b019
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/Mouse.lua
@@ -0,0 +1,133 @@
+local UserInputService = game:GetService("UserInputService")
+local RunService = game:GetService("RunService")
+local GuiService = game:GetService("GuiService")
+local Players = game:GetService("Players")
+
+-----------------------------------------------------------------
+
+local inGuiFocus = false
+local inputQueue = {}
+
+local function checkGuiFocus()
+ inGuiFocus = (next(inputQueue) ~= nil)
+end
+
+local function onInputChanged(input,gameProcessed)
+ if input.UserInputType == Enum.UserInputType.MouseMovement then
+ inputQueue[input] = gameProcessed or nil
+ checkGuiFocus()
+ end
+end
+
+UserInputService.InputChanged:Connect(onInputChanged)
+
+-----------------------------------------------------------------
+
+local activated = false
+local player = Players.LocalPlayer
+local mouseGui
+
+local function onInputBegan(input,gameProcessed)
+ if mouseGui then
+ if input.UserInputType == Enum.UserInputType.Touch and not gameProcessed then
+ wait(.1)
+ if input.UserInputState == Enum.UserInputState.End then
+ activated = true
+ else
+ mouseGui.ImageTransparency = 1
+ end
+ end
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+
+-----------------------------------------------------------------
+
+local GUN_WAIT_CURSOR = "rbxasset://textures/GunWaitCursor.png"
+local GUN_CURSOR = "rbxasset://textures/GunCursor.png"
+local IS_TOUCH = UserInputService.TouchEnabled
+
+return function (script)
+ mouseGui = script.Parent
+
+ local hasTool = mouseGui:WaitForChild("HasTool")
+ UserInputService.MouseIconEnabled = false
+
+ local canActivate = true
+
+ if UserInputService.TouchEnabled then
+ local c = workspace.CurrentCamera
+ local playerGui = player:WaitForChild("PlayerGui")
+ local touchGui = playerGui:WaitForChild("TouchGui")
+ local touchFrame = touchGui:WaitForChild("TouchControlFrame")
+ if c.ViewportSize.Y < 600 then
+ touchFrame.Size = UDim2.new(0.85,0,0.8,0)
+ else
+ touchFrame.Size = UDim2.new(0.9,0,0.9,0)
+ end
+ touchFrame.Position = UDim2.new(0.05,0,0,0)
+ end
+
+ local function updateMouse()
+ local char = player.Character
+ local tool
+ local override = false
+ if char then
+ tool = char:FindFirstChildWhichIsA("Tool")
+ hasTool.Value = (tool ~= nil)
+ if tool then
+ if tool:FindFirstChild("IconOverride") then
+ if tool.IconOverride.Value ~= "" then
+ mouseGui.Image = tool.IconOverride.Value
+ else
+ mouseGui.Image = "rbxassetid://1000000"
+ end
+ elseif tool.Enabled then
+ mouseGui.Image = GUN_CURSOR
+ if IS_TOUCH then
+ canActivate = true
+ mouseGui.ImageTransparency = 1
+ end
+ else
+ mouseGui.Image = GUN_WAIT_CURSOR
+ end
+ end
+ else
+ hasTool.Value = false
+ end
+ if inGuiFocus then
+ mouseGui.Image = "rbxassetid://1000000"
+ end
+
+ local guiInset = GuiService:GetGuiInset()
+ local pos = UserInputService:GetMouseLocation() - guiInset
+ local upos = UDim2.new(0,pos.X,0,pos.Y)
+
+ if IS_TOUCH then
+ if hasTool.Value then
+ mouseGui.Visible = true
+ if activated and mouseGui.Image == GUN_WAIT_CURSOR then
+ if canActivate then
+ canActivate = false
+ mouseGui.Position = upos
+ mouseGui.ImageTransparency = -1
+ end
+ activated = false
+ else
+ mouseGui.ImageTransparency = math.min(1,mouseGui.ImageTransparency + 0.01)
+ end
+ else
+ mouseGui.Visible = false
+ end
+ else
+ mouseGui.Position = upos
+ end
+
+ if UserInputService.MouseIconEnabled then
+ UserInputService.MouseIconEnabled = false
+ end
+ end
+
+ RunService:BindToRenderStep("UpdateMouse",1000,updateMouse)
+end
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/Sky.lua b/Shared/ReplicatedStorage/Client/Sky.lua
new file mode 100644
index 0000000..a423fce
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/Sky.lua
@@ -0,0 +1,146 @@
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Services
+
+local Lighting = game:GetService("Lighting")
+local RunService = game:GetService("RunService")
+local TeleportService = game:GetService("TeleportService")
+local UserInputService = game:GetService("UserInputService")
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Sky Colors
+
+local midnight = 0
+local day = 86400
+local hour = day/24
+
+local sunRise = day * .25
+local sunSet = day * .75
+local riseAndSetTime = hour/2
+
+local times =
+{
+ midnight;
+ sunRise - hour;
+ sunRise - riseAndSetTime;
+ sunRise;
+ sunRise + riseAndSetTime;
+ sunSet - riseAndSetTime;
+ sunSet;
+ sunSet + (hour/3);
+ day;
+}
+
+local colors =
+{
+ Color3.new();
+ Color3.new();
+ Color3.new(.2, .15, .01);
+ Color3.new(.2, .15, .01);
+ Color3.new(1, 1, 1);
+ Color3.new(1, 1, 1);
+ Color3.new(.4, .2, .05);
+ Color3.new();
+ Color3.new();
+}
+
+local function linearSpline(x,times,values)
+ assert(#times == #values)
+ if #values == 1 or x < times[1] then
+ return values[1]
+ end
+
+ for i = 2, #times do
+ if x < times[i] then
+ local alpha = (times[i] - x) / (times[i] - times[i-1])
+ return values[i-1]:lerp(values[i], 1-alpha)
+ end
+ end
+
+ return values[#values]
+end
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local lastTime = 0
+local c = workspace.CurrentCamera
+
+local function r()
+ return -1 + (math.random()*2)
+end
+
+local skyAdorn = script:WaitForChild("SkyAdorn")
+local night = skyAdorn:WaitForChild("Night")
+local nightFrame = night:WaitForChild("NightFrame")
+local star = script:WaitForChild("Star")
+
+if UserInputService.TouchEnabled then
+ -- TODO: Get rid of this when shadow-mapping is available
+ -- on mobile or the tone mapping is corrected.
+
+ spawn(function ()
+ local legacyToneMap = Lighting:WaitForChild("LegacyToneMap")
+ legacyToneMap:Destroy()
+ end)
+end
+
+return function (script)
+ local shadowsOn = true
+
+ for i = 1,500 do
+ local bb = star:Clone()
+ bb.StudsOffsetWorldSpace = Vector3.new(r(), r(), r()).Unit * 2500
+ bb.Size = UDim2.new(0, math.random(2, 5), 0, math.random(2, 5))
+ bb.Adornee = skyAdorn
+ bb.Parent = skyAdorn
+ end
+
+ local function updateSky()
+ local shadowState = TeleportService:GetTeleportSetting("StencilShadows")
+
+ if shadowState == nil then
+ TeleportService:SetTeleportSetting("StencilShadows", true)
+ shadowState = true
+ end
+
+ if shadowState ~= shadowsOn then
+ shadowsOn = shadowState
+
+ if shadowsOn then
+ local black = Color3.new()
+ Lighting.GlobalShadows = true
+ Lighting.Ambient = black:Lerp(Lighting.OutdoorAmbient, 0.5)
+ else
+ Lighting.GlobalShadows = false
+ Lighting.Ambient = Lighting.OutdoorAmbient
+ end
+ end
+
+
+
+ if TeleportService:GetTeleportSetting("ClassicSky") then
+ local seconds = Lighting:GetMinutesAfterMidnight() * 60
+
+ if seconds < 0 then
+ seconds = day + seconds
+ end
+
+ if seconds ~= lastTime then
+ local sunDir = game.Lighting:GetSunDirection()
+ local skyColor = linearSpline(seconds, times, colors)
+ nightFrame.BackgroundColor3 = skyColor
+ nightFrame.BackgroundTransparency = math.clamp((sunDir.Y + .033) * 10, 0, 1)
+ lastTime = seconds
+ end
+
+ local sunDir = Lighting:GetSunDirection()
+ skyAdorn.CFrame = CFrame.new(c.CFrame.p) * CFrame.new(Vector3.new(), sunDir)
+ skyAdorn.Parent = (nightFrame.BackgroundTransparency < 1 and c or nil)
+ else
+ skyAdorn.Parent = nil
+ end
+ end
+
+ RunService:BindToRenderStep("UpdateSky", 201, updateSky)
+end
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/Sky/SkyAdorn.rbxmx b/Shared/ReplicatedStorage/Client/Sky/SkyAdorn.rbxmx
new file mode 100644
index 0000000..a7866db
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/Sky/SkyAdorn.rbxmx
@@ -0,0 +1,186 @@
+
+ -
+
+ true
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 4
+ 0
+
+ 154.9001
+ 49.55002
+ -213.09
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ false
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ SkyAdorn
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 1
+
+ 0
+ 0
+ 0
+
+ 1
+ 1
+
+ 0.05
+ 0.05
+ 0.05
+
+ true
+
+
-
+
+ false
+
[null]
+ false
+
+ true
+ false
+ 0
+ 0
+ -1
+ true
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+ 1
+ INF
+ Night
+ [null]
+ true
+ [null]
+
+ 0
+ 3000
+ 0
+ 3000
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+ -3000
+
+
+ 0
+ 0
+ 0
+
+
+ 0
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ NightFrame
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/Sky/Star.rbxmx b/Shared/ReplicatedStorage/Client/Sky/Star.rbxmx
new file mode 100644
index 0000000..3ef3671
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/Sky/Star.rbxmx
@@ -0,0 +1,109 @@
+
+ -
+
+ false
+
[null]
+ false
+
+ true
+ false
+ 0
+ 0
+ -1
+ true
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+ 0
+ INF
+ Star
+ [null]
+ true
+ [null]
+
+ 0
+ 3
+ 0
+ 3
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+ 0
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ Frame
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+
+
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/SunRays.lua b/Shared/ReplicatedStorage/Client/SunRays.lua
new file mode 100644
index 0000000..32e763b
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/SunRays.lua
@@ -0,0 +1,79 @@
+local Lighting = game:GetService("Lighting")
+local RunService = game:GetService("RunService")
+local TeleportService = game:GetService("TeleportService")
+
+local adorn = script:WaitForChild("Rays")
+local sunRays = adorn:WaitForChild("SunRays")
+
+local function getCamera()
+ return workspace.CurrentCamera
+end
+
+local function projectRay(ray,length)
+ local origin = ray.Origin
+ local direction = ray.Direction
+ return Ray.new(origin,direction.Unit * length)
+end
+
+local function computeSunVisibility()
+ local sunPos = Lighting:GetSunDirection()
+ local camera = getCamera()
+ local cf = camera.CFrame
+
+ if sunPos:Dot(cf.lookVector) > 0 then
+ local sunView = camera:WorldToViewportPoint(cf.p + sunPos)
+ local visibility = 0
+ local total = 0
+
+ for dx = -1,1 do
+ for dy = -1,1 do
+ local posX = math.floor(sunView.X + dx * 15)
+ local posY = math.floor(sunView.Y + dy * 15)
+
+ local sunRay = camera:ViewportPointToRay(posX, posY)
+ sunRay = projectRay(sunRay,5000)
+
+ local hit,pos = workspace:FindPartOnRay(sunRay,camera)
+ if not hit then
+ visibility = visibility + 1
+ end
+ total = total + 1
+ end
+ end
+
+ visibility = visibility / total
+ return visibility,sunView
+ end
+
+ return 0
+end
+
+local function update()
+ if TeleportService:GetTeleportSetting("ClassicSky") then
+ local sunPos = Lighting:GetSunDirection()
+ if sunPos.Y >= -.1 then
+ local visibility,sunView = computeSunVisibility()
+ if visibility > 0.001 then
+ local attenuation = (1 - (2*visibility - 1)*(2*visibility - 1))
+ local strength = math.clamp((((1 - sunPos.Y)*2) / math.sqrt(2)), 0, 1)
+ local opacity = attenuation * 0.4 * strength
+
+ local camera = getCamera()
+ local rayPos = camera:ViewportPointToRay(sunView.X,sunView.Y,1).Origin
+ local rayLook = camera.CFrame.p
+
+ adorn.Parent = camera
+ adorn.CFrame = CFrame.new(rayPos,rayLook)
+ sunRays.Transparency = NumberSequence.new(1-opacity)
+
+ return
+ end
+ end
+ end
+
+ adorn.Parent = nil
+end
+
+return function ()
+ RunService:BindToRenderStep("SunRays",201,update)
+end
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/Client/SunRays/Rays.rbxmx b/Shared/ReplicatedStorage/Client/SunRays/Rays.rbxmx
new file mode 100644
index 0000000..f45dd8d
--- /dev/null
+++ b/Shared/ReplicatedStorage/Client/SunRays/Rays.rbxmx
@@ -0,0 +1,153 @@
+
+ -
+
+ true
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ -85.06
+ 0.025018
+ 62.01
+ 0
+ 0
+ -1
+ 0
+ 1
+ 0
+ 1
+ 0
+ 0
+
+ false
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ true
+ false
+ 256
+ Rays
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 1
+
+ 0
+ 0
+ 0
+
+ 1
+ 1
+
+ 0.05
+ 0.05
+ 0.05
+
+ true
+
+
-
+
+
+
+ -1
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ Ray0
+
+ false
+ true
+
+
+ -
+
+
+
+ 1
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ Ray1
+
+ false
+ true
+
+
+ -
+
+
[RBX06B41274A1604F5E9F7C6D5B74C7CF50]
+ [RBX3965BDEF98754F7DA4F5245A097859B0]
+
+ 0 1 1 1 0 1 1 1 1 0
+ 0
+ 0
+ true
+ false
+ 1
+ 0
+ SunRays
+ 1
+
+
+ rbxasset://sky/sun-rays.jpg
+
+ 1
+ 0
+ 0
+ 0 0.5 0 1 0.5 0
+ 2
+ 2
+ 0
+ true
+
+
+
+
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/ItemData/Face.lua b/Shared/ReplicatedStorage/ItemData/Face.lua
new file mode 100644
index 0000000..9485c6d
--- /dev/null
+++ b/Shared/ReplicatedStorage/ItemData/Face.lua
@@ -0,0 +1,195 @@
+return
+{
+ [1] =
+ {
+ Name = "Default";
+ AssetId = 1104210678;
+ OwnedByDefault = true;
+ };
+ [2] =
+ {
+ Name = "Glee";
+ Price = 250;
+ AssetId = 7074729;
+ };
+ [3] =
+ {
+ Name = "Drool";
+ Price = 150;
+ AssetId = 7074893;
+ };
+ [4] =
+ {
+ Name = "Chubs";
+ Price = 800;
+ AssetId = 7076110;
+ };
+ [5] =
+ {
+ Name = "Fearless";
+ Price = 2500;
+ AssetId = 7074991;
+ };
+ [6] =
+ {
+ Name = "Winky";
+ Price = 450;
+ AssetId = 7074864;
+ };
+ [7] =
+ {
+ Name = "Fang";
+ Price = 500;
+ AssetId = 7075142;
+ };
+ [8] =
+ {
+ Name = "Toothy Grin";
+ Price = 950;
+ AssetId = 7075434;
+ };
+ [9] =
+ {
+ Name = "Classic Goof";
+ Price = 1000;
+ AssetId = 7074661;
+ };
+ [10] =
+ {
+ Name = "Uh Oh";
+ Price = 1050;
+ AssetId = 7074944;
+ };
+ [11] =
+ {
+ Name = ":-/";
+ Price = 1500;
+ AssetId = 7075469;
+ };
+ [12] =
+ {
+ Name = "Mysterious";
+ Price = 1230;
+ AssetId = 7132035;
+ };
+ [13] =
+ {
+ Name = "Good Intentioned";
+ Price = 150;
+ AssetId = 7317793;
+ };
+ [14] =
+ {
+ Name = "Alright";
+ Price = 250;
+ AssetId = 7131541;
+ };
+ [15] =
+ {
+ Name = "Lazy Eye";
+ Price = 100;
+ AssetId = 7075502;
+ };
+ [16] =
+ {
+ Name = "O.o";
+ Price = 1000;
+ AssetId = 7074595;
+ };
+ [17] =
+ {
+ Name = "Chippy McTooth";
+ Price = 1440;
+ AssetId = 7317769;
+ };
+ [18] =
+ {
+ Name = "Daring";
+ Price = 1000;
+ AssetId = 8329690;
+ };
+ [19] =
+ {
+ Name = "Hmmm...";
+ Price = 3333;
+ AssetId = 7076076;
+ };
+ [20] =
+ {
+ Name = "It's Go Time!";
+ Price = 1500;
+ AssetId = 7506141;
+ };
+ [21] =
+ {
+ Name = "Classic Vampire";
+ Price = 10000;
+ AssetId = 7074836;
+ };
+ [22] =
+ {
+ Name = "Emotionally Distressed Zombie";
+ Price = 20000;
+ AssetId = 7506136
+ };
+ [23] =
+ {
+ Name = "Sinister";
+ Price = 10001;
+ AssetId = 8329686;
+ };
+ [24] =
+ {
+ Name = "Slickfang";
+ Price = 1750;
+ AssetId = 7317765;
+ };
+ [25] =
+ {
+ Name = "Existential Angst";
+ Price = 50000;
+ AssetId = 7074814;
+ };
+ [26] =
+ {
+ Name = "Sad";
+ Price = 9000;
+ AssetId = 7699177;
+ };
+ [27] =
+ {
+ Name = "I Am Not Amused";
+ Price = 2500;
+ AssetId = 7131886;
+ };
+ [28] =
+ {
+ Name = "RAWR!";
+ Price = 3000;
+ AssetId = 7076224;
+ };
+ [29] =
+ {
+ Name = "Stitchface";
+ Price = 40000;
+ AssetId = 8329679;
+ };
+ [30] =
+ {
+ Name = "Frightful";
+ Price = 4500;
+ AssetId = 7699193;
+ };
+ [31] =
+ {
+ Name = "Frightening Unibrow";
+ Price = 8000;
+ AssetId = 8560985;
+ };
+ [32] =
+ {
+ Name = "Aghast";
+ Price = 3000;
+ AssetId = 9250633;
+ }
+}
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/ItemData/Hat.lua b/Shared/ReplicatedStorage/ItemData/Hat.lua
new file mode 100644
index 0000000..4931701
--- /dev/null
+++ b/Shared/ReplicatedStorage/ItemData/Hat.lua
@@ -0,0 +1,609 @@
+return
+{
+ [1] =
+ {
+ Name = "2008 ROBLOX Visor";
+ Price = 8;
+ AssetId = 1459035;
+ };
+ [2] =
+ {
+ Name = "Ribbons";
+ Price = 20;
+ AssetId = 1098284;
+ };
+ [3] =
+ {
+ Name = "Lampshade";
+ Price = 25;
+ AssetId = 1098282;
+ };
+ [4] =
+ {
+ Name = "Blue Winter Cap";
+ Price = 29;
+ AssetId = 1309918;
+ };
+ [5] =
+ {
+ Name = "Kitty Ears";
+ Price = 30;
+ AssetId = 1374269;
+ };
+ [6] =
+ {
+ Name = "Chef Hat";
+ Price = 35;
+ AssetId = 1374258;
+ };
+ [7] =
+ {
+ Name = "Firefighter Helmet";
+ Price = 40;
+ AssetId = 1081381;
+ };
+ [8] =
+ {
+ Name = "Stage Prop";
+ Price = 50;
+ AssetId = 1098285;
+ };
+ [9] =
+ {
+ Name = "Screw";
+ Price = 70;
+ AssetId = 1090516;
+ };
+ [10] =
+ {
+ Name = "Little Fluffy Cloud";
+ Price = 85;
+ AssetId = 1098272;
+ };
+ [11] =
+ {
+ Name = "Sapling";
+ Price = 100;
+ AssetId = 1090511;
+ };
+ [12] =
+ {
+ Name = "Game Input Device";
+ Price = 125;
+ AssetId = 1098274;
+ };
+ [13] =
+ {
+ Name = "Headrow";
+ Price = 210;
+ AssetId = 1082935;
+ };
+ [14] =
+ {
+ Name = "Hammerhead";
+ Price = 250;
+ AssetId = 1098276;
+ };
+ [15] =
+ {
+ Name = "Headstack";
+ Price = 280;
+ AssetId = 1080950;
+ };
+ [16] =
+ {
+ Name = "Satellite Dish";
+ Price = 325;
+ AssetId = 1098278;
+ };
+ [17] =
+ {
+ Name = "Vegetable Hat";
+ Price = 500;
+ AssetId = 1098273;
+ };
+ [18] =
+ {
+ Name = "Bandana";
+ Price = 600;
+ AssetId = 1369338;
+ };
+ [19] =
+ {
+ Name = "The Agonizingly Ugly Yellow Baseball Cap";
+ Price = 750;
+ AssetId = 1034433;
+ };
+ [20] =
+ {
+ Name = "Traffic Cone";
+ Price = 1000;
+ AssetId = 1082932;
+ };
+ [21] =
+ {
+ Name = "Teakettle Hat";
+ Price = 1337;
+ AssetId = 1374328;
+ };
+ [22] =
+ {
+ Name = "Fruit Hat";
+ Price = 1500;
+ AssetId = 1081347;
+ };
+ [23] =
+ {
+ Name = "Golden Crown";
+ Price = 5000;
+ AssetId = 1081300;
+ };
+ [24] =
+ {
+ Name = "Bucket";
+ Price = 7331;
+ AssetId = 1081239;
+ };
+ [25] =
+ {
+ Name = "The Void Star";
+ Price = 20000;
+ AssetId = 1125510;
+ };
+ [26] =
+ {
+ Name = "Valkyrie Helm";
+ Price = 30000;
+ AssetId = 1365767;
+ };
+ [27] =
+ {
+ Name = "The Crimson Catseye";
+ Price = 50000;
+ AssetId = 1193866;
+ };
+ [28] =
+ {
+ Name = "Paper Bag";
+ Price = 60;
+ AssetId = 1051573;
+ };
+ [29] =
+ {
+ Name = "Red Baseball Cap";
+ Price = 60;
+ AssetId = 1028606;
+ };
+ [30] =
+ {
+ Name = "Ninja Mask of Shadows";
+ Price = 120;
+ AssetId = 1309911;
+ };
+ [31] =
+ {
+ Name = "Target Hat";
+ Price = 150;
+ AssetId = 1051578;
+ };
+ [32] =
+ {
+ Name = "Lavender Baseball Cap";
+ Price = 150;
+ AssetId = 1031324;
+ };
+ [33] =
+ {
+ Name = "Antennae";
+ Price = 200;
+ AssetId = 1374256;
+ };
+ [34] =
+ {
+ Name = "Blue Baseball Cap";
+ Price = 240;
+ AssetId = 1028728;
+ };
+ [35] =
+ {
+ Name = "Astronaut Helmet";
+ Price = 115;
+ AssetId = 1081366;
+ };
+ [36] =
+ {
+ Name = "Cheese Hat";
+ Price = 350;
+ AssetId = 1097026;
+ };
+ [37] =
+ {
+ Name = "Brickasaw Headband";
+ Price = 160;
+ AssetId = 1235486;
+ };
+ [38] =
+ {
+ Name = "Picnic Hat";
+ Price = 390;
+ AssetId = 1051577;
+ };
+ [39] =
+ {
+ Name = "Question Mark";
+ Price = 420;
+ AssetId = 1028826;
+ };
+ [40] =
+ {
+ Name = "Wizard's Hat";
+ Price = 500;
+ AssetId = 1049198;
+ };
+ [41] =
+ {
+ Name = "Chessboard";
+ Price = 640;
+ AssetId = 1098275;
+ };
+ [42] =
+ {
+ Name = "Arrow Hat";
+ Price = 670;
+ AssetId = 1036705;
+ };
+ [43] =
+ {
+ Name = "Bighead";
+ Price = 700;
+ AssetId = 1048037;
+ };
+ [44] =
+ {
+ Name = "Green Baseball Cap";
+ Price = 750;
+ AssetId = 1028842;
+ };
+ [45] =
+ {
+ Name = "Native American Headdress";
+ Price = 760;
+ AssetId = 1081236;
+ };
+ [46] =
+ {
+ Name = "Police Cap";
+ Price = 770;
+ AssetId = 1028793;
+ };
+ [47] =
+ {
+ Name = "Thunderstorm Hat";
+ Price = 860;
+ AssetId = 1098279;
+ };
+ [48] =
+ {
+ Name = "Helmet";
+ Price = 930;
+ AssetId = 1045407;
+ };
+ [49] =
+ {
+ Name = "Straw Hat";
+ Price = 980;
+ AssetId = 1033722;
+ };
+ [50] =
+ {
+ Name = "Hunting Hat";
+ Price = 1050;
+ AssetId = 1043468;
+ };
+ [51] =
+ {
+ Name = "Princess Hat";
+ Price = 1090;
+ AssetId = 1032641;
+ };
+ [52] =
+ {
+ Name = "Red Stetson";
+ Price = 1150;
+ AssetId = 1029668;
+ };
+ [53] =
+ {
+ Name = "LOL Sign";
+ Price = 1200;
+ AssetId = 1037450;
+ };
+ [54] =
+ {
+ Name = "Viking Helm";
+ Price = 1250;
+ AssetId = 1028720;
+ };
+ [55] =
+ {
+ Name = "Police Sergeants Cap";
+ Price = 2000;
+ AssetId = 1081392;
+ };
+ [56] =
+ {
+ Name = "Striped Hat";
+ Price = 2050;
+ AssetId = 1031492;
+ };
+ [57] =
+ {
+ Name = "Brown Cowboy Hat";
+ Price = 2140;
+ AssetId = 1029597;
+ };
+ [58] =
+ {
+ Name = "Bunny Ears";
+ Price = 2420;
+ AssetId = 1080949;
+ };
+ [59] =
+ {
+ Name = "Italian Ski Cap";
+ Price = 3000;
+ AssetId = 1038669;
+ };
+ [60] =
+ {
+ Name = "Tan Stetson";
+ Price = 3540;
+ AssetId = 1029673;
+ };
+ [61] =
+ {
+ Name = "Navy Captain Hat";
+ Price = 4050;
+ AssetId = 1098283;
+ };
+ [62] =
+ {
+ Name = "Sombrero";
+ Price = 4500;
+ AssetId = 1028804;
+ };
+ [63] =
+ {
+ Name = "BiggerHead";
+ Price = 5500;
+ AssetId = 1048213;
+ };
+ [64] =
+ {
+ Name = "Jester's Cap";
+ Price = 5950;
+ AssetId = 1037673;
+ };
+ [65] =
+ {
+ Name = "Tiara";
+ Price = 7000;
+ AssetId = 1031418;
+ };
+ [66] =
+ {
+ Name = "Fedora";
+ Price = 9000;
+ AssetId = 1029025;
+ };
+ [67] =
+ {
+ Name = "Coif of Glory";
+ Price = 9500;
+ AssetId = 1091572;
+ };
+ [68] =
+ {
+ Name = "Teapot Hat";
+ Price = 13337;
+ AssetId = 1045408;
+ };
+ [69] =
+ {
+ Name = "White Cowboy Hat";
+ Price = 14000;
+ AssetId = 1029602;
+ };
+ [70] =
+ {
+ Name = "Pirate Captain's Hat";
+ Price = 20000;
+ AssetId = 1028859;
+ };
+ [71] =
+ {
+ Name = "Kaiser Helm";
+ Price = 27500;
+ AssetId = 1045406;
+ };
+ [72] =
+ {
+ Name = "Clockwork's Headphones";
+ Price = 33920;
+ AssetId = 1235488;
+ };
+ [73] =
+ {
+ Name = "Purple Banded Top Hat";
+ Price = 50000;
+ AssetId = 1028715;
+ };
+ [74] =
+ {
+ Name = "JJ5x5's White Top Hat";
+ Price = 100000;
+ AssetId = 1073690;
+ };
+ [75] =
+ {
+ Name = "The Ice Crown";
+ Price = 250000;
+ AssetId = 1323384;
+ };
+ [76] =
+ {
+ Name = "The Agonizingly Ugly Bucket of Doom";
+ Price = 300000;
+ AssetId = 1286490;
+ };
+ [77] =
+ {
+ Name = "Ninja Mask of Awesome";
+ Price = 150000;
+ AssetId = 1286496;
+ };
+ [78] =
+ {
+ Name = "Sparkle Time Fedora";
+ Price = 500000;
+ AssetId = 1285307;
+ };
+ [79] =
+ {
+ Name = "The Kleos Aphthiton";
+ Price = 750000;
+ AssetId = 1114768;
+ };
+ [80] =
+ {
+ Name = "2007 ROBLOX Visor";
+ Price = 8;
+ AssetId = 1163672;
+ };
+ [81] =
+ {
+ Name = "Got Milk Visor";
+ Price = 10000;
+ AssetId = 1081240;
+ };
+ [82] =
+ {
+ Name = "The Bluesteel Bathelm";
+ Price = 250000;
+ AssetId = 1172161;
+ };
+ [83] =
+ {
+ Name = "Chemistry Textbook";
+ Price = 900;
+ AssetId = 1139753;
+ };
+ [84] =
+ {
+ Name = "Biology Textbook";
+ Price = 900;
+ AssetId = 1136591;
+ };
+ [85] =
+ {
+ Name = "Emerald Eye";
+ Price = 850;
+ AssetId = 1185264;
+ };
+ [86] =
+ {
+ Name = "Mouse Ears";
+ Price = 30;
+ AssetId = 1098271;
+ };
+ [87] =
+ {
+ Name = "Roblox Rogues Visor";
+ Price = 400;
+ AssetId = 1081285;
+ };
+ [88] =
+ {
+ Name = "Superbrick Visor";
+ Price = 400;
+ AssetId = 1090471;
+ };
+ [89] =
+ {
+ Name = "Monstars Visor";
+ Price = 400;
+ AssetId = 1086815;
+ };
+ [90] =
+ {
+ Name = "Happy 100K Day";
+ Price = 100000;
+ AssetId = 1213029;
+ };
+ [91] =
+ {
+ Name = "Blue Snipers Visor";
+ Price = 400;
+ AssetId = 1086814;
+ };
+ [92] =
+ {
+ Name = "Domino Crown";
+ Price = 1000000;
+ AssetId = 1031429;
+ };
+ [93] =
+ {
+ Name = "Clone's Hat";
+ AssetId = 1053447516;
+ Owner = 2032622;
+ };
+ [94] =
+ {
+ Name = "Nostalgia Club Hard Hat";
+ AssetId = 1080951;
+ };
+ [95] =
+ {
+ Name = "Four Sky";
+ AssetId = 42211680;
+ Owner = 9769187;
+ };
+ [96] =
+ {
+ Name = "The Riddling Skull";
+ AssetId = 4765718;
+ };
+ [97] =
+ {
+ Name = "Sinister P.";
+ AssetId = 17450053;
+ };
+ [98] =
+ {
+ Name = "Ruby Serpent Mummy Mask";
+ AssetId = 17450058;
+ };
+ [99] =
+ {
+ Name = "No Hat";
+ AssetId = 0;
+ ThumbId = 1016721709;
+ OwnedByDefault = true;
+ };
+ [100] =
+ {
+ Name = "Mystery Box III";
+ AssetId = 2421046388;
+ };
+ [101] =
+ {
+ Name = "Halloween Paintball 2009 Trophy";
+ AssetId = 17440559;
+ };
+ [102] =
+ {
+ Name = "Orange Paintball Mask";
+ AssetId = 17415907;
+ }
+}
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/ItemData/Pants.lua b/Shared/ReplicatedStorage/ItemData/Pants.lua
new file mode 100644
index 0000000..528e8fb
--- /dev/null
+++ b/Shared/ReplicatedStorage/ItemData/Pants.lua
@@ -0,0 +1,94 @@
+return
+{
+ [1] =
+ {
+ Name = "No Pants";
+ AssetId = 1110798365;
+ ThumbId = 1110798367;
+ OwnedByDefault = true;
+ };
+ [2] =
+ {
+ Name = "Jeans";
+ AssetId = 1804738;
+ Price = 125;
+ };
+ [3] =
+ {
+ Name = "Battle Pants of Awesomeness";
+ AssetId = 1804767;
+ Price = 200;
+ };
+ [4] =
+ {
+ Name = "Grey Wizard Robes";
+ AssetId = 1812625;
+ Price = 125;
+ };
+ [5] =
+ {
+ Name = "Dark Armor";
+ AssetId = 5624321;
+ Price = 300;
+ };
+ [6] =
+ {
+ Name = "Red Wizard Robes";
+ AssetId = 1882758;
+ Price = 250;
+ };
+ [7] =
+ {
+ Name = "Toxic";
+ AssetId = 1819272;
+ Price = 100;
+ };
+ [8] =
+ {
+ Name = "Emerald Armor";
+ AssetId = 4084364;
+ Price = 250;
+ };
+ [9] =
+ {
+ Name = "Cloud";
+ AssetId = 2320047;
+ Price = 200;
+ };
+ [10] =
+ {
+ Name = "Bone Armor";
+ AssetId = 8992193;
+ Price = 100;
+ };
+ [11] =
+ {
+ Name = "Wave";
+ AssetId = 2459354;
+ Price = 300;
+ };
+ [12] =
+ {
+ Name = "Vision";
+ AssetId = 2528468;
+ Price = 300;
+ };
+ [13] =
+ {
+ Name = "Twisted Metal";
+ AssetId = 4161674;
+ Price = 300;
+ };
+ [14] =
+ {
+ Name = "Ruby";
+ AssetId = 5578473;
+ Price = 300;
+ };
+ [15] =
+ {
+ Name = "Offer";
+ AssetId = 1860640;
+ Price = 300;
+ };
+}
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/ItemData/Shirt.lua b/Shared/ReplicatedStorage/ItemData/Shirt.lua
new file mode 100644
index 0000000..6d32fe7
--- /dev/null
+++ b/Shared/ReplicatedStorage/ItemData/Shirt.lua
@@ -0,0 +1,95 @@
+return
+{
+ [1] =
+ {
+ Name = "No Shirt";
+ AssetId = 1110695025;
+ ThumbId = 1110695032;
+ OwnedByDefault = true;
+ };
+ [2] =
+ {
+ Name = "White Shirt";
+ AssetId = 1804746;
+ Price = 20;
+ };
+ [3] =
+ {
+ Name = "Battle Shirt of Awesomeness";
+ AssetId = 1804770;
+ Price = 200;
+ };
+ [4] =
+ {
+ Name = "Stanford Sweatshirt";
+ AssetId = 1804865;
+ Price = 125;
+ };
+ [5] =
+ {
+ Name = "Spartan Armor";
+ AssetId = 1806213;
+ ThumbId = 1806215;
+ Price = 250;
+ };
+ [6] =
+ {
+ Name = "Vision";
+ AssetId = 2528460;
+ Price = 300;
+ };
+ [7] =
+ {
+ Name = "Camo-Shirt";
+ AssetId = 1804726;
+ Price = 40;
+ };
+ [8] =
+ {
+ Name = "Cloud";
+ AssetId = 2332774;
+ Price = 200;
+ };
+ [9] =
+ {
+ Name = "Nuke the Whales";
+ AssetId = 4892452;
+ Price = 250;
+ };
+ [10] =
+ {
+ Name = "Link";
+ AssetId = 1810231;
+ Price = 100;
+ };
+ [11] =
+ {
+ Name = "Emerald Armor";
+ AssetId = 4084197;
+ Price = 250;
+ };
+ [12] =
+ {
+ Name = "Bone Armor";
+ AssetId = 8988296;
+ Price = 100;
+ };
+ [13] =
+ {
+ Name = "Wave";
+ AssetId = 2459414;
+ Price = 300;
+ };
+ [14] =
+ {
+ Name = "Ruby";
+ AssetId = 5578459;
+ Price = 300;
+ };
+ [15] =
+ {
+ Name = "Offer";
+ AssetId = 1860633;
+ Price = 300;
+ }
+}
\ No newline at end of file
diff --git a/Shared/ReplicatedStorage/PlaceData.lua b/Shared/ReplicatedStorage/PlaceData.lua
new file mode 100644
index 0000000..f5e2a7c
--- /dev/null
+++ b/Shared/ReplicatedStorage/PlaceData.lua
@@ -0,0 +1,60 @@
+local AssetService = game:GetService("AssetService")
+local places = AssetService:GetGamePlacesAsync()
+
+local creators =
+{
+ ["Ultimate Paintball"] = "miked";
+ ["Sunset Plain"] = "Schwaabo";
+ ["Sword Fights on the Heights I"] = "Telamon";
+ ["Sword Fights on the Heights IV"] = "Telamon";
+ ["Haunted Mansion"] = "Telamon";
+ ["Rocket Fight Advanced"] = "Telamon";
+ ["Pinball Wizards!"] = "Telamon";
+ ["Balance"] = "Matt Dusek";
+ ["Dodge The Teapots of Doom!"] = "clockwork";
+ ["Thrillville"] = "JJ5x5";
+ ["The Undead Coming"] = "Stealth Pilot";
+ ["Brick Battle: Superiority Complex"] = "Cruss Kilderstrohe";
+ ["Mini Robloxia"] = "Are92";
+ ["Nevermoor's Blight"] = "Telamon";
+ ["ROBLOX Halloween Treasure Hunt 2009"] = "Jacobxxduel and Stealth Pilot";
+ ["Yorick's Resting Place"] = "Yorick";
+ ["King of the Hill"] = "Zuka and JoshJosh117";
+ ["Minigame World"] = "miked";
+ ["Super Nostalgia Zone Sandbox"] = "CloneTrooper1019";
+ ["The Underground War"] = "stickmasterluke";
+ ["Doomspire Brickbattle"] = "Temple of Brickbattle";
+ ["HERE, where the world is quiet"] = "Swinburne";
+ ["ROBLOX Bowling Alley"] = "blXhd";
+ ["ROBLOX Halloween Paintball 2009"] = "Stealth Pilot";
+}
+
+local function iterPageItems(pages)
+ return coroutine.wrap(function ()
+ local pageNum = 1
+ while true do
+ for _, item in ipairs(pages:GetCurrentPage()) do
+ coroutine.yield(item, pageNum)
+ end
+ if pages.IsFinished then
+ break
+ end
+ pages:AdvanceToNextPageAsync()
+ pageNum = pageNum + 1
+ end
+ end)
+end
+
+local placeData = {}
+
+for place in iterPageItems(places) do
+ if place.PlaceId ~= game.PlaceId then
+ if place.Name:lower():find("devtest") then
+ place.DevTest = true
+ end
+ place.Creator = creators[place.Name] or "ROBLOX"
+ table.insert(placeData,place)
+ end
+end
+
+return placeData
\ No newline at end of file
diff --git a/Shared/ServerScriptService/LightingConfig.server.lua b/Shared/ServerScriptService/LightingConfig.server.lua
new file mode 100644
index 0000000..9746e13
--- /dev/null
+++ b/Shared/ServerScriptService/LightingConfig.server.lua
@@ -0,0 +1,25 @@
+local CollectionService = game:GetService("CollectionService")
+local Lighting = game:GetService("Lighting")
+
+if not CollectionService:HasTag(Lighting, "ConfigApplied") then
+ local toneMap = Instance.new("ColorCorrectionEffect")
+ toneMap.TintColor = Color3.new(1.25, 1.25, 1.25)
+ toneMap.Brightness = 0.03
+ toneMap.Saturation = 0.07
+ toneMap.Contrast = -0.15
+
+ toneMap.Name = "LegacyToneMap"
+ toneMap.Parent = Lighting
+
+ local black = Color3.new()
+
+ if Lighting.Ambient ~= black then
+ Lighting.OutdoorAmbient = Lighting.Ambient
+ Lighting.Ambient = black:Lerp(Lighting.Ambient, 0.5)
+ end
+
+ Lighting.GlobalShadows = true
+ Lighting.ShadowSoftness = 0
+
+ CollectionService:AddTag(Lighting, "ConfigApplied")
+end
\ No newline at end of file
diff --git a/Shared/ServerStorage/PlayerDataStore.lua b/Shared/ServerStorage/PlayerDataStore.lua
new file mode 100644
index 0000000..6aba7c0
--- /dev/null
+++ b/Shared/ServerStorage/PlayerDataStore.lua
@@ -0,0 +1,831 @@
+--===============================================--
+--== API ==--
+--===============================================--
+--[[
+
+The module returns an object 'PlayerDataStore'
+with the following methods:
+
+PlayerDataStore:GetSaveData(Player player) -> returns SaveData
+ Returns the SaveData structure for a given
+ player who is currently in the server.
+ Will yield until the player's data is actually
+ ready before returning.
+
+PlayerDataStore:GetSaveDataById(integer userId) -> returns SaveData
+ Returns the SaveData structure for a player
+ with a given userId, who may or may not
+ currently be in the server.
+ Will yield until the user's data is actually
+ ready before returning.
+
+PlayerDataStore:FlushAll()
+ Saves all unsaved changes in all SaveData
+ structures in the place at the time of calling.
+ Will yield until all of the save calls have
+ completed.
+
+The SaveData structures have the following methods:
+
+SaveData:Get(string key) -> returns stored value
+ Gets the value associated with |key| in this
+ player's stored data. The cached value for this
+ server will be returned, so this call will always
+ return immediately.
+
+SaveData:Set(string key, variant value)
+ Sets the cached value associated with |key| on
+ this server. This cached value will be saved to
+ the DataStore at some point in the future: either
+ when the passive save runs, when the player leaves
+ the server, or when you call Flush() on he SaveData,
+ whichever happens first.
+ The call returns immediately.
+
+SaveData:Update((array of strings) keys, function updateFunction)
+ Atomically update multiple keys of a SaveData
+ at once, and update the results in the data store,
+ ensuring that no data is lost. Will yield until the
+ save to the data store has actually completed.
+ EG, for an important developer product purchase:
+ saveData:Update({'PurchaseCount', 'Money'}, function(oldPurchaseCount, oldMoney)
+ if not oldPurchaseCount then oldPurchaseCount = 0 end
+ if not oldMoney then oldMoney = 0 end
+ oldMoney = oldMoney + developerProductAmount
+ oldPurchaseCount = oldPurchaseCount + 1
+ return oldPurchaseCount, oldMoney
+ end)
+ In general you should only be using this function to
+ handle developer product purchases, as the data store
+ throttling limit is quite low, and you will run into
+ it if your users are hitting part of your functionality
+ that uses this with a lot of requests.
+
+]]--
+
+
+--===============================================--
+--== Global Module Settings ==--
+--===============================================--
+
+-- How long to keep cached data for a given player
+-- before letting it be thrown away if there are
+-- no other references to it in your code.
+-- Note1: Even after the expiry time, as long as
+-- there is Lua code in your project somewhere
+-- holding onto a reference to the SaveData object
+-- for a player, the cached data for that player
+-- will be kept.
+-- Note2: Data for a given play will be cached for
+-- as long as that player is in the server whether
+-- or not you actually have a reference to it. The
+-- expiry time is only relevant for the data of
+-- players who are not in the server.
+local CACHE_EXPIRY_TIME = 60*1 --10 minutes
+
+-- How often to save unsaved changes to a player's
+-- data store if it has not been manually Flush'd
+-- by calling the Flush method on their SaveData,
+-- or Flush'd via the player leaving the place.
+local PASSIVE_SAVE_FREQUENCY = 60*1 -- once every 1 minute
+
+-- How accurately to clear cache entries / do passive
+-- saves. That is, how often to check if those things
+-- need to be done.
+local PASSIVE_GRANULARITY = 5 -- check once every 5 seconds
+
+-- Optional key serialization. This specifies how
+-- a given key should be saved to or loaded from
+-- the actual data store. If not specified in the
+-- tables then the key will just be directly
+-- passed to the DataStore:Get/Set/UpdateAsync
+-- methods.
+local SERIALIZE = {}
+local DESERIALIZE = {}
+-- Put your entries here --
+ --vvvvv--
+
+ --^^^^^--
+----------------------------
+-- EG:
+-- SERIALIZE.ScoreObject = function(scoreObject)
+-- return scoreObject.Value
+-- end
+-- DESERIALIZE.ScoreObject = function(value)
+-- local object = Instance.new('IntValue')
+-- object.Name = 'ScoreIntValue'
+-- object.Value = value
+-- return object
+-- end
+-- ...usage (Note, you would never actually want to do this with an
+-- IntValue object as shown, you could just be storing a straight
+-- number instead, and it wouldn't work very well because
+-- if you just set the IntValue's value the PlayerDataStore
+-- would not know that it had changed, and would not save the
+-- change. You'd actually have to call Set(...) to make the
+-- change save):
+-- local PlayerDataStore = require(game.ServerStorage.PlayerDataStore)
+-- local saveData = PlayerDataStore:GetSaveData(player)
+-- local valueObject = saveData:Get('ScoreObject')
+-- print(valueObject.Name, valueObject.Value) -> ScoreIntValue
+-- local newScoreObject = Instance.new('IntValue')
+-- newScoreObject.Value = 4
+-- -- Even though normally you cannot save objects to the data
+-- -- store, the custom serialize you provided handles it
+-- saveData:Set('ScoreObject', newScoreObject)
+-- saveData:Flush()
+
+
+-- The name of the data store to use to store the
+-- player data.
+local DATASTORE_NAME = '__SuP3R_n0sTa1Gi4_z0nE__'
+
+-- Guarantee a save ASAP when a player leaves a server.
+-- This ensures that if they go to another server of the
+-- same place the save will almost certainly have already
+-- completed when they enter the new place. You can leave
+-- this off to be lighter on the save quota if you are
+-- not possibly storing very important stuff in the data
+-- store right before a player leaves.
+local SAVE_ON_LEAVE = true
+
+-- Debug flag, for internal debugging
+local DEBUG = false
+
+-- Check if we are able to use DataStores.
+if game.GameId < 1 then
+ warn("Game is not connected to a universe, cannot load DataStores.")
+ return { Success = false }
+end
+
+local success,errorMsg = pcall(function ()
+ local DataStoreService = game:GetService("DataStoreService")
+ wait()
+ DataStoreService:GetGlobalDataStore()
+end)
+
+if not success then
+ warn("DataStore is unavailable: " .. tostring(errorMsg))
+ return { Success = false }
+end
+
+--===============================================--
+--== Utility Functions ==--
+--===============================================--
+-- Deep copy a table without circular references
+local function DeepCopy(tb)
+ if type(tb) == 'table' then
+ local new = {}
+ for k, v in pairs(tb) do
+ new[k] = DeepCopy(v)
+ end
+ return new
+ else
+ return tb
+ end
+end
+
+-- Spawn a new thread and run it immedately up to
+-- the first yield before returning
+local function SpawnNow(func)
+ local ev = Instance.new('BindableEvent')
+ ev.Event:connect(func)
+ ev:Fire()
+end
+
+--===============================================--
+--== SaveData Class ==--
+--===============================================--
+
+-- Holds the cached saved data for a given player.
+local SaveData = {}
+function SaveData.new(playerDataStore, userId)
+ local this = {}
+
+ --===============================================--
+ --== Private Data ==--
+ --===============================================--
+ this.userId = userId
+ this.lastSaved = 0
+
+ -- Locked status, so that saves and updates cannot
+ -- end up fighting over the same data
+ this.locked = false
+ this.unlocked = Instance.new('BindableEvent')
+
+ -- The actual data for this SaveData structure
+ this.dataSet = nil
+
+ -- Keys that have unsaved changes
+ this.dirtyKeySet = {}
+
+ -- keys that we "own", that is, ones we have
+ -- written to or read from on this SaveData.
+ this.ownedKeySet = {}
+
+ --===============================================--
+ --== Private Implementation ==--
+ --===============================================--
+ local function ownKey(key)
+ this.ownedKeySet[key] = true
+ end
+ local function dirtyKey(key)
+ this.dirtyKeySet[key] = true
+ end
+ local function markAsTouched(key)
+ ownKey(key)
+ playerDataStore:markAsTouched(this)
+ end
+ local function markAsDirty(key)
+ ownKey(key)
+ dirtyKey(key)
+ playerDataStore:markAsDirty(this)
+ end
+
+ -- Load in the data for the struct
+ function this:makeReady(data)
+ this.dataSet = data
+ this.lastSaved = tick()
+ playerDataStore:markAsTouched(this)
+ end
+
+ function this:waitForUnlocked()
+ while this.locked do
+ this.unlocked.Event:wait()
+ end
+ end
+ function this:lock()
+ this.locked = true
+ end
+ function this:unlock()
+ this.locked = false
+ this.unlocked:Fire()
+ end
+
+ --===============================================--
+ --== Public API ==--
+ --===============================================--
+ -- Getter and setter function to manipulate keys
+ -- for this player.
+ function this:Get(key)
+ if type(key) ~= 'string' then
+ error("Bad argument #1 to SaveData::Get() (string expected)", 2)
+ end
+ if DEBUG then
+ print("SaveData<"..this.userId..">::Get("..key..")")
+ end
+ markAsTouched(key)
+ local value = this.dataSet[key]
+ if value == nil and DESERIALIZE[key] then
+ -- If there's no current value, and the key
+ -- has serialization, then we should get the
+ -- null deserialized state.
+ local v = DESERIALIZE[key](nil)
+ -- Note: we don't markAsDirty here, that's
+ -- intentional, as we don't want to save
+ -- if we don't have to, and we don't need
+ -- to here, as Deserialize(key, nil) should
+ -- return back the same thing every time.
+ -- However, we still need cache the value,
+ -- because deserialize(key, nil) might still
+ -- be expensive or the caller might expect
+ -- the same reference back each time.
+ this.dataSet[key] = v
+ return v
+ else
+ return value
+ end
+ end
+ -- Set(key, value, allowErase)
+ -- Note: If allowErase is not set to true, then
+ -- the call will error on value = nil, this is
+ -- to prevent you accidentally erasing data when
+ -- you don't mean to. If you do want to erase,
+ -- then call with allowErase = true
+ function this:Set(key, value, allowErase)
+ if type(key) ~= 'string' then
+ error("Bad argument #1 to SaveData::Set() (string expected)", 2)
+ end
+ if value == nil and not allowErase then
+ error("Attempt to SaveData::Set('"..key.."', nil) without allowErase = true", 2)
+ end
+ if DEBUG then
+ print("SaveData<"..this.userId..">::Set("..key..", "..tostring(value)..")")
+ end
+ markAsDirty(key)
+ this.dataSet[key] = value
+ end
+
+ -- For important atomic transactions, update data
+ -- store. For example, for any Developer Product
+ -- based purchases you should use this to ensure
+ -- that the changes are saved right away, and
+ -- correctly.
+ -- Note: Update() will automatically Flush any
+ -- unsaved changes while doing the update.
+ function this:Update(keyList, func)
+ if type(keyList) ~= 'table' then
+ error("Bad argument #1 to SaveData::Update() (table of keys expected)", 2)
+ end
+ if type(func) ~= 'function' then
+ error("Bad argument #2 to SaveData::Update() (function expected)", 2)
+ end
+ if DEBUG then
+ print("SaveData<"..this.userId..">::Update("..table.concat(keyList, ", ")..", "..tostring(func)..")")
+ end
+ playerDataStore:doUpdate(this, keyList, func)
+ end
+
+ -- Flush all unsaved changes out to the data
+ -- store for this player.
+ -- Note: This call will yield and not return
+ -- until the data has actually been saved if
+ -- there were any unsaved changes.
+ function this:Flush()
+ if DEBUG then
+ print("SaveData<"..this.userId..">::Flush()")
+ end
+ playerDataStore:doSave(this)
+ end
+
+ return this
+end
+
+
+--===============================================--
+--== PlayerDataStore Class ==--
+--===============================================--
+-- A singleton that manages all of the player data
+-- saving and loading in a place.
+local PlayerDataStore = {}
+function PlayerDataStore.new()
+ local this = {}
+
+ --===============================================--
+ --== Private Data ==--
+ --===============================================--
+
+ -- The actual data store we are writing to
+ local DataStoreService = game:GetService('DataStoreService')
+ local mDataStore = DataStoreService:GetDataStore(DATASTORE_NAME)
+
+ -- The weak-reference to each player's data, so
+ -- that as long as the place owner keeps a ref
+ -- to the data we will have it in this cache, and
+ -- won't reload a second copy.
+ local mUserIdSaveDataCache = setmetatable({}, {__mode = 'v'}) -- {UserId -> SaveData}
+
+ -- Strong-reference to recently touched data, to
+ -- implement the cache expiry time.
+ local mTouchedSaveDataCacheSet = {} -- {SaveData}
+
+ -- Strong-reference to the data of players who are
+ -- online, we always want to keep a reference to
+ -- their data.
+ local mOnlinePlayerSaveDataMap = {} -- {Player -> SaveData}
+
+ -- Dirty save datas, that is, ones that have
+ -- unsaved changes.
+ local mDirtySaveDataSet = {} -- {SaveData}
+
+ -- Players whose data is currently being requested
+ local mOnRequestUserIdSet = {} -- {UserId}
+ local mRequestCompleted = Instance.new('BindableEvent')
+
+ -- Number of save functions still running
+ -- used on server shutdown to know how long to keep the
+ -- server alive for after the last player has left.
+ local mSavingCount = 0
+
+ --===============================================--
+ --== Private Implementation ==--
+ --===============================================--
+ -- transform a userId into a data store key
+ local function userIdToKey(userId)
+ if script:FindFirstChild("DebugUserId") and game.JobId == "" then
+ return "PlayerList$" .. script.DebugUserId.Value
+ else
+ return 'PlayerList$'..userId
+ end
+ end
+
+ function this:markAsTouched(saveData)
+ if DEBUG then print("PlayerDataStore::markAsTouched("..saveData.userId..")") end
+ mTouchedSaveDataCacheSet[saveData] = true
+ saveData.lastTouched = tick()
+ mUserIdSaveDataCache[saveData.userId] = saveData
+ end
+ function this:markAsDirty(saveData)
+ if DEBUG then print("PlayerDataStore::markAsDirty("..saveData.userId..")") end
+ this:markAsTouched(saveData)
+ mDirtySaveDataSet[saveData] = true
+ mUserIdSaveDataCache[saveData.userId] = saveData
+ end
+
+ -- the initial data to record for a given userId
+ local function initialData(userId)
+ return {}
+ end
+
+ -- collect and clear out the dirty key set of a save data
+ local function collectDataToSave(saveData)
+ local toSave = {}
+ local toErase = {}
+ for key, _ in pairs(saveData.dirtyKeySet) do
+ local value = saveData.dataSet[key]
+ if value ~= nil then
+ -- Get the value to be saved
+ if SERIALIZE[key] then
+ -- If there is a seralization function provided, then use
+ -- that to serialize out the value into the data to be
+ -- stored.
+ toSave[key] = SERIALIZE[key](value)
+ else
+ -- If no serialiation is provided, still do at least a deep
+ -- copy of the value, so that further changes to the SaveData
+ -- after the save call will not interfear with the call if
+ -- it takes multiple tries to update the DataStore data.
+ toSave[key] = DeepCopy(value)
+ end
+ else
+ -- no value, add to the list of keys to erase
+ table.insert(toErase, key)
+ end
+ -- Turn off the dirty flag for the key, we are working on saving it
+ saveData.dirtyKeySet[key] = nil
+ end
+ return toSave, toErase
+ end
+
+ -- Main saving functions that push out unsaved
+ -- changes to the actual data store
+ function this:doSave(saveData)
+ if DEBUG then print("PlayerDataStore::doSave("..saveData.userId..") {") end
+ -- update save time and dirty status in my
+ -- listing even if there arn't any changes
+ -- to save.
+ saveData.lastSaved = tick()
+ mDirtySaveDataSet[saveData] = nil
+
+ -- are there any dirty keys?
+ if next(saveData.dirtyKeySet) then
+ -- cache the data to save
+ local toSave, toErase = collectDataToSave(saveData)
+
+ -- update the data with all the dirty keys
+ saveData:waitForUnlocked()
+ saveData:lock()
+ mSavingCount = mSavingCount + 1
+
+ local attempts = 0
+ for i = 1,10 do
+ local success = pcall(function ()
+ mDataStore:UpdateAsync(userIdToKey(saveData.userId), function(oldData)
+ -- Init the data if there is none yet
+ if not oldData then
+ oldData = initialData(saveData.userId)
+ end
+ if DEBUG then print("\tattempting save:") end
+
+ -- For each dirty key to be saved, update it
+ for key, data in pairs(toSave) do
+ if DEBUG then print("\t\tsaving `"..key.."` = "..tostring(data)) end
+ oldData[key] = data
+ end
+
+ -- For each key to erase, erase it
+ for _, key in pairs(toErase) do
+ if DEBUG then print("\t\tsaving `"..key.."` = nil [ERASING])") end
+ oldData[key] = nil
+ end
+
+ -- Return back the updated data
+ return oldData
+ end)
+ end)
+ if success then
+ break
+ else
+ attempts = attempts + 1
+ warn("save failed, trying again...", attempts)
+ end
+ end
+ if DEBUG then print("\t saved.") end
+ mSavingCount = mSavingCount - 1
+ saveData:unlock()
+ elseif DEBUG then
+ print("\tnothing to save")
+ end
+ if DEBUG then print("}") end
+ end
+ function this:doUpdate(saveData, keyList, updaterFunc)
+ if DEBUG then print("PlayerDataStore::doUpdate("..saveData.userId..", {"..table.concat(keyList, ", ").."}, "..tostring(updaterFunc)..") {") end
+ -- updates happen all at once, lock right away
+ saveData:waitForUnlocked()
+ saveData:lock()
+ mSavingCount = mSavingCount + 1
+
+ -- Unflag this SaveData as dirty
+ saveData.lastSaved = tick()
+ mDirtySaveDataSet[saveData] = nil
+
+ -- turn the keyList into a key set as well
+ -- also own all of the keys in it.
+ local updateKeySet = {}
+ for _, key in pairs(keyList) do
+ saveData.ownedKeySet[key] = true
+ updateKeySet[key] = true
+ end
+
+ -- gather the data to save currently in the saveData. There
+ -- may be some or none.
+ local toSave, toErase = collectDataToSave(saveData)
+
+ -- do the actual update
+ mDataStore:UpdateAsync(userIdToKey(saveData.userId), function(oldData)
+ if DEBUG then print("\ttrying update:") end
+ -- Init the data if there is none yet
+ if not oldData then
+ oldData = initialData(saveData.userId)
+ end
+
+ -- gather current values to pass to the the updater func
+ local valueList = {}
+ for i, key in pairs(keyList) do
+ local value = saveData.dataSet[key]
+ if value == nil and DESERIALIZE[key] then
+ valueList[i] = DESERIALIZE[key](nil)
+ else
+ valueList[i] = value
+ end
+ end
+
+ -- call the updaterFunc and get the results back
+ local results = {updaterFunc(unpack(valueList, 1, #keyList))}
+
+ -- Save the results to the data store and SaveData cache
+ for i, result in pairs(results) do
+ local key = keyList[i]
+ -- Serialize if needed, and save to the result for the data store
+ if SERIALIZE[key] then
+ local serialized = SERIALIZE[key](result)
+ if DEBUG then print("\t\tsaving result: `"..key.."` = "..tostring(serialized).." [SERIALIZED]") end
+ oldData[key] = serialized
+ else
+ if DEBUG then print("\t\tsaving result: `"..key.."` = "..tostring(result)) end
+ oldData[key] = result
+ end
+ -- also save the result to the SaveData cache:
+ saveData.dataSet[key] = result
+ end
+
+ -- Also while we're at it, save the dirty values to the data store
+ -- but only if they weren't in the set that we just updated.
+ for key, value in pairs(toSave) do
+ -- Serialize if needed.
+ if not updateKeySet[key] then
+ if DEBUG then print("\t\tsaving unsaved value: `"..key.."` = "..tostring(value)) end
+ oldData[key] = value --(note, value is already serialized)
+ end
+ end
+ for _, key in pairs(toErase) do
+ if not updateKeySet[key] then
+ if DEBUG then print("\t\tsaving unsaved value: `"..key.."` = nil [ERASING]") end
+ oldData[key] = nil
+ end
+ end
+
+ -- return the finalized result
+ return oldData
+ end)
+
+
+ -- finish the save
+ mSavingCount = mSavingCount - 1
+ saveData:unlock()
+
+ if DEBUG then print("}") end
+ end
+
+ -- Main method for loading in the data of a user
+ -- or grabbing it from the cache if it is still
+ -- "hot" (in the UserIdSaveDataCache but nowhere
+ -- else)
+ local function doLoad(userId)
+ if DEBUG then print("PlayerDataStore::doLoad("..userId..") {") end
+ local saveData;
+ -- First see if it is in the cache
+ saveData = mUserIdSaveDataCache[userId]
+ if saveData then
+ if DEBUG then print("\tRecord was already in cache") end
+ -- touch it and return it
+ this:markAsTouched(saveData)
+ if DEBUG then print("}") end
+ return saveData
+ end
+ -- Not on file, we need to load it in, are
+ -- we already loading it though?
+ if mOnRequestUserIdSet[userId] then
+ if DEBUG then print("\tRecord already requested, wait for it...") end
+ -- wait for the existing request to complete
+ while true do
+ saveData = mRequestCompleted.Event:wait()()
+ if saveData.userId == userId then
+ -- this IS the request we're looking for
+ this:markAsTouched(saveData)
+ if DEBUG then
+ print("\tRecord successfully retrieved by another thread")
+ print("}")
+ end
+ return saveData
+ end
+ end
+ else
+ if DEBUG then print("\tRequest record...") end
+ -- Not on request, we need to do the load
+ mOnRequestUserIdSet[userId] = true
+ -- load the data
+ local data = mDataStore:GetAsync(userIdToKey(userId)) or {}
+ -- deserialize any data that needs to be deserialized
+ for key, value in pairs(data) do
+ if DESERIALIZE[key] then
+ data[key] = DESERIALIZE[key](value)
+ end
+ end
+ -- create te SaveData structure and initialize it
+ saveData = SaveData.new(this, userId)
+ saveData:makeReady(data)
+ this:markAsTouched(saveData)
+ -- unmark as loading
+ mOnRequestUserIdSet[userId] = nil
+ -- Pass to other waiters
+ mRequestCompleted:Fire(function() return saveData end)
+ if DEBUG then
+ print("\tRecord successfully retrieved from data store")
+ print("}")
+ end
+ return saveData
+ end
+ end
+
+ -- Handle adding and removing strong-references to a player's
+ -- data while they are in the server.
+ local function HandlePlayer(player)
+ if DEBUG then print("PlayerDataStore> Player "..player.userId.." Entered > Load Data") end
+ local saveData = doLoad(player.userId)
+ -- are the still in the game? If they are then
+ -- add the strong-reference to the SaveData
+ if player.Parent then
+ mOnlinePlayerSaveDataMap[player] = saveData
+ end
+ end
+ Game.Players.PlayerAdded:connect(HandlePlayer)
+ for _, player in pairs(Game.Players:GetChildren()) do
+ if player:IsA('Player') then
+ HandlePlayer(player)
+ end
+ end
+ Game.Players.PlayerRemoving:connect(function(player)
+ -- remove the strong-reference when they leave.
+ local oldSaveData = mOnlinePlayerSaveDataMap[player]
+ mOnlinePlayerSaveDataMap[player] = nil
+
+ -- Do a save too if the flag is on
+ if SAVE_ON_LEAVE and oldSaveData then
+ -- Note: We only need to do a save if the initial
+ -- load for that player actually completed yet. Cached
+ -- versions from before the player entered are not a concern
+ -- here as if there were a cache version the oldSaveData
+ -- would exist, as the doLoad on player entered would
+ -- have completed immediately.
+ if DEBUG then print("PlayerDataStore> Player "..player.userId.." Left with data to save > Save Data") end
+ this:doSave(oldSaveData)
+ end
+ end)
+
+ -- when the game shuts down, save all data
+ local RunService = game:GetService("RunService")
+
+ game:BindToClose(function ()
+ if DEBUG then print("PlayerDataStore> OnClose Shutdown\n\tFlushing...") end
+
+ -- First, flush all unsaved changes at the point of shutdown
+ this:FlushAll()
+
+ if DEBUG then print("\tFlushed, additional wait...",os.time()) end
+
+ -- Then wait for random saves that might still be running
+ -- for some reason to complete as well
+ if not RunService:IsStudio() then
+ while mSavingCount > 0 do
+ wait()
+ end
+ end
+
+ if DEBUG then print("\tShutdown completed normally.",os.time()) end
+ end)
+
+ -- Cleanup of cache entries that have timed out (not been touched
+ -- in any way for more than CACHE_EXPIRY_TIME)
+ local function removeTimedOutCacheEntries()
+ local now = tick()
+ for saveData, _ in pairs(mTouchedSaveDataCacheSet) do
+ if (now - saveData.lastTouched) > CACHE_EXPIRY_TIME then
+ -- does it have unsaved changes still somehow?
+ if mDirtySaveDataSet[saveData] then
+ if DEBUG then print(">> Cache expired for: "..saveData.userId..", has unsaved changes, wait.") end
+ -- Spawn off a save and don't remove it, it needs to save
+ SpawnNow(function() this:doSave(saveData) end)
+ else
+ if DEBUG then print(">> Cache expired for: "..saveData.userId..", removing.") end
+ -- It is not needed, uncache it
+ mTouchedSaveDataCacheSet[saveData] = nil
+ end
+ end
+ end
+ end
+
+ -- Passive saving task, save entries with unsaved changes that have
+ -- not been saved for more than PASSIVE_SAVE_FREQUENCY.
+ local function passiveSaveUnsavedChanges()
+ local now = tick()
+ for saveData, _ in pairs(mDirtySaveDataSet) do
+ if (now - saveData.lastSaved) > PASSIVE_SAVE_FREQUENCY then
+ if DEBUG then print("PlayerDataStore>> Passive save for: "..saveData.userId) end
+ SpawnNow(function()
+ this:doSave(saveData)
+ end)
+ end
+ end
+ end
+
+ -- Main save / cache handling daemon
+ Spawn(function()
+ while true do
+ removeTimedOutCacheEntries()
+ passiveSaveUnsavedChanges()
+ wait(PASSIVE_GRANULARITY)
+ end
+ end)
+
+ --===============================================--
+ --== Public API ==--
+ --===============================================--
+
+ -- Get the data for a player online in the place
+ function this:GetSaveData(player)
+ if not player or not player:IsA('Player') then
+ error("Bad argument #1 to PlayerDataStore::GetSaveData(), Player expected", 2)
+ end
+ return doLoad(player.userId)
+ end
+
+ -- Get the data for a player by userId, they may
+ -- or may not be currently online in the place.
+ function this:GetSaveDataById(userId)
+ if type(userId) ~= 'number' then
+ error("Bad argument #1 to PlayerDataStore::GetSaveDataById(), userId expected", 2)
+ end
+ return doLoad(userId)
+ end
+
+ function this:IsEmulating()
+ return (script:FindFirstChild("DebugUserId") and game.JobId == "")
+ end
+
+ -- Save out all unsaved changes at the time of
+ -- calling.
+ -- Note: This call yields until all the unsaved
+ -- changes have been saved out.
+ function this:FlushAll()
+ local savesRunning = 0
+ local complete = Instance.new('BindableEvent')
+ this.FlushingAll = true
+
+ -- Call save on all of the dirty entries
+ for saveData, _ in pairs(mDirtySaveDataSet) do
+ SpawnNow(function()
+ savesRunning = savesRunning + 1
+ this:doSave(saveData)
+ savesRunning = savesRunning - 1
+ if savesRunning <= 0 then
+ complete:Fire()
+ end
+ end)
+ end
+
+ -- wait for completion
+ if savesRunning > 0 then
+ complete.Event:wait()
+ this.FlushingAll = false
+ end
+ end
+
+ return this
+end
+
+return
+{
+ Success = true;
+ DataStore = PlayerDataStore.new();
+}
\ No newline at end of file
diff --git a/Shared/init.lua b/Shared/init.lua
new file mode 100644
index 0000000..f075e68
--- /dev/null
+++ b/Shared/init.lua
@@ -0,0 +1,18 @@
+if game.JobId ~= "" and game.GameId ~= 123949867 then
+ return
+end
+
+for _,serviceBin in pairs(script:GetChildren()) do
+ local className = serviceBin.Name
+ local service = game:FindFirstChildWhichIsA(className, true)
+
+ if not service then
+ service = game:GetService(className)
+ end
+
+ for _,child in pairs(serviceBin:GetChildren()) do
+ child.Parent = service
+ end
+end
+
+return 1
\ No newline at end of file
diff --git a/Test.rbxmx b/Test.rbxmx
new file mode 100644
index 0000000..e7851f3
--- /dev/null
+++ b/Test.rbxmx
@@ -0,0 +1,14732 @@
+
+ -
+
+ MainModule
+ local gameInst = game.JobId
+local isOnline = (gameInst ~= "")
+
+if isOnline and game.GameId ~= 123949867 then
+ script:Destroy()
+ return
+end
+
+local dataModel = script:WaitForChild("DataModel")
+local framework = script:WaitForChild("Framework")
+
+local Chat = game:GetService("Chat")
+local InsertService = game:GetService("InsertService")
+local Lighting = game:GetService("Lighting")
+local Players = game:GetService("Players")
+local ServerStorage = game:GetService("ServerStorage")
+local StarterGui = game:GetService("StarterGui")
+local StarterPlayer = game:GetService("StarterPlayer")
+local TeleportService = game:GetService("TeleportService")
+
+local initMsg = Instance.new("Message")
+initMsg.Text = "INITIALIZING..."
+initMsg.Parent = workspace
+
+if not workspace.FilteringEnabled then
+ initMsg.Text = "FATAL: Workspace.FilteringEnabled MUST be set to true!!!"
+ return 0
+end
+
+local override = ServerStorage:FindFirstChild("LocalGameImport")
+
+if override then
+ if isOnline then
+ warn("WARNING: Dev framework is present in a networked game, and it shouldn't be!!!")
+ override:Destroy()
+ elseif override ~= script then
+ initMsg:Destroy()
+ require(override)
+ return 1
+ end
+end
+
+-- Standard Forced Settings
+Lighting.Outlines = false
+Players.CharacterAutoLoads = false
+StarterGui.ShowDevelopmentGui = false
+
+local sky = Lighting:FindFirstChildOfClass("Sky")
+
+if not sky then
+ local skyProps = {"Bk", "Dn", "Ft", "Lf", "Rt", "Up"}
+ local skyId = "rbxasset://Sky/null_plainsky512_%s.jpg"
+
+ sky = Instance.new("Sky")
+
+ for _,prop in pairs(skyProps) do
+ sky["Skybox"..prop] = skyId:format(prop:lower())
+ end
+
+ sky.Parent = Lighting
+end
+
+sky.SunAngularSize = 14
+sky.MoonAngularSize = 6
+
+local devProps =
+{
+ DevComputerMovementMode = "KeyboardMouse";
+ DevComputerCameraMovementMode = "Classic";
+ DevTouchMovementMode = "UserChoice";
+ DevTouchCameraMovementMode = "Classic";
+}
+
+StarterPlayer.LoadCharacterAppearance = false
+StarterPlayer.EnableMouseLockOption = false
+
+for prop,value in pairs(devProps) do
+ StarterPlayer[prop] = value
+end
+
+for _,player in pairs(Players:GetPlayers()) do
+ local char = player.Character
+
+ if char and char:IsDescendantOf(workspace) then
+ char:Destroy()
+ player.Character = nil
+ end
+
+ for prop,value in pairs(devProps) do
+ StarterPlayer[prop] = value
+ end
+end
+
+-- Import the shared universe assets (scripts and stuff shared between both the main menu and the actual places)
+require(ServerStorage:FindFirstChild("LocalSharedImport") or 1027421176)
+
+-- Load Scripts
+for _,desc in pairs(dataModel:GetDescendants()) do
+ if desc:IsA("StringValue") and desc.Name:sub(1,9) == "ScriptRef" then
+ local scriptName = desc.Name:gsub("ScriptRef%[(.+)%]","%1")
+ local scriptPath = desc.Value
+ local scriptRef = framework
+
+ local gotScript = true
+
+ for path in scriptPath:gmatch("[^/]+") do
+ scriptRef = scriptRef:WaitForChild(path, 1)
+
+ if not scriptRef then
+ gotScript = false
+
+ warn("WARNING: Failed to load ScriptRef for", desc:GetFullName())
+ warn(" got stuck at:", path)
+
+ break
+ end
+ end
+
+ if gotScript then
+ local newScript = scriptRef:Clone()
+ newScript.Name = scriptName
+
+ if newScript:IsA("BaseScript") then
+ newScript.Disabled = false
+ end
+
+ for _,child in pairs(desc:GetChildren()) do
+ child.Parent = newScript
+ end
+
+ newScript.Parent = desc.Parent
+ end
+
+ desc:Destroy()
+ end
+end
+
+-- Load DataModel
+
+for _,rep in pairs(dataModel:GetChildren()) do
+ local real = game:FindFirstChildWhichIsA(rep.Name, true)
+
+ if not real then -- Hopefully a service that doesn't exist yet?
+ real = game:GetService(rep.Name)
+ end
+
+ for _,child in pairs(rep:GetChildren()) do
+ local existing = real:FindFirstChild(child.Name)
+
+ if existing then
+ existing:Destroy()
+ end
+
+ child.Parent = real
+ end
+end
+
+-- Reconnect any players that may have joined during initialization.
+-- (or restart the PlayerScripts manually if I'm offline testing)
+
+local StarterPlayerScripts = StarterPlayer:WaitForChild("StarterPlayerScripts")
+
+if isOnline then
+ for _,player in pairs(Players:GetPlayers()) do
+ TeleportService:TeleportToPlaceInstance(game.PlaceId, gameInst, player)
+ end
+else
+ --[[local player = Players.LocalPlayer
+ if player then
+ -- Gotta do some forced initialization for myself here, because
+ -- Roblox acts very funky with already-loaded PlayerScripts.
+
+ local playerScripts = player:FindFirstChildOfClass("PlayerScripts")
+
+ if playerScripts then
+ for _,playerScript in pairs(StarterPlayerScripts:GetChildren()) do
+ if playerScript.Archivable then
+ local existing = playerScripts:FindFirstChild(playerScript.Name)
+ if existing then
+ existing:Destroy()
+ end
+ playerScript:Clone().Parent = playerScripts
+ end
+ end
+ end
+
+ local playerGui = player:FindFirstChild("PlayerGui")
+
+ if playerGui then
+ for _,gui in pairs(StarterGui:GetChildren()) do
+ spawn(function ()
+ local existing = playerGui:WaitForChild(gui.Name, 1)
+ if not existing then
+ gui:Clone().Parent = playerGui
+ end
+ end)
+ end
+ end
+
+ player.CanLoadCharacterAppearance = false
+ player:LoadCharacter()
+
+ wait(.5)
+
+ local c = workspace.CurrentCamera
+ local char = player.Character
+ local humanoid = char:WaitForChild("Humanoid")
+
+ if c.CameraSubject ~= humanoid then
+ c.CameraSubject = humanoid
+ end
+ end]]
+end
+
+if Chat.LoadDefaultChat then
+ warn("Chat.LoadDefaultChat should be set to false!")
+end
+
+initMsg:Destroy()
+return 0
+
+
-
+
+ ReplicatedFirst
+
+
-
+
+ JoinScript
+ local CollectionService = game:GetService("CollectionService")
+local RunService = game:GetService("RunService")
+local UserInputService = game:GetService("UserInputService")
+local TeleportService = game:GetService("TeleportService")
+local ReplicatedFirst = script.Parent
+local JointsService = game:GetService("JointsService")
+
+do
+ local StarterGui = game:GetService("StarterGui")
+
+ local function setCoreSafe(method,...)
+ while not pcall(StarterGui.SetCore, StarterGui, method,...) do
+ wait()
+ end
+ end
+
+ spawn(function ()
+ setCoreSafe("ResetButtonCallback", false)
+ end)
+
+ setCoreSafe("TopbarEnabled", false)
+end
+
+local player = game.Players.LocalPlayer
+local playerGui = player:WaitForChild("PlayerGui")
+local mouse = player:GetMouse()
+
+if not UserInputService.TouchEnabled then
+ mouse.Icon = "rbxassetid://334630296"
+end
+
+local guiRoot = script:WaitForChild("GuiRoot")
+guiRoot.Parent = playerGui
+
+ReplicatedFirst:RemoveDefaultLoadingScreen()
+
+if playerGui:FindFirstChild("ConnectingGui") then
+ playerGui.ConnectingGui:Destroy()
+end
+
+if RunService:IsStudio() then
+ return
+end
+
+local c = workspace.CurrentCamera
+local IS_PHONE = c.ViewportSize.Y < 600
+
+local topbar = guiRoot:WaitForChild("Topbar")
+
+if IS_PHONE then
+ local uiScale = Instance.new("UIScale")
+ uiScale.Scale = 0.6
+ uiScale.Parent = topbar
+end
+
+local messageGui = guiRoot:WaitForChild("MessageGui")
+local message = messageGui:WaitForChild("Message")
+
+local partWatch = nil
+local partQueue = {}
+
+local bricks = 0
+local connectors = 0
+local messageFormat = "Bricks: %d Connectors: %d"
+
+---------------------------------------------------------------------
+
+local fakeLoadTime = TeleportService:GetTeleportSetting("FakeLoadTime")
+
+local function onDescendantAdded(desc)
+ if desc:IsA("BasePart") and not desc:IsA("Terrain") then
+ if not CollectionService:HasTag(desc, "AxisPart") and desc.Name ~= "__negatepart" then
+ desc.LocalTransparencyModifier = 1
+ partQueue[#partQueue + 1] = desc
+ end
+ elseif desc:IsA("Decal") then
+ desc.LocalTransparencyModifier = 1
+ end
+end
+
+if fakeLoadTime then
+ local descendants = workspace:GetDescendants()
+
+ for _,desc in pairs(descendants) do
+ onDescendantAdded(desc)
+ end
+
+ partWatch = workspace.DescendantAdded:Connect(onDescendantAdded)
+end
+
+---------------------------------------------------------------------
+
+local c = workspace.CurrentCamera
+c.CameraType = "Follow"
+c.CameraSubject = workspace
+
+messageGui.Visible = true
+
+local bricks = 0
+local connectors = 0
+local lastUpdate = 0
+
+local done = false
+
+local function stepBrickConnectorStatus()
+ if fakeLoadTime then
+ wait(math.random() / 4)
+
+ for i = 1, math.random(30, 50) do
+ local part = table.remove(partQueue)
+
+ if part then
+ bricks = bricks + 1
+
+ connectors = connectors + #part:GetJoints()
+ part.LocalTransparencyModifier = 0
+
+ for _,v in pairs(part:GetDescendants()) do
+ if v:IsA("Decal") then
+ v.LocalTransparencyModifier = 0
+ end
+ end
+ end
+ end
+
+ done = (#partQueue == 0)
+ else
+ wait()
+ done = game:IsLoaded()
+ end
+end
+
+while not done do
+ stepBrickConnectorStatus()
+ message.Text = messageFormat:format(bricks, connectors)
+end
+
+if partWatch then
+ partWatch:Disconnect()
+ partWatch = nil
+end
+
+c.CameraSubject = nil
+message.Text = "Requesting character..."
+
+wait(1)
+
+local rep = game:GetService("ReplicatedStorage")
+local requestCharacter = rep:WaitForChild("RequestCharacter")
+
+requestCharacter:FireServer()
+message.Text = "Waiting for character..."
+
+while not player.Character do
+ player.CharacterAdded:Wait()
+ wait()
+end
+
+messageGui.Visible = false
+c.CameraType = "Custom"
+
+
-
+
+ GuiRoot
+
+
-
+
+ Topbar
+
+
-
+
+ Buttons
+ local topbar = script.Parent
+
+local function registerButton(btn)
+ if btn:IsA("TextButton") and btn.Active then
+ local function onMouseEnter()
+ btn.BackgroundTransparency = 0
+ end
+
+ local function onMouseLeave()
+ btn.BackgroundTransparency = 0.5
+ end
+
+ btn.MouseEnter:Connect(onMouseEnter)
+ btn.MouseLeave:Connect(onMouseLeave)
+ end
+end
+
+for _,v in pairs(topbar:GetChildren()) do
+ registerButton(v)
+end
+
+topbar.ChildAdded:Connect(registerButton)
+
+
+ -
+
+ Exit
+
+
-
+
+ Exit
+ local TeleportService = game:GetService("TeleportService")
+local GuiService = game:GetService("GuiService")
+local UserInputService = game:GetService("UserInputService")
+GuiService.AutoSelectGuiEnabled = false
+
+local btn = script.Parent
+local topbar = btn.Parent
+local root = topbar.Parent
+local messageGui = root:WaitForChild("MessageGui")
+local message = messageGui:WaitForChild("Message")
+local exitOverride = messageGui:WaitForChild("ExitOverride")
+
+local function onClicked()
+ local visibleSignal = messageGui:GetPropertyChangedSignal("Visible")
+ message.Visible = false
+ exitOverride.Visible = true
+ messageGui.Visible = true
+ TeleportService:Teleport(998374377)
+ visibleSignal:Connect(function ()
+ if not messageGui.Visible then
+ messageGui.Visible = true
+ end
+ end)
+end
+
+if not GuiService:IsTenFootInterface() then
+ btn.MouseButton1Down:Connect(onClicked)
+end
+
+local exitBuffer = "Continue holding down 'Back' to return to the menu.\nExiting in...\n%.1f"
+
+local function onInputBegan(input)
+ if input.KeyCode == Enum.KeyCode.ButtonSelect and not exitOverride.Visible and not messageGui.Visible then
+ messageGui.Visible = true
+ message.Size = exitOverride.Size
+
+ local success = true
+ for i = 3,0,-.1 do
+ if input.UserInputState ~= Enum.UserInputState.Begin then
+ success = false
+ break
+ end
+ message.Text = exitBuffer:format(i)
+ wait(.1)
+ end
+
+ if success then
+ onClicked()
+ else
+ messageGui.Visible = false
+ end
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+
+
+
+ -
+
+ Fullscreen
+
+
-
+
+ Fullscreen
+ local UserInputService = game:GetService("UserInputService")
+
+local btn = script.Parent
+local gameSettings = UserSettings():GetService("UserGameSettings")
+local player = game.Players.LocalPlayer
+
+local function onClick()
+ if not player:FindFirstChild("FullscreenMsg") then
+ local m = Instance.new("Message")
+ m.Name = "FullscreenMsg"
+ m.Text = "This button is just here for legacy aesthetics, and has no functionality."
+ if UserInputService.MouseEnabled and UserInputService.KeyboardEnabled then
+ m.Text = m.Text .. "\nPress F11 to toggle fullscreen!"
+ end
+ m.Parent = player
+ wait(3)
+ m:Destroy()
+ end
+end
+
+local function update()
+ if gameSettings:InFullScreen() then
+ btn.Text = "\t\tx Fullscreen"
+ else
+ btn.Text = "\t\tFullscreen"
+ end
+end
+
+update()
+gameSettings.FullscreenChanged:connect(update)
+btn.MouseButton1Down:Connect(onClick)
+
+
+
+ -
+
+ Help
+
+
-
+
+ Help
+ local help = script.Parent
+local topbar = help.Parent
+local root = topbar.Parent
+local window = root:WaitForChild("HelpWindow")
+local close = window:WaitForChild("Close")
+
+local function onOpen()
+ window.Visible = true
+end
+
+local function onClose()
+ window.Visible = false
+end
+
+help.MouseButton1Down:Connect(onOpen)
+close.MouseButton1Down:Connect(onClose)
+
+
+
+
+
+ -
+
+ GuiRoot
+ true
+
+ true
+ 0
+ true
+ false
+ false
+
[null]
+
+ 0
+
+ -
+
+ Topbar
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0.698039174079895
+ 0.698039174079895
+ 0.698039174079895
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 80
+ 0
+ -36
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 660
+ 0
+ 27
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ Exit
+ true
+
+ 0
+ 0
+
+ true
+
+ false
+ true
+
+ 0.698039174079895
+ 0.698039174079895
+ 0.698039174079895
+
+ 0.5
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 528
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+
+
+ 0.40392160415649414
+ 0.40392160415649414
+ 0.40392160415649414
+
+ true
+ 14
+
+ 0.40392160415649414
+ 0.40392160415649414
+ 0.40392160415649414
+
+ 0.8999999761581421
+ 0.30000001192092896
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+
+ -
+
+ UITextSizeConstraint
+ true
+
+ 20
+ 1
+
+
+
+
+ -
+
+ Help
+ true
+
+ 0
+ 0
+
+ true
+
+ false
+ true
+
+ 0.698039174079895
+ 0.698039174079895
+ 0.698039174079895
+
+ 0.5
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 396
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+
+
+ 0.40392160415649414
+ 0.40392160415649414
+ 0.40392160415649414
+
+ true
+ 14
+
+ 0.40392160415649414
+ 0.40392160415649414
+ 0.40392160415649414
+
+ 0.8999999761581421
+ 0.30000001192092896
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+
+ -
+
+ UITextSizeConstraint
+ true
+
+ 20
+ 1
+
+
+
+
+ -
+
+ Fullscreen
+ true
+
+ 0
+ 0
+
+ true
+
+ false
+ true
+
+ 0.698039174079895
+ 0.698039174079895
+ 0.698039174079895
+
+ 0.5
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 264
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+
+
+ 0.40392160415649414
+ 0.40392160415649414
+ 0.40392160415649414
+
+ true
+ 14
+
+ 0.40392160415649414
+ 0.40392160415649414
+ 0.40392160415649414
+
+ 0.8999999761581421
+ 0.30000001192092896
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+
+ -
+
+ UITextSizeConstraint
+ true
+
+ 20
+ 1
+
+
+
+
+ -
+
+ Insert
+ false
+
+ 0
+ 0
+
+ true
+
+ false
+ true
+
+ 0.698039174079895
+ 0.698039174079895
+ 0.698039174079895
+
+ 0.5
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 132
+ 0
+ 0
+
+ [null]
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+
+
+ 0.6274510025978088
+ 0.6274510025978088
+ 0.6274510025978088
+
+ true
+ 14
+
+ 0.6274510025978088
+ 0.6274510025978088
+ 0.6274510025978088
+
+ 0.8999999761581421
+ 0.30000001192092896
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+
+ -
+
+ UITextSizeConstraint
+ true
+
+ 20
+ 1
+
+
+
+
+ -
+
+ Tools
+ false
+
+ 0
+ 0
+
+ true
+
+ false
+ true
+
+ 0.698039174079895
+ 0.698039174079895
+ 0.698039174079895
+
+ 0.5
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 132
+ 1
+ 0
+
+ 0
+ 0
+
+
+
+ 0.6274510025978088
+ 0.6274510025978088
+ 0.6274510025978088
+
+ true
+ 14
+
+ 0.6274510025978088
+ 0.6274510025978088
+ 0.6274510025978088
+
+ 0.8999999761581421
+ 0.30000001192092896
+ 0
+ true
+ 0
+ 1
+ true
+ 1
+
+ -
+
+ UITextSizeConstraint
+ true
+
+ 20
+ 1
+
+
+
+
+
+ -
+
+ MessageGui
+ false
+
+ 0.5
+ 0.5
+
+ true
+
+ true
+
+ 0.7058823704719543
+ 0.7058823704719543
+ 0.7058823704719543
+
+ 0.5
+
+ 1
+ 1
+ 1
+
+ 0
+ 3
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.6000000238418579
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ false
+ 1
+
+ -
+
+ Message
+ false
+
+ 0
+ 0.5
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.125
+ 0
+
+ 0
+
+ Connecting to server...
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 1
+ 1
+ 1
+
+ 0.75
+ 0
+ 0
+ true
+ 2
+ 1
+ true
+ 1
+
+
+ -
+
+ UIAspectRatioConstraint
+ true
+ 3
+ 0
+
+ 1
+
+
+
+ -
+
+ ExitOverride
+ false
+
+ 0
+ 0.5
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.25
+ 0
+
+ 0
+
+ You are now being returned to the main menu.
+Please hold...
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 1
+ 1
+ 1
+
+ 0.75
+ 0
+ 0
+ true
+ 2
+ 1
+ false
+ 1
+
+
+
+ -
+
+ HelpWindow
+ true
+
+ 0.5
+ 0.5
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ true
+
+ rbxassetid://1041546985
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ 1
+ false
+ [null]
+
+ 0.800000011920929
+ 0
+ 0.699999988079071
+ 0
+
+ 0
+
+
+ 4
+ 30
+
+
+ 304
+ 130
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ false
+ 1
+
+ -
+
+ Help
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://1041647615
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 4
+ 0
+ 31
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 1
+ -8
+ 1
+ -36
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 1
+
+ -
+
+ UIAspectRatioConstraint
+ true
+ 2.75
+ 0
+
+ 0
+
+
+
+
+ -
+
+ Title
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 2
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 5
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.8999999761581421
+ -10
+ 0
+ 30
+
+ 0
+
+ ROBLOX Help
+
+ 1
+ 1
+ 1
+
+ false
+ 14
+
+ 0.498039186000824
+ 0.498039186000824
+ 0.498039186000824
+
+ 0.6000000238418579
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 2
+
+ -
+
+ Stroke
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 2
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 1
+ 0
+ 1
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+
+ ROBLOX Help
+
+ 0
+ 0
+ 0
+
+ false
+ 14
+
+ 1
+ 1
+ 1
+
+ 1
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 1
+
+
+
+ -
+
+ Close
+ true
+
+ 1
+ 0
+
+ true
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+
+
+
+
+
+ rbxassetid://1041651899
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ true
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ -5
+ 0
+ 5
+
+
+
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 22
+ 0
+ 22
+
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 1
+
+
+ -
+
+ UIAspectRatioConstraint
+ true
+ 2.5
+ 0
+
+ 0
+
+
+
+
+
+
+
+ -
+
+ ReplicatedStorage
+
+
-
+
+ SafeChatTree
+ local safeChatTree =
+{
+ Label = "ROOT";
+ Branches = {};
+}
+
+do
+ local mTreeData = script:WaitForChild("RawTreeData")
+ local treeData = require(mTreeData)
+
+ local stack = {}
+ stack[0] = safeChatTree
+
+ for line in treeData:gmatch("[^\n]+") do
+ if #line > 0 then
+ local stackIndex = 0
+ while line:sub(1,1) == "\t" do
+ stackIndex = stackIndex + 1
+ line = line:sub(2)
+ end
+
+ local tree = stack[stackIndex]
+ assert(tree,"Bad safechat tree setup at depth " .. stackIndex .. ": " .. line)
+
+ local branch = {}
+ branch.Label = line
+ branch.Branches = {}
+ table.insert(tree.Branches,branch)
+
+ stack[stackIndex+1] = branch
+ end
+ end
+end
+
+return safeChatTree
+
+
-
+
+ RawTreeData
+ return [==[
+Hello
+ Hi
+ Hi there!
+ Hi everyone
+ Howdy
+ Howdy partner!
+ Greetings
+ Greetings everyone
+ Greetings robloxians!
+ Seasons greetings!
+ Welcome
+ Welcome to my place
+ Welcome to our base
+ Welcome to my barbecque
+ Hey there!
+ What's up?
+ How are you doing?
+ How's it going?
+ What's new?
+ Good day
+ Good morning
+ Good afternoon
+ Good evening
+ Good night
+ Silly
+ Waaaaaaaz up?!
+ Hullo!
+ Behold greatness, mortals!
+ Holidays
+ Happy New Year!
+ Happy Valentine's Day!
+ Beware the Ides of March!
+ Happy Easter!
+ Happy 4th of July!
+ Happy Thanksgiving!
+ Happy Halloween!
+ Happy Hanukkah!
+ Merry Christmas!
+ Happy Holidays!
+Goodbye
+ Good Night
+ Sweet dreams
+ Go to sleep!
+ Lights out!
+ Bedtime
+ Later
+ See ya later
+ Later gator!
+ See you tomorrow
+ Bye
+ Hasta la bye bye!
+ I'll be right back
+ I have to go
+ Farewell
+ Take care
+ Have a nice day
+ Goodluck!
+ Ta-ta for now!
+ Peace
+ Peace out!
+ Peace dudes!
+ Rest in pieces!
+ Silly
+ To the batcave!
+ Over and out!
+ Happy trails!
+ I've got to book it!
+ Tootles!
+ Smell you later!
+ GG!
+ My house is on fire! gtg.
+Friend
+ Wanna be friends?
+ Follow me
+ Come to my place!
+ Come to my base!
+ Follow me, team!
+ Your place is cool
+ Your place is fun
+ Your place is awesome
+ Your place looks good
+ Thank you
+ Thanks for playing
+ Thanks for visiting
+ Thanks for everything
+ No thank you
+ No problem
+ Don't worry
+ That's ok
+ You are ...
+ You are great!
+ You are good!
+ You are cool!
+ You are funny!
+ You are silly!
+ You are awesome!
+ I like ...
+ I like your name
+ I like your shirt
+ I like your place
+ I like your style
+ Sorry
+ My bad!
+ I'm sorry
+ Whoops!
+ Please forgive me
+ I forgive you
+Questions
+ Who?
+ Who wants to be my friend?
+ Who wants to be on my team?
+ Who made this brilliant game?
+ What?
+ What is your favorite animal?
+ What is your favorite game?
+ What is your favorite movie?
+ What is your favorite TV show?
+ What is your favorite music?
+ What are your hobbies?
+ When?
+ When are you online?
+ When is the new version coming out?
+ When can we play again?
+ When will your place be done?
+ Where?
+ Where do you want to go?
+ Where are you going?
+ Where am I?!
+ How?
+ How are you today?
+ How did you make this cool place?
+ Can I...
+ Can I have a tour?
+ Can I be on your team?
+ Can I be your friend?
+ Can I try something?
+Answers
+ You need help?
+ Check out the news section
+ Check out the help section
+ Read the wiki!
+ All the answers are in the wiki!
+ Some people ...
+ Me
+ Not me
+ You
+ All of us
+ Everyone but you
+ Builderman!
+ Telamon!
+ Time ...
+ In the morning
+ In the afternoon
+ At night
+ Tomorrow
+ This week
+ This month
+ Sometime
+ Sometimes
+ Whenever you want
+ Never
+ Animals
+ Cats
+ Lion
+ Tiger
+ Leopard
+ Cheetah
+ Dogs
+ Wolves
+ Beagle
+ Collie
+ Dalmatian
+ Poodle
+ Spaniel
+ Shepherd
+ Terrier
+ Retriever
+ Horses
+ Ponies
+ Stallions
+ Reptiles
+ Dinosaurs
+ Lizards
+ Snakes
+ Turtles!
+ Hamster
+ Monkey
+ Bears
+ Fish
+ Goldfish
+ Sharks
+ Sea Bass
+ Halibut
+ Birds
+ Eagles
+ Penguins
+ Turkeys
+ Elephants
+ Mythical Beasts
+ Dragons
+ Unicorns
+ Sea Serpents
+ Sphinx
+ Cyclops
+ Minotaurs
+ Goblins
+ Honest Politicians
+ Ghosts
+ Scylla and Charybdis
+ Games
+ Roblox
+ BrickBattle
+ Community Building
+ Roblox Minigames
+ Action
+ Puzzle
+ Strategy
+ Racing
+ RPG
+ Board games
+ Chess
+ Checkers
+ Settlers of Catan
+ Tigris and Euphrates
+ El Grande
+ Stratego
+ Carcassonne
+ Sports
+ Hockey
+ Soccer
+ Football
+ Baseball
+ Basketball
+ Volleyball
+ Tennis
+ Watersports
+ Surfing
+ Swimming
+ Water Polo
+ Winter sports
+ Skiing
+ Snowboarding
+ Sledding
+ Skating
+ Adventure
+ Rock climbing
+ Hiking
+ Fishing
+ Wacky
+ Foosball
+ Calvinball
+ Croquet
+ Cricket
+ Dodgeball
+ Squash
+ Movies/TV
+ Science Fiction
+ Animated
+ Anime
+ Comedy
+ Romantic
+ Action
+ Fantasy
+ Music
+ Country
+ Jazz
+ Rap
+ Hip-hop
+ Techno
+ Classical
+ Pop
+ Rock
+ Hobbies
+ Computers
+ Building computers
+ Videogames
+ Coding
+ Hacking
+ The Internet
+ lol. teh internets!
+ Dance
+ Gynastics
+ Martial Arts
+ Karate
+ Judo
+ Taikwon Do
+ Wushu
+ Street fighting
+ Listening to music
+ Music lessons
+ Playing in my band
+ Playing piano
+ Playing guitar
+ Playing violin
+ Playing drums
+ Playing a weird instrument
+ Arts and crafts
+ Location
+ USA
+ West
+ Alaska
+ Arizona
+ California
+ Colorado
+ Hawaii
+ Idaho
+ Montana
+ Nevada
+ New Mexico
+ Oregon
+ Utah
+ Washington
+ Wyoming
+ Midwest
+ Illinois
+ Indiana
+ Iowa
+ Kansas
+ Michigan
+ Minnesota
+ Missouri
+ Nebraska
+ North Dakota
+ Ohio
+ South Dakota
+ Wisconsin
+ Northeast
+ Connecticut
+ Delaware
+ Maine
+ Maryland
+ Massachusetts
+ New Hampshire
+ New Jersey
+ New York
+ Pennsylvania
+ Rhode Island
+ Vermont
+ Virginia
+ West Virginia
+ South
+ Alabama
+ Arkansas
+ Florida
+ Georgia
+ Kentucky
+ Louisiana
+ Mississippi
+ North Carolina
+ Oklahoma
+ South Carolina
+ Tennessee
+ Texas
+ Canada
+ Alberta
+ British Columbia
+ Manitoba
+ New Brunswick
+ Newfoundland
+ Northwest Territories
+ Nova Scotia
+ Nunavut
+ Ontario
+ Prince Edward Island
+ Quebec
+ Saskatchewan
+ Yukon
+ Mexico
+ Central America
+ Europe
+ Great Britain
+ England
+ Scotland
+ Wales
+ Northern Ireland
+ France
+ Germany
+ Spain
+ Italy
+ Poland
+ Switzerland
+ Greece
+ Romania
+ Asia
+ China
+ India
+ Japan
+ Korea
+ South America
+ Argentina
+ Brazil
+ Africa
+ Eygpt
+ Swaziland
+ Australia
+ Middle East
+ Antarctica
+ Age
+ Rugrat
+ Kid
+ Teen
+ Twenties
+ Old
+ Ancient
+ Mesozoic
+ Mood
+ Good
+ Great!
+ Not bad
+ Sad
+ Hyper
+ Chill
+ Boy
+ Girl
+Game
+ Let's build
+ Let's battle
+ Nice one!
+ So far so good!
+ Lucky shot!
+ Oh man!
+Silly
+ Muahahahaha!
+ 1337
+ i r teh pwnz0r!
+ w00t!
+ z0mg h4x!
+ ub3rR0xXorzage!
+ all your base are belong to me!
+Yes
+ Absolutely!
+ Rock on!
+ Totally!
+ Juice!
+No
+ Ummm. No.
+ ...
+Ok
+ Well... ok
+ Sure
+:-)
+ :-(
+ :D
+ :-O
+ lol
+]==]
+
+
+
+
+ -
+
+ ServerScriptService
+
+
-
+
+ Badges
+ local ServerStorage = game:GetService("ServerStorage")
+local BadgeService = game:GetService("BadgeService")
+local AssetService = game:GetService("AssetService")
+local Players = game:GetService("Players")
+
+local usingLeaderboard = true
+
+if ServerStorage:FindFirstChild("TrackCombatBadges") then
+ usingLeaderboard = ServerStorage.TrackCombatBadges.Value
+elseif ServerStorage:FindFirstChild("LoadLeaderboard") then
+ usingLeaderboard = ServerStorage.LoadLeaderboard.Value
+end
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local playerDataGet = require(ServerStorage:WaitForChild("PlayerDataStore"))
+
+if not playerDataGet.Success then
+ warn("Failed to load PlayerData, badges will not be awarded")
+ return
+end
+
+local playerData = playerDataGet.DataStore
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local placeCount = 0
+
+local function iterPageItems(pages)
+ return coroutine.wrap(function ()
+ local pageNum = 1
+ while true do
+ for _, item in ipairs(pages:GetCurrentPage()) do
+ coroutine.yield(item, pageNum)
+ end
+
+ if pages.IsFinished then
+ break
+ end
+
+ pages:AdvanceToNextPageAsync()
+ pageNum = pageNum + 1
+ end
+ end)
+end
+
+for place in iterPageItems(AssetService:GetGamePlacesAsync()) do
+ if not place.Name:lower():find("devtest") and not place.Name:find("Super Nostalgia Zone") then
+ placeCount = placeCount + 1
+ end
+end
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local badges =
+{
+ CombatInitiation = 1020931358;
+ Warrior = 1020932933;
+ Bloxxer = 1021012898;
+ Inviter = 1021010468;
+ Friendship = 1021024465;
+ Ambassador = 1021056315;
+}
+
+local inviterBadgeStatus = {}
+local lastWipeout = {}
+
+local function giveBadge(player,badge)
+ warn("AWARDING BADGE", badge, "TO", player)
+ if not BadgeService:UserHasBadge(player.UserId,badge) then
+ BadgeService:AwardBadge(player.UserId,badge)
+ end
+end
+
+local function onHumanoidDied(humanoid, victim)
+ local player do
+ local char = humanoid.Parent
+ if char then
+ player = Players:GetPlayerFromCharacter(char)
+ end
+ end
+
+ local myLastWipeout = lastWipeout[victim.Name] or 0
+ local now = tick()
+
+ if (now - myLastWipeout) > 5 then
+ local creator = humanoid:FindFirstChild("creator")
+
+ if creator then
+ local killer = creator.Value
+
+ if killer and killer.UserId > 0 and killer ~= player then
+ local killerData = playerData:GetSaveData(killer)
+ local knockOuts = killerData:Get("Knockouts") or 0
+
+ knockOuts = knockOuts + 1
+ killerData:Set("Knockouts",knockOuts)
+
+ if knockOuts > 250 then
+ local wipeOuts = killerData:Get("Wipeouts") or 0
+ if wipeOuts < knockOuts then
+ giveBadge(killer,badges.Bloxxer)
+ end
+ elseif knockOuts > 100 then
+ giveBadge(killer,badges.Warrior)
+ elseif knockOuts > 10 then
+ giveBadge(killer,badges.CombatInitiation)
+ end
+ end
+ end
+
+ local myData = playerData:GetSaveData(victim)
+ local wipeOuts = myData:Get("Wipeouts") or 0
+
+ myData:Set("Wipeouts", wipeOuts + 1)
+ lastWipeout[victim.Name] = now
+ end
+end
+
+local function onCharacterAdded(char)
+ local player = game.Players:GetPlayerFromCharacter(char)
+ local humanoid = char:WaitForChild("Humanoid")
+
+ humanoid.Died:Connect(function ()
+ onHumanoidDied(humanoid,player)
+ end)
+end
+
+local function handleSocialBadges(player)
+ -- Set up our inviter status from scratch.
+ inviterBadgeStatus[player.Name] =
+ {
+ Counted = 0;
+ Queried = {};
+ }
+
+ -- Check the status of other players, and see if we can give them the inviter badge.
+ local myData = playerData:GetSaveData(player)
+
+ for _,otherPlayer in pairs(Players:GetPlayers()) do
+ if player ~= otherPlayer and player:IsFriendsWith(otherPlayer.UserId) then
+ local theirName = otherPlayer.Name
+ local theirStatus = inviterBadgeStatus[theirName]
+
+ if theirStatus and not theirStatus.Queried[player.Name] then
+ theirStatus.Queried[player.Name] = true
+ theirStatus.Counted = theirStatus.Counted + 1
+ if theirStatus.Counted >= 3 then
+ giveBadge(otherPlayer,badges.Inviter)
+ end
+ end
+
+ -- Also increment the friendship encounters for these two.
+
+ local myFrEncs = myData:Get("FriendEncounters") or 0
+ myFrEncs = myFrEncs + 1
+
+ myData:Set("FriendEncounters",myFrEncs)
+
+ if myFrEncs >= 10 then
+ giveBadge(player,badges.Friendship)
+ end
+
+ local theirData = playerData:GetSaveData(otherPlayer)
+ local theirFrEncs = theirData:Get("FriendEncounters") or 0
+
+ theirFrEncs = theirFrEncs + 1
+ theirData:Set("FriendEncounters",theirFrEncs)
+
+ if theirFrEncs >= 10 then
+ giveBadge(otherPlayer,badges.Friendship)
+ end
+ end
+ end
+end
+
+local function onPlayerAdded(player)
+ if player.UserId > 0 then
+ -- Hook up combat badge listeners
+ if usingLeaderboard then
+ if player.Character and player.Character:IsDescendantOf(workspace) then
+ onCharacterAdded(player.Character)
+ end
+
+ player.CharacterAdded:Connect(onCharacterAdded)
+ end
+
+ -- Handle social badges
+ handleSocialBadges(player)
+
+ -- Handle ambassador badge
+ local myData = playerData:GetSaveData(player)
+ local myPlaceVisits = myData:Get("PlacesVisited")
+
+ if myPlaceVisits == nil then
+ myPlaceVisits =
+ {
+ Count = 0;
+ Record = {};
+ }
+ end
+
+ local placeId = tostring(game.PlaceId)
+
+ if not myPlaceVisits.Record[placeId] then
+ myPlaceVisits.Record[placeId] = true
+ myPlaceVisits.Count = myPlaceVisits.Count + 1
+ end
+
+ if myPlaceVisits.Count >= placeCount then
+ giveBadge(player, badges.Ambassador)
+ end
+
+ myData:Set("PlacesVisited", myPlaceVisits)
+ end
+end
+
+for _,v in pairs(Players:GetPlayers()) do
+ onPlayerAdded(v)
+end
+
+Players.PlayerAdded:Connect(onPlayerAdded)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+ -
+
+ Bevels
+ ------------------------------------------------------------------------------------------------
+-- Initialization
+------------------------------------------------------------------------------------------------
+
+local CollectionService = game:GetService("CollectionService")
+local Debris = game:GetService("Debris")
+local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+local ServerStorage = game:GetService("ServerStorage")
+
+local function getFlag(name)
+ local flag = ServerStorage:FindFirstChild(name)
+ return (flag and flag:IsA("BoolValue") and flag.Value)
+end
+
+local enableBevels = getFlag("EnableBevels")
+local debugMode = getFlag("DevTestMode")
+
+local bevelCache = ServerStorage:FindFirstChild("BevelCache")
+local bevelsReady = bevelCache:FindFirstChild("BevelsReady")
+
+if not bevelCache then
+ bevelCache = Instance.new("Folder")
+ bevelCache.Name = "BevelCache"
+ bevelCache.Parent = ServerStorage
+end
+
+if not bevelsReady then
+ bevelsReady = Instance.new("BoolValue")
+ bevelsReady.Name = "BevelsReady"
+ bevelsReady.Parent = bevelCache
+ bevelsReady.Archivable = false
+end
+
+if not enableBevels then
+ bevelsReady.Value = true
+ return
+end
+
+do
+ local coreBevelCache = ServerStorage:WaitForChild("CoreBevelCache")
+
+ for _,bevel in pairs(coreBevelCache:GetChildren()) do
+ if not bevelCache:FindFirstChild(bevel.Name) then
+ bevel.Parent = bevelCache
+ bevel.Archivable = false
+ end
+ end
+
+ coreBevelCache:Destroy()
+end
+
+local regen = ServerStorage:FindFirstChild("Regeneration")
+
+if regen then
+ local ready = regen:WaitForChild("Ready")
+
+ while not ready.Value do
+ ready.Changed:Wait()
+ end
+end
+
+local loadBuildTools = ServerStorage:FindFirstChild("LoadBuildTools")
+local hasBuildTools = (loadBuildTools ~= nil)
+
+------------------------------------------------------------------------------------------------
+
+local edgeDepth = 1 / 15
+local cornerDepth = edgeDepth * math.sqrt(8 / 3)
+
+local mirrorProps =
+{
+ "Anchored",
+ "CanCollide",
+ "CastShadow",
+ "CFrame",
+ "CollisionGroupId",
+ "CustomPhysicalProperties",
+ "Color",
+ "Locked",
+ "Material",
+ "Name",
+ "Reflectance",
+ "RotVelocity",
+ "Transparency",
+ "Velocity",
+}
+
+local surfaceProps =
+{
+ "ParamA",
+ "ParamB",
+ "Surface",
+ "SurfaceInput"
+}
+
+local bevelHash = "%.2f ~ %.2f ~ %.2f"
+local isStudio = RunService:IsStudio()
+
+local negateBase = Instance.new("Part")
+negateBase.Name = "__negateplane"
+negateBase.CanCollide = false
+negateBase.BottomSurface = 0
+negateBase.Transparency = 1
+negateBase.Anchored = true
+negateBase.TopSurface = 0
+negateBase.Locked = true
+
+CollectionService:AddTag(negateBase, "NoBevels")
+
+for _,normalId in pairs(Enum.NormalId:GetEnumItems()) do
+ local name = normalId.Name
+ for _,surfaceProp in pairs(surfaceProps) do
+ table.insert(mirrorProps, name .. surfaceProp)
+ end
+end
+
+------------------------------------------------------------------------------------------------
+
+local overload = 0
+local threshold = Vector3.new(30, 30, 30)
+
+if ServerStorage:FindFirstChild("BevelThreshold") then
+ threshold = ServerStorage.BevelThreshold.Value
+end
+
+local function debugPrint(...)
+ if debugMode then
+ warn("[BEVELS DEBUG]:", ...)
+ end
+end
+
+local function isPartOfHumanoid(object)
+ local model = object:FindFirstAncestorOfClass("Model")
+
+ if model then
+ if model:FindFirstChildOfClass("Humanoid") then
+ return true
+ else
+ return isPartOfHumanoid(model)
+ end
+ end
+
+ return false
+end
+
+local function canGiveBevels(part)
+ if part.Parent and part:IsA("Part") and not CollectionService:HasTag(part, "NoBevels") then
+ if not isPartOfHumanoid(part) and not part:FindFirstChildWhichIsA("DataModelMesh") then
+ local inThreshold = false
+ local diff = threshold - part.Size
+
+ if diff.X >= 0 and diff.Y >= 0 and diff.Z >= 0 then
+ inThreshold = true
+ end
+
+ if inThreshold then
+ if CollectionService:HasTag(part, "ForceBevels") then
+ return true
+ else
+ return part.Shape.Name == "Block" and part.Transparency < 1
+ end
+ end
+ end
+ end
+
+ return false
+end
+
+local function createProxyPart(part, name, tag, sizeChange)
+ local proxyPart = Instance.new("Part")
+ proxyPart.Name = name
+ proxyPart.Locked = true
+ proxyPart.TopSurface = 0
+ proxyPart.Massless = true
+ proxyPart.Transparency = 1
+ proxyPart.BottomSurface = 0
+ proxyPart.CanCollide = false
+ proxyPart.CFrame = part.CFrame
+
+ local size = part.Size
+ if sizeChange then
+ size = size + sizeChange
+ end
+
+ local proxyWeld = Instance.new("Weld")
+ proxyWeld.Name = "ProxyWeld"
+ proxyWeld.Part1 = proxyPart
+ proxyWeld.Part0 = part
+
+ if hasBuildTools then
+ local mesh = Instance.new("SpecialMesh")
+ mesh.Scale = size * 20
+ mesh.MeshType = "Brick"
+ mesh.Offset = part.Size
+ mesh.Parent = proxyPart
+
+ proxyPart.Size = Vector3.new(.05, .05, .05)
+ proxyWeld.C0 = CFrame.new(-mesh.Offset)
+ else
+ proxyPart.Size = part.Size
+ end
+
+ CollectionService:AddTag(proxyPart, tag)
+ CollectionService:AddTag(proxyPart, "NoBevels")
+ CollectionService:AddTag(proxyWeld, "GorillaGlue")
+
+ proxyWeld.Parent = proxyPart
+ proxyPart.Parent = part
+
+ return proxyPart
+end
+
+local function createBevels(part, initializing)
+ if not canGiveBevels(part) or isPartOfHumanoid(part) then
+ return
+ end
+
+ local size = part.Size
+ local sx, sy, sz = size.X, size.Y, size.Z
+ local bevelKey = bevelHash:format(sx, sy, sz)
+
+ local debugBox
+
+ if debugMode then
+ debugBox = Instance.new("BoxHandleAdornment")
+
+ debugBox.Color3 = Color3.new(0, 2, 2)
+ debugBox.AlwaysOnTop = true
+ debugBox.Name = "DebugBox"
+ debugBox.Size = size
+ debugBox.ZIndex = 0
+
+ debugBox.Adornee = part
+ debugBox.Parent = part
+ end
+
+ if not bevelCache:FindFirstChild(bevelKey) then
+ local halfSize = size / 2
+
+ local planeScale = math.max(sx, sy, sz)
+ local planes = {}
+
+ local solverPart = part:Clone()
+ solverPart.CFrame = CFrame.new()
+ solverPart.BrickColor = BrickColor.new(-1)
+
+ debugPrint("Solving:", bevelKey)
+
+ for x = -1, 1 do
+ local x0 = (x == 0)
+
+ for y = -1, 1 do
+ local y0 = (y == 0)
+
+ for z = -1, 1 do
+ local z0 = (z == 0)
+
+ local isCenter = (x0 and y0 and z0)
+ local isFace = ((x0 and y0) or (y0 and z0) or (z0 and x0))
+
+ if not (isCenter or isFace) then
+ local isCorner = (not x0 and not y0 and not z0)
+ local depth = isCorner and cornerDepth or edgeDepth
+
+ local offset = Vector3.new(x, y, z)
+ local cornerPos = (halfSize * offset)
+
+ local plane = negateBase:Clone()
+ plane.CFrame = CFrame.new(cornerPos, cornerPos + offset)
+ plane.Size = Vector3.new(planeScale, planeScale, depth)
+ plane.Parent = part
+
+ table.insert(planes, plane)
+ end
+ end
+ end
+ end
+
+ local success, union = pcall(function ()
+ return solverPart:SubtractAsync(planes, "Box")
+ end)
+
+ if success then
+ union.Name = bevelKey
+ union.UsePartColor = true
+ union.Parent = bevelCache
+
+ CollectionService:AddTag(union, "HasBevels")
+
+ if debugBox then
+ debugBox.Color3 = Color3.new(0, 2, 0)
+ end
+ elseif debugBox then
+ debugBox.Color3 = Color3.new(2, 0, 0)
+ end
+
+ for _,plane in pairs(planes) do
+ plane:Destroy()
+ end
+
+ overload = 0
+ else
+ if debugBox then
+ debugBox.Color3 = Color3.new(2, 0, 2)
+ end
+
+ overload = overload + 1
+
+ if overload % 10 == 0 then
+ RunService.Heartbeat:Wait()
+ end
+ end
+
+ local baseUnion = bevelCache:FindFirstChild(bevelKey)
+
+ if baseUnion then
+ local archivable = baseUnion.Archivable
+ baseUnion.Archivable = true
+
+ local union = baseUnion:Clone()
+ baseUnion.Archivable = archivable
+
+ for _,prop in ipairs(mirrorProps) do
+ union[prop] = part[prop]
+ end
+
+ for _,joint in pairs(part:GetJoints()) do
+ if joint:IsA("JointInstance") or joint:IsA("WeldConstraint") then
+ if joint.Part0 == part then
+ joint.Part0 = union
+ elseif joint.Part1 == part then
+ joint.Part1 = union
+ end
+ end
+ end
+
+ for _,child in pairs(part:GetChildren()) do
+ if not child:IsA("TouchTransmitter") and not child:IsA("Texture") then
+ if child:IsA("BaseScript") then
+ child.Disabled = true
+ end
+
+ child.Parent = union
+
+ if child:IsA("BaseScript") then
+ child.Disabled = false
+ end
+ end
+ end
+
+ if not initializing then
+ wait()
+ end
+
+ if CollectionService:HasTag(part, "DoUnlock") then
+ union.Locked = false
+ end
+
+ if part.ClassName ~= "Part" then
+ local holder = Instance.new("Weld")
+ holder.Part0 = part
+ holder.Part1 = union
+ holder.Parent = part
+
+ union.Anchored = false
+ union.Massless = true
+ union.Parent = part
+
+ part.Transparency = 1
+ CollectionService:AddTag(holder, "GorillaGlue")
+ else
+ local parent = part.Parent
+ part:Destroy()
+
+ union.Parent = parent
+ end
+ elseif debugBox then
+ debugBox.Color3 = Color3.new(2, 0, 0)
+ end
+
+ if debugBox then
+ debugBox.Transparency = 0.5
+ Debris:AddItem(debugBox, 2)
+ end
+end
+
+------------------------------------------------------------------------------------------------
+
+do
+ local waitForPlayer = getFlag("BevelsWaitForPlayer")
+
+ if waitForPlayer then
+ -- Wait for a player to spawn
+ local playerSpawned = false
+
+ while not playerSpawned do
+ for _,player in pairs(Players:GetPlayers()) do
+ if player.Character and player.Character:IsDescendantOf(workspace) then
+ playerSpawned = true
+ break
+ end
+ end
+
+ workspace.ChildAdded:Wait()
+ end
+ end
+
+ warn("Solving bevels...")
+
+ -- Collect all blocks currently in the workspace.
+ local initialPass = {}
+ local debugHint
+
+ for _,desc in pairs(workspace:GetDescendants()) do
+ if canGiveBevels(desc) then
+ if not desc.Locked then
+ CollectionService:AddTag(desc, "DoUnlock")
+ desc.Locked = true
+ end
+
+ table.insert(initialPass, desc)
+ end
+ end
+
+ if waitForPlayer then
+ -- Sort the blocks by the sum of their distances from players in the game.
+ local samplePoints = {}
+
+ for _,player in pairs(Players:GetPlayers()) do
+ local char = player.Character
+ if char then
+ local root = char.PrimaryPart
+ if root then
+ local rootPos = root.Position
+ table.insert(samplePoints, rootPos)
+ end
+ end
+ end
+
+ table.sort(initialPass, function (a, b)
+ local distSumA = 0
+ local distSumB = 0
+
+ local posA = a.Position
+ local posB = b.Position
+
+ for _,rootPos in pairs(samplePoints) do
+ local distA = (rootPos - posA).Magnitude
+ distSumA = distSumA + distA
+
+ local distB = (rootPos - posB).Magnitude
+ distSumB = distSumB + distB
+ end
+
+ if distSumA ~= distSumB then
+ return distSumA < distSumB
+ end
+
+ if posA.Y ~= posB.Y then
+ return posA.Y < posB.Y
+ end
+
+ if posA.X ~= posB.X then
+ return posA.X < posB.X
+ end
+
+ if posA.Z ~= posB.Z then
+ return posA.Z < posB.Z
+ end
+
+ return 0
+ end)
+ end
+
+ if debugMode then
+ debugHint = Instance.new("Hint")
+ debugHint.Text = "Generating Bevels..."
+ debugHint.Parent = workspace
+ end
+
+ -- Run through the initial bevel creation phase.
+ for _,block in ipairs(initialPass) do
+ createBevels(block, true)
+ end
+
+ if debugHint then
+ debugHint:Destroy()
+ end
+end
+
+-- Listen for new parts being added.
+workspace.DescendantAdded:Connect(createBevels)
+
+-- Allow regeneration to request bevel solving
+local bevelSolver = bevelCache:FindFirstChild("RequestSolve")
+
+if not bevelSolver then
+ bevelSolver = Instance.new("BindableFunction")
+ bevelSolver.Name = "RequestSolve"
+ bevelSolver.Parent = bevelCache
+ bevelSolver.Archivable = false
+end
+
+function bevelSolver.OnInvoke(inst)
+ for _,desc in pairs(inst:GetDescendants()) do
+ if desc:IsA("Part") then
+ createBevels(desc)
+ end
+ end
+end
+
+if RunService:IsStudio() then
+ local exportBin = Instance.new("Folder")
+ exportBin.Name = "ExportBin"
+ exportBin.Parent = ServerStorage
+
+ for _,v in pairs(bevelCache:GetChildren()) do
+ if v:IsA("TriangleMeshPart") and v.Archivable then
+ v:Clone().Parent = exportBin
+ end
+ end
+
+ wait(.1)
+
+ for _,v in pairs(exportBin:GetChildren()) do
+ if v:FindFirstChild("LOD") then
+ v.LOD:Destroy()
+ end
+ end
+end
+
+-- Ready!
+warn("Bevels ready!")
+bevelsReady.Value = true
+
+------------------------------------------------------------------------------------------------
+
+
+ -
+
+ BuildTools
+ local CollectionService = game:GetService("CollectionService")
+local HttpService = game:GetService("HttpService")
+local Players = game:GetService("Players")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+
+local ServerStorage = game:GetService("ServerStorage")
+
+local loadBuildTools = ServerStorage:FindFirstChild("LoadBuildTools")
+if not (loadBuildTools and loadBuildTools.Value) then
+ return
+end
+
+local looseBranches = ServerStorage:FindFirstChild("LooseBranches")
+
+if looseBranches and looseBranches:IsA("BoolValue") then
+ looseBranches = looseBranches.Value
+else
+ looseBranches = false
+end
+
+local toolList = loadBuildTools.Value
+if toolList == true then -- If it's a BoolValue, load all of them.
+ toolList = "GameTool;Clone;Delete"
+end
+
+local DraggerService = Instance.new("Folder")
+DraggerService.Name = "DraggerService"
+DraggerService.Parent = ReplicatedStorage
+
+local draggerGateway = Instance.new("RemoteFunction")
+draggerGateway.Name = "DraggerGateway"
+draggerGateway.Parent = DraggerService
+
+local submitUpdate = Instance.new("RemoteEvent")
+submitUpdate.Name = "SubmitUpdate"
+submitUpdate.Parent = DraggerService
+
+local draggerScript = script:WaitForChild("Dragger")
+
+local activeKeys = {}
+local playerToKey = {}
+local partToKey = {}
+local debounce = {}
+
+local SIMULATE_TAG = "SimulateAfterDrag"
+local NO_BREAK_TAG = "GorillaGlue"
+
+local function assertClass(obj, class)
+ assert(obj)
+ assert(typeof(obj) == "Instance")
+ assert(obj:IsA(class))
+end
+
+local function canGiveKey(player, part)
+ if part.Locked then
+ return false
+ end
+ local playerHasKey = playerToKey[player]
+ if playerHasKey then
+ return false
+ end
+ local partHasKey = partToKey[part]
+ if partHasKey then
+ return false
+ end
+ return true
+end
+
+local function claimAssembly(player, part)
+ if part:CanSetNetworkOwnership() then
+ part:SetNetworkOwner(player)
+ end
+end
+
+local function validJointsOf(part)
+ return coroutine.wrap(function ()
+ for _,joint in pairs(part:GetJoints()) do
+ if not CollectionService:HasTag(joint, NO_BREAK_TAG) then
+ coroutine.yield(joint)
+ end
+ end
+ end)
+end
+
+local function breakJoints(part)
+ for joint in validJointsOf(part) do
+ if not CollectionService:HasTag(joint, NO_BREAK_TAG) then
+ joint:Destroy()
+ end
+ end
+end
+
+local function makeJoints(part)
+ -- Connect this part to a nearby surface
+ workspace:JoinToOutsiders({part}, "Surface")
+end
+
+local function removePartKey(key)
+ local data = activeKeys[key]
+ if data then
+ local player = data.Player
+ if player then
+ playerToKey[player] = nil
+ end
+
+ local part = data.Part
+
+ if part then
+ makeJoints(part)
+
+ if CollectionService:HasTag(part, SIMULATE_TAG) then
+ data.Anchored = false
+ CollectionService:RemoveTag(part, SIMULATE_TAG)
+ end
+
+ part.Anchored = data.Anchored
+ claimAssembly(player, part)
+
+ partToKey[part] = nil
+ end
+
+ activeKeys[key] = nil
+ end
+end
+
+local function restoreJointUpstream(part)
+ local collectedParts = {}
+
+ if part and CollectionService:HasTag(part, SIMULATE_TAG) then
+ CollectionService:RemoveTag(part, SIMULATE_TAG)
+ part.Anchored = false
+
+ makeJoints(part)
+
+ for joint in validJointsOf(part) do
+ local part0 = joint.Part0
+ local part1 = joint.Part1
+
+ if part0 and part ~= part0 then
+ collectedParts[part0] = true
+ restoreJointUpstream(part0)
+ end
+
+ if part1 and part ~= part1 then
+ collectedParts[part1] = true
+ restoreJointUpstream(part1)
+ end
+ end
+ end
+
+ return collectedParts
+end
+
+local function collapseJointUpstream(part)
+ if part and not (part.Locked or CollectionService:HasTag(part, SIMULATE_TAG)) then
+ CollectionService:AddTag(part, SIMULATE_TAG)
+ part.Anchored = true
+
+ for joint in validJointsOf(part) do
+ local part0 = joint.Part0
+ local part1 = joint.Part1
+
+ if part0 and part ~= part0 then
+ collapseJointUpstream(part0)
+ end
+
+ if part1 and part ~= part1 then
+ collapseJointUpstream(part1)
+ end
+ end
+
+ breakJoints(part)
+ end
+end
+
+function draggerGateway.OnServerInvoke(player, request, ...)
+ if request == "GetKey" then
+ local part, asClone = ...
+ assertClass(part, "BasePart")
+
+ if asClone then
+ local newPart = part:Clone()
+ newPart.Parent = workspace
+
+ breakJoints(newPart)
+ newPart.CFrame = CFrame.new(part.Position + Vector3.new(0, part.Size.Y, 0))
+
+ local copySound = Instance.new("Sound")
+ copySound.SoundId = "rbxasset://sounds/electronicpingshort.wav"
+ copySound.PlayOnRemove = true
+ copySound.Parent = newPart
+
+ wait()
+
+ part = newPart
+ copySound:Destroy()
+ end
+
+ if canGiveKey(player, part) then
+ local key = HttpService:GenerateGUID(false)
+ claimAssembly(player, part)
+
+ playerToKey[player] = key
+ partToKey[part] = key
+
+ local collected = restoreJointUpstream(part)
+
+ local anchored = part.Anchored
+ part.Anchored = true
+ breakJoints(part)
+
+ for otherPart in pairs(collected) do
+ if otherPart:IsGrounded() then
+ collapseJointUpstream(otherPart)
+ end
+ end
+
+ activeKeys[key] =
+ {
+ Player = player;
+ Part = part;
+ Anchored = anchored;
+ }
+
+ return true, key, part
+ else
+ return false
+ end
+ elseif request == "ClearKey" then
+ local key = ...
+
+ if not key then
+ key = playerToKey[player]
+ end
+
+ if key then
+ local data = activeKeys[key]
+ if data then
+ local owner = data.Player
+ if player == owner then
+ removePartKey(key)
+ end
+ end
+ end
+ elseif request == "RequestDelete" then
+ if not debounce[player] then
+ local part = ...
+ assertClass(part, "BasePart")
+
+ debounce[player] = true
+
+ if canGiveKey(player, part) then
+ local e = Instance.new("Explosion")
+ e.BlastPressure = 0
+ e.Position = part.Position
+ e.Parent = workspace
+
+ local s = Instance.new("Sound")
+ s.SoundId = "rbxasset://sounds/collide.wav"
+ s.Volume = 1
+ s.PlayOnRemove = true
+ s.Parent = part
+
+ local connectedParts = restoreJointUpstream(part)
+ part:Destroy()
+
+ for otherPart in pairs(connectedParts) do
+ if otherPart:IsGrounded() then
+ collapseJointUpstream(otherPart)
+ end
+ end
+ end
+
+ wait(.1)
+ debounce[player] = false
+ end
+ end
+end
+
+local function onChildAdded(child)
+ if child:IsA("Backpack") then
+ for draggerTool in toolList:gmatch("[^;]+") do
+ local tool = Instance.new("Tool")
+ tool.Name = draggerTool
+ tool.RequiresHandle = false
+
+ local newDragger = draggerScript:Clone()
+ newDragger.Parent = tool
+ newDragger.Disabled = false
+
+ tool.Parent = child
+ end
+ end
+end
+
+local function onPlayerAdded(player)
+ for _, v in pairs(player:GetChildren()) do
+ onChildAdded(v)
+ end
+
+ player.ChildAdded:Connect(onChildAdded)
+end
+
+local function onPlayerRemoved(player)
+ local key = playerToKey[player]
+ if key then
+ removePartKey(key)
+ end
+end
+
+local function onSubmitUpdate(player, key, cframe)
+ local keyData = activeKeys[key]
+ if keyData then
+ local owner = keyData.Player
+ if owner == player then
+ local part = keyData.Part
+ if part and part:IsDescendantOf(workspace) then
+ breakJoints(part)
+ part.CFrame = cframe
+ end
+ end
+ end
+end
+
+for _, player in pairs(game.Players:GetPlayers()) do
+ onPlayerAdded(player)
+end
+
+submitUpdate.OnServerEvent:Connect(onSubmitUpdate)
+Players.PlayerAdded:Connect(onPlayerAdded)
+Players.PlayerRemoving:Connect(onPlayerRemoved)
+
+-- Garbage Collection
+
+while wait(5) do
+ for part, key in pairs(partToKey) do
+ if not part:IsDescendantOf(workspace) then
+ removePartKey(key)
+ end
+ end
+end
+
+
-
+
+ Dragger
+ local CollectionService = game:GetService("CollectionService")
+local RunService = game:GetService("RunService")
+local UserInputService = game:GetService("UserInputService")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local SoundService = game:GetService("SoundService")
+local Dragger = Instance.new("Dragger")
+
+local tool = script.Parent
+local selection = Instance.new("SelectionBox")
+selection.Parent = tool
+selection.Transparency = 1
+
+local icon = Instance.new("StringValue")
+icon.Name = "IconOverride"
+icon.Parent = tool
+
+local mode = tool.Name
+local draggerService = ReplicatedStorage:WaitForChild("DraggerService")
+local gateway = draggerService:WaitForChild("DraggerGateway")
+local submitUpdate = draggerService:WaitForChild("SubmitUpdate")
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Connections
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local cons = {}
+
+local function addConnections(connections)
+ for event, func in pairs(connections) do
+ local con = event:Connect(func)
+ table.insert(cons, con)
+ end
+end
+
+local function clearCons()
+ while #cons > 0 do
+ local connection = table.remove(cons)
+ connection:Disconnect()
+ end
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Keys
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local keyLocks = {}
+
+local function onInputEnded(input)
+ if keyLocks[input.KeyCode.Name] then
+ keyLocks[input.KeyCode.Name] = nil
+ end
+end
+
+local function isKeyDown(key)
+ if UserInputService:IsKeyDown(key) and not keyLocks[key] then
+ keyLocks[key] = true
+ return true
+ end
+ return false
+end
+
+UserInputService.InputEnded:Connect(onInputEnded)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Tool Style
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local style =
+{
+ GameTool =
+ {
+ Icon = "rbxassetid://1048129653";
+ HoverColor = Color3.fromRGB(25,153,255);
+ Cursors =
+ {
+ Idle = "";
+ Hover = "rbxasset://textures/DragCursor.png";
+ Grab = "rbxasset://textures/GrabRotateCursor.png";
+ };
+ };
+ Clone =
+ {
+ Icon = "rbxasset://textures/Clone.png";
+ HoverColor = Color3.fromRGB(25,153,255);
+ Cursors =
+ {
+ Idle = "rbxasset://textures/CloneCursor.png";
+ Hover = "rbxassetid://1048136830";
+ Grab = "rbxasset://textures/GrabRotateCursor.png";
+ }
+ };
+ Delete =
+ {
+ Icon = "rbxasset://textures/Hammer.png";
+ HoverColor = Color3.new(1,0.5,0);
+ CanShowWithHover = true;
+ Cursors =
+ {
+ Idle = "rbxasset://textures/HammerCursor.png";
+ Hover = "rbxasset://textures/HammerOverCursor.png";
+ }
+ }
+}
+
+local function getIcon(iconType)
+ return style[mode].Cursors[iconType]
+end
+
+tool.TextureId = style[mode].Icon
+selection.Color3 = style[mode].HoverColor
+
+if style[mode].CanShowWithHover then
+ selection.Transparency = 0
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Dragger
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local mouse
+local currentKey
+local down = false
+local debounce = false
+
+local function onIdle()
+ if not down and mouse then
+ local mousePart = mouse.Target
+
+ if mousePart and not mousePart.Locked then
+ selection.Adornee = mousePart
+ icon.Value = getIcon("Hover")
+ return
+ end
+
+ selection.Adornee = nil
+ icon.Value = getIcon("Idle")
+ end
+end
+
+local function draggerRotate(axis)
+ if down then
+ Dragger:AxisRotate(axis)
+ end
+end
+
+local function startDraggerAction(mPart)
+ if mode == "Delete" then
+ gateway:InvokeServer("RequestDelete",mPart)
+ return
+ end
+
+ local pointOnMousePart = mPart.CFrame:ToObjectSpace(mouse.Hit).Position
+ local canDrag, dragKey, mousePart = gateway:InvokeServer("GetKey", mPart, mode == "Clone")
+
+ if canDrag then
+ selection.Adornee = mousePart
+ selection.Transparency = 0
+
+ down = true
+ currentKey = dragKey
+
+ icon.Value = getIcon("Grab")
+ Dragger:MouseDown(mousePart, pointOnMousePart, {mousePart})
+
+ local lastSubmit = 0
+
+ while down do
+ local now = tick()
+ local joints = {}
+
+ for _,joint in pairs(mousePart:GetJoints()) do
+ if CollectionService:HasTag(joint, "GorillaGlue") then
+ joints[joint] = joint.Parent
+ joint.Parent = nil
+ end
+ end
+
+ --local mousePart = selection.Adornee
+ if down then
+ Dragger:MouseMove(mouse.UnitRay)
+ end
+
+ if mousePart and currentKey then
+ if isKeyDown("R") then
+ draggerRotate("Z")
+ elseif isKeyDown("T") then
+ draggerRotate("X")
+ end
+
+ if now - lastSubmit > 0.03 then
+ submitUpdate:FireServer(currentKey, mousePart.CFrame)
+ lastSubmit = now
+ end
+ end
+
+ for joint, parent in pairs(joints) do
+ joint.Parent = parent
+ end
+
+ RunService.Heartbeat:Wait()
+ end
+
+ selection.Transparency = 1
+ gateway:InvokeServer("ClearKey", dragKey)
+
+ currentKey = nil
+ end
+end
+
+local function onButton1Down()
+ if not debounce then
+ debounce = true
+
+ local mousePart = selection.Adornee
+
+ if mousePart and not down then
+ startDraggerAction(mousePart)
+ end
+
+ debounce = false
+ end
+end
+
+local function onButton1Up()
+ if down then
+ down = false
+ Dragger:MouseUp()
+ end
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Tool
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function onEquipped(newMouse)
+ mouse = newMouse
+ addConnections
+ {
+ [mouse.Button1Down] = onButton1Down;
+ [mouse.Button1Up] = onButton1Up;
+ [mouse.Idle] = onIdle;
+ }
+end
+
+local function onUnequipped()
+ onButton1Up()
+ clearCons()
+
+ selection.Adornee = nil
+ mouse = nil
+end
+
+tool.Equipped:Connect(onEquipped)
+tool.Unequipped:Connect(onUnequipped)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+
+ -
+
+ CaptureTheFlag
+ local Players = game:GetService("Players")
+local CollectionService = game:GetService("CollectionService")
+local Teams = game:GetService("Teams")
+
+local FlagInstance = "FlagInstance"
+local FlagStandInstance = "FlagStandInstance"
+
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Flags
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function restoreFlag(flag)
+ local owner = flag:FindFirstChild("FlagStand")
+ local flagStand = owner and owner.Part0
+
+ if owner and flagStand then
+ for _,joint in pairs(flag:GetJoints()) do
+ if joint.Name == "RightGrip" then
+ joint:Destroy()
+ end
+ end
+
+ if flag.Name == "Handle" then
+ local tool = flag.Parent
+ if tool:IsA("Tool") then
+ flag.Name = tool.Name
+ tool.Parent = nil
+ end
+ end
+
+ flag.CFrame = flagStand.CFrame
+ flag.Parent = flagStand.Parent
+
+ wait()
+
+ flag.Velocity = Vector3.new()
+ flag.RotVelocity = Vector3.new()
+
+ owner.Part1 = flag
+ flag.Anchored = false
+ end
+end
+
+local function mountFlagAsTool(flag, humanoid)
+ local owner = flag:FindFirstChild("FlagStand")
+ local teamColor = flag:FindFirstChild("TeamColor")
+
+ if not (owner and teamColor) or flag.Name == "Handle" then
+ return
+ end
+
+ local grip = CFrame.new(0.25, 0, 0) * CFrame.Angles(0, -math.pi / 2, 0)
+
+ local tool = Instance.new("Tool")
+ tool.Name = flag.Name
+ tool.Grip = grip
+
+ local deathCon
+
+ local function onDied()
+ local char = humanoid.Parent
+
+ if char and tool.Parent == char then
+ humanoid:UnequipTools()
+ end
+
+ if deathCon then
+ deathCon:Disconnect()
+ deathCon = nil
+ end
+ end
+
+ local function onUnequipped()
+ if deathCon then
+ deathCon:Disconnect()
+ deathCon = nil
+ end
+
+ if humanoid then
+ local rootPart = humanoid.RootPart
+
+ if rootPart then
+ local cf = rootPart.CFrame * CFrame.new(0, 4, -8)
+ flag.RotVelocity = Vector3.new(1, 1, 1)
+ flag.Position = cf.Position
+ end
+ end
+
+ if flag.Parent == tool then
+ flag.Parent = workspace
+ end
+
+ flag.Name = tool.Name
+
+ spawn(function ()
+ tool:Destroy()
+ end)
+ end
+
+ tool.Unequipped:Connect(onUnequipped)
+ CollectionService:AddTag(tool, "Flag")
+
+ tool.Parent = workspace
+ owner.Part1 = nil
+
+ flag.Name = "Handle"
+ flag.Parent = tool
+
+ humanoid:EquipTool(tool)
+ deathCon = humanoid.Died:Connect(onDied)
+end
+
+local function onFlagAdded(flag)
+ if not flag:IsA("BasePart") then
+ return
+ end
+
+ -- Mount TeamColor
+ local teamColor = flag:FindFirstChild("TeamColor")
+
+ if not teamColor then
+ teamColor = Instance.new("BrickColorValue")
+ teamColor.Value = flag.BrickColor
+ teamColor.Name = "TeamColor"
+ teamColor.Parent = flag
+ end
+
+ -- Mount FlagStand
+ local flagStand, owner
+
+ for _,part in pairs(flag:GetConnectedParts()) do
+ if CollectionService:HasTag(part, FlagStandInstance) then
+ flagStand = part
+ break
+ end
+ end
+
+ if flagStand then
+ owner = Instance.new("Weld")
+ owner.C0 = flagStand.CFrame:ToObjectSpace(flag.CFrame)
+ owner.Name = "FlagStand"
+ owner.Part0 = flagStand
+ owner.Parent = flag
+
+ for _,joint in pairs(flag:GetJoints()) do
+ if joint ~= owner then
+ joint:Destroy()
+ end
+ end
+
+ owner.Part1 = flag
+ CollectionService:AddTag(owner, "GorillaGlue")
+ end
+
+ spawn(function ()
+ -- Try to keep the flag from falling out of the world.
+ local deathPlane = workspace.FallenPartsDestroyHeight
+
+ while flag:IsDescendantOf(workspace) do
+ if flag.Position.Y < deathPlane + 200 then
+ local tool = flag.Parent
+
+ if tool:IsA("Tool") then
+ tool.Parent = workspace
+ wait()
+ end
+
+ restoreFlag(flag)
+ end
+
+ wait()
+ end
+ end)
+
+ local function onTouched(hit)
+ local char = hit.Parent
+ if char then
+ local player = Players:GetPlayerFromCharacter(char)
+ local humanoid = char:FindFirstChildOfClass("Humanoid")
+
+ if player and humanoid then
+ if player.Neutral then
+ return
+ end
+
+ if player.TeamColor == teamColor.Value then
+ if owner.Part1 ~= flag then
+ restoreFlag(flag)
+ end
+ else
+ mountFlagAsTool(flag, humanoid)
+ end
+ end
+ end
+ end
+
+ flag.Touched:Connect(onTouched)
+end
+
+for _,flag in pairs(CollectionService:GetTagged(FlagInstance)) do
+ onFlagAdded(flag)
+end
+
+local flagAdded = CollectionService:GetInstanceAddedSignal(FlagInstance)
+flagAdded:Connect(onFlagAdded)
+
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Flag Stands
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function onFlagStandAdded(flagStand)
+ if not flagStand:IsA("BasePart") then
+ return
+ end
+
+ local debounce = false
+ local teamColor = flagStand:FindFirstChild("TeamColor")
+ local flagCaptured = flagStand:FindFirstChild("FlagCaptured")
+
+ if not teamColor then
+ teamColor = Instance.new("BrickColorValue")
+ teamColor.Value = flagStand.BrickColor
+ teamColor.Name = "TeamColor"
+ teamColor.Parent = flagStand
+ end
+
+ if not flagCaptured then
+ flagCaptured = Instance.new("BindableEvent")
+ flagCaptured.Name = "FlagCaptured"
+ flagCaptured.Parent = flagStand
+ end
+
+ local function onTouched(hit)
+ if debounce then
+ return
+ end
+
+ local char = hit.Parent
+ if char then
+ local player = Players:GetPlayerFromCharacter(char)
+ if player then
+ if player.Neutral then
+ return
+ end
+
+ if player.TeamColor ~= teamColor.Value then
+ return
+ end
+
+ local tool = char:FindFirstChildOfClass("Tool")
+ local handle = tool and tool:FindFirstChild("Handle")
+
+ if handle and CollectionService:HasTag(handle, FlagInstance) then
+ debounce = true
+ print("flag captured!")
+
+ flagCaptured:Fire(player)
+ restoreFlag(handle)
+
+ tool:Destroy()
+
+ wait(1)
+ debounce = false
+ end
+ end
+ end
+ end
+
+ flagStand.Touched:Connect(onTouched)
+end
+
+local function onFlagStandRemoved(flagStand)
+ local teamColor = flagStand:FindFirstChild("TeamColor")
+ local flagCaptured = flagStand:FindFirstChild("FlagCaptured")
+
+ if teamColor then
+ teamColor:Destroy()
+ end
+
+ if flagCaptured then
+ flagCaptured:Destroy()
+ end
+end
+
+for _,flagStand in pairs(CollectionService:GetTagged(FlagStandInstance)) do
+ onFlagStandAdded(flagStand)
+end
+
+local flagStandAdded = CollectionService:GetInstanceAddedSignal(FlagStandInstance)
+flagStandAdded:Connect(onFlagStandAdded)
+
+local flagStandRemoved = CollectionService:GetInstanceRemovedSignal(FlagStandInstance)
+flagStandRemoved:Connect(onFlagStandRemoved)
+
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+ -
+
+ Characters
+ local CollectionService = game:GetService("CollectionService")
+local Players = game:GetService("Players")
+local InsertService = game:GetService("InsertService")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local ServerStorage = game:GetService("ServerStorage")
+
+local hats = ServerStorage:WaitForChild("ServerHatCache")
+local requestCharacter = ReplicatedStorage:WaitForChild("RequestCharacter")
+local assetUtil = require(ReplicatedStorage:WaitForChild("AssetUtil"))
+local itemData = ReplicatedStorage:WaitForChild("ItemData")
+local hatData = require(itemData:WaitForChild("Hat"))
+
+local playerDataGet = { Success = false }
+pcall(function ()
+ playerDataGet = require(ServerStorage:WaitForChild("PlayerDataStore"))
+end)
+
+if not playerDataGet.Success then
+ warn("Failed to get PlayerData. Avatars will not be loaded.")
+end
+
+local playerDataStore = playerDataGet.DataStore
+
+local limbs = {"Head", "Torso", "LeftArm", "RightArm", "LeftLeg", "RightLeg"}
+
+local function preBufferHat(hatId)
+ local hat = hats:FindFirstChild(hatId)
+
+ if not hat then
+ local success, import = assetUtil:SafeCall(InsertService, "LoadAsset", tonumber(hatId))
+
+ if success then
+ hat = import:FindFirstChildWhichIsA("Accoutrement")
+ if hat then
+ hat.Name = hatId
+ hat.Parent = hats
+ end
+ end
+ end
+
+ return hat
+end
+
+local function safeDestroy(obj)
+ spawn(function ()
+ obj:Destroy()
+ end)
+end
+
+local function onCharacterAdded(char)
+ local player = Players:GetPlayerFromCharacter(char)
+
+ local bodyColors = script.BodyColors:Clone()
+ CollectionService:AddTag(bodyColors, "RespectCharacterAsset")
+
+ local graphic = script.ShirtGraphic:Clone()
+
+ local shirt = char:FindFirstChildWhichIsA("Shirt")
+ if not shirt then
+ shirt = script.Shirt:Clone()
+ end
+
+ local pants = char:FindFirstChildWhichIsA("Pants")
+ if not pants then
+ pants = script.Pants:Clone()
+ end
+
+ local faceId = 1104210678
+ local tshirtId = 131792587
+
+ local humanoid = char:WaitForChild("Humanoid")
+ CollectionService:AddTag(humanoid, "Animator")
+
+ local function onDied()
+ if char:FindFirstChild("HumanoidRootPart") then
+ char.HumanoidRootPart:Destroy()
+ end
+
+ wait(5)
+
+ local player = game.Players:GetPlayerFromCharacter(char)
+ if player then
+ player:LoadCharacter()
+ end
+ end
+
+ local function onDescendantAdded(desc)
+ if desc:IsA("CharacterMesh") and not desc.Name:sub(1, 3) == "CL_" then
+ safeDestroy(desc)
+ elseif desc:IsA("Accoutrement") then
+ -- Safe way to deter non-game accessories, since I name them by their AssetId
+ if not tonumber(desc.Name) then
+ safeDestroy(desc)
+ end
+ elseif desc:IsA("SpecialMesh") and desc.Parent.Name == "Head" then
+ if desc.Name ~= "HeadMesh" then
+ wait()
+
+ local override = Instance.new("SpecialMesh")
+ override.Name = "HeadMesh"
+ override.Scale = Vector3.new(1.25, 1.25, 1.25)
+ override.Parent = desc.Parent
+
+ safeDestroy(desc)
+ end
+ elseif desc:IsA("BodyColors") and desc ~= bodyColors and not CollectionService:HasTag(bodyColors, "RespectCharacterAsset") then
+ safeDestroy(desc)
+ bodyColors.Parent = nil
+ wait()
+ bodyColors.Parent = char
+ end
+ end
+
+ for _,v in pairs(char:GetDescendants()) do
+ onDescendantAdded(v)
+ end
+
+ char.DescendantAdded:Connect(onDescendantAdded)
+ humanoid.Died:Connect(onDied)
+
+ if player.UserId > 0 and playerDataStore then
+ local playerData = playerDataStore:GetSaveData(player)
+ local colorData = playerData:Get("BodyColors")
+ if colorData then
+ for _,limb in pairs(limbs) do
+ local num = colorData[limb]
+ if num then
+ bodyColors[limb.."Color"] = BrickColor.new(num)
+ end
+ end
+ end
+
+ local loadout = playerData:Get("Loadout")
+ if loadout then
+ local shirtId = loadout.Shirt
+ if shirtId then
+ shirt.ShirtTemplate = "rbxassetid://" .. shirtId
+ end
+ local pantsId = loadout.Pants
+ if pantsId then
+ pants.PantsTemplate = "rbxassetid://" .. pantsId
+ end
+
+ faceId = loadout.Face or faceId
+ spawn(function ()
+ local hatId = loadout.Hat or 0
+ if hatId > 0 then
+ local hatSrc = preBufferHat(hatId)
+ local hat = hatSrc:Clone()
+ hat.Parent = char
+ end
+ end)
+ end
+
+ tshirtId = playerData:Get("TShirt") or tshirtId
+ end
+
+ if tshirtId > 0 then
+ local success,img = assetUtil:RequestImage(tshirtId)
+ if success and img then
+ graphic.Graphic = img
+ graphic.Parent = char
+ end
+ end
+
+ bodyColors.Parent = char
+ shirt.Parent = char
+ pants.Parent = char
+
+ local head = char:WaitForChild("Head")
+ local face = head:WaitForChild("face")
+ face.Texture = "rbxhttp://Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=420&ht=420&aid=" .. faceId
+end
+
+local function onRequestCharacter(player)
+ if not player.Character then
+ player:LoadCharacter()
+ end
+end
+
+local function onPlayerAdded(player)
+ player.CanLoadCharacterAppearance = false
+ player.CharacterAdded:connect(onCharacterAdded)
+
+ if player.Character then
+ onCharacterAdded(player.Character)
+ end
+
+ if game.JobId == "" then
+ player:LoadCharacter()
+ end
+end
+
+for _,v in pairs(Players:GetPlayers()) do
+ onPlayerAdded(v)
+end
+
+Players.PlayerAdded:connect(onPlayerAdded)
+requestCharacter.OnServerEvent:Connect(onRequestCharacter)
+
+
+ -
+
+ Chat
+ local Players = game:GetService("Players")
+local TextService = game:GetService("TextService")
+local Chat = game:GetService("Chat")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+
+local chatRemote = ReplicatedStorage:WaitForChild("ChatRemote")
+local mSafeChatTree = ReplicatedStorage:WaitForChild("SafeChatTree")
+local safeChatTree = require(mSafeChatTree)
+
+local filterCache = {}
+local maxChatLength = 128
+
+local function onServerEvent(player, message)
+ assert(typeof(message) == "string", "bad input passed")
+ assert(#message <= maxChatLength, "Chat message was too long!")
+
+ if message:sub(1,3) == "/sc" then
+ local tree = safeChatTree
+
+ for t in message:gmatch("%d+") do
+ local i = tonumber(t) + 1
+ tree = tree.Branches[i]
+
+ if not tree then
+ break
+ end
+ end
+
+ message = tree and tree.Label or " "
+ end
+
+ local asciiMessage = ""
+
+ for p, c in utf8.codes(message) do
+ if c > 0x1F600 then
+ asciiMessage = asciiMessage .. "??"
+ else
+ asciiMessage = asciiMessage .. utf8.char(c)
+ end
+ end
+
+ message = asciiMessage
+
+ local userId = player.UserId
+ if not filterCache[userId] then
+ filterCache[userId] = {}
+ end
+
+ local filterResult = filterCache[userId][message]
+
+ if not filterResult then
+ filterResult = TextService:FilterStringAsync(message,userId)
+ filterCache[userId][message] = filterResult
+ end
+
+ for _,receiver in pairs(Players:GetPlayers()) do
+ spawn(function ()
+ pcall(function ()
+ local filtered = filterResult:GetChatForUserAsync(receiver.UserId)
+ chatRemote:FireClient(receiver, player, filtered, filtered ~= message)
+ end)
+ end)
+ end
+end
+
+chatRemote.OnServerEvent:Connect(onServerEvent)
+
+
+ -
+
+ Cylinders
+
+
+
-
+
+ CylinderSurface
+ true
+
[null]
+ false
+ true
+
+ true
+
+ 100
+ 100
+
+ false
+ true
+ 1
+ 0
+ 50
+ true
+ [null]
+ 0
+
+ 0
+ 0
+ 0
+
+ -
+
+ Frame
+ false
+
+ 0.5
+ 0.5
+
+ true
+
+ true
+
+ 0.32549020648002625
+ 0.32549020648002625
+ 0.32549020648002625
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 20
+ 0.699999988079071
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+
+ -
+
+ Frame
+ false
+
+ 0.5
+ 0.5
+
+ true
+
+ true
+
+ 0.32549020648002625
+ 0.32549020648002625
+ 0.32549020648002625
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.699999988079071
+ 0
+ 0
+ 20
+
+ 0
+ 0
+
+ true
+ 1
+
+
+
+
+ -
+
+ Explosions
+ local CollectionService = game:GetService("CollectionService")
+local Debris = game:GetService("Debris")
+local ServerStorage = game:GetService("ServerStorage")
+
+local FORCE_GRANULARITY = 2
+local allowTeamDamage = false
+
+local teamDamage = ServerStorage:FindFirstChild("TeamDamage")
+if teamDamage then
+ allowTeamDamage = teamDamage.Value
+end
+
+local function processExplosion(explosion)
+ local BLAST_RADIUS = explosion.BlastRadius
+ local BLAST_PRESSURE = explosion.BlastPressure
+
+ if explosion:FindFirstChild("BLAST_PRESSURE") then
+ BLAST_PRESSURE = explosion.BLAST_PRESSURE.Value
+ end
+
+ if BLAST_PRESSURE > 0 then
+ local damagedPlayerSet = {}
+ local blastCenter = explosion.Position
+
+ local function onExplosionHit(p, dist)
+ if explosion:FindFirstChild("Owner") then
+ local player = explosion.Owner.Value
+ if player then
+ local char = player.Character
+ if char and p:IsDescendantOf(char) then
+ return
+ end
+ end
+ end
+
+ local isInCharacter = false
+
+ if p.Size.Magnitude / 2 < 20 then
+ --world->ticklePrimitive(p, true);
+
+ local doBreakjoints = true
+ local hitCharacter = p:FindFirstAncestorWhichIsA("Model")
+ local hitHumanoid = hitCharacter:FindFirstChild("Humanoid")
+
+ if hitCharacter and hitHumanoid then
+ -- flag as character
+ isInCharacter = true
+
+ -- don't breakjoints characters
+ doBreakjoints = false
+
+ -- work out what damage to do
+ local hitPlayer = game.Players:GetPlayerFromCharacter(hitCharacter)
+ local creatorTag = explosion:FindFirstChild("creator")
+ local myPlayer
+
+ if creatorTag then
+ myPlayer = creatorTag.Value
+ end
+
+ if hitPlayer and not damagedPlayerSet[hitPlayer] then
+ local doDamage = true
+
+ if not allowTeamDamage then
+ if myPlayer and hitPlayer ~= myPlayer then
+ if hitPlayer.Team and myPlayer.Team and hitPlayer.Team == myPlayer.Team then
+ doDamage = false
+ end
+ end
+ end
+
+ if doDamage then
+ -- flag as damaged
+ damagedPlayerSet[hitPlayer] = true
+
+ -- assume the torso is a massless frictionless unit ball in a perfect vaccum
+ dist = math.min(math.max(dist - 0.8, 0), 1)
+
+ -- damage to do
+ local frac = (dist / BLAST_RADIUS)
+
+ -- do damage. See how much damage to do
+ if myPlayer == hitPlayer then
+ hitHumanoid:TakeDamage((BLAST_RADIUS * 20) - (frac * 38))
+ hitHumanoid:ChangeState("Ragdoll")
+ else
+ hitHumanoid:TakeDamage(100)
+ end
+ end
+ end
+ end
+
+ -- breakjoints stuff
+ if doBreakjoints then
+ if not hitHumanoid and p:CanSetNetworkOwnership() then
+ p:SetNetworkOwner(nil)
+ end
+
+ for _,joint in pairs(p:GetJoints()) do
+ if not CollectionService:HasTag(joint, "GorillaGlue") then
+ joint:Destroy()
+ end
+ end
+ end
+
+ --Vector3 delta = (p->getCoordinateFrame().translation - position);
+ local delta = (p.Position - blastCenter)
+
+ --Vector3 normal =
+ -- (delta == Vector3::zero())
+ -- ? Vector3::unitY()
+ -- : delta.direction();
+ local normal = (delta == Vector3.new(0, 0, 0))
+ and Vector3.new(0, 1, 0)
+ or delta.unit
+
+ --float radius = p->getRadius();
+ local radius = p.Size.magnitude / 2
+
+ --float surfaceArea = radius * radius;
+ local surfaceArea = radius * radius
+
+ --Vector3 impulse = normal * blastPressure * surfaceArea * (1.0f / 4560.0f); // normalizing factor
+ local impulse = normal * BLAST_PRESSURE * surfaceArea * (1.0 / 4560.0)
+
+ -- How much force to apply (for characters, ramp it down towards the edge)
+ local frac;
+
+ if isInCharacter then
+ frac = 1 - math.max(0, math.min(1, (dist - 2) / BLAST_RADIUS))
+ else
+ frac = 1
+ end
+
+ --p->getBody()->accumulateLinearImpulse(impulse, p->getCoordinateFrame().translation);
+ local currentVelocity = p.Velocity
+ local deltaVelocity = impulse / p:GetMass() -- m * del-v = F * del-t = Impulse
+ local forceNeeded = workspace.Gravity * p:GetMass() -- F = ma
+
+ local bodyV = Instance.new('BodyVelocity')
+ bodyV.Velocity = currentVelocity + deltaVelocity
+ bodyV.MaxForce = Vector3.new(forceNeeded, forceNeeded, forceNeeded) * 10 * frac
+ bodyV.Parent = p
+
+ Debris:AddItem(bodyV, 0.2 / FORCE_GRANULARITY)
+
+ --p->getBody()->accumulateRotationalImpulse(impulse * 0.5 * radius); // a somewhat arbitrary, but nice torque
+ local rotImpulse = impulse * 0.5 * radius
+ local currentRotVelocity = p.RotVelocity
+
+ local momentOfInertia = (2 * p:GetMass() * radius * radius / 5) -- moment of inertia = 2/5*m*r^2 (assuming roughly spherical)
+ local deltaRotVelocity = rotImpulse / momentOfInertia
+ local torqueNeeded = 20 * momentOfInertia -- torque = r x F, want about alpha = 20 rad/s^2, alpha * P = torque
+
+ local rot = Instance.new('BodyAngularVelocity')
+ rot.MaxTorque = Vector3.new(torqueNeeded, torqueNeeded, torqueNeeded) * 10 * frac
+ rot.AngularVelocity = currentRotVelocity + deltaRotVelocity
+ rot.Parent = p
+
+ Debris:AddItem(rot, 0.2 / FORCE_GRANULARITY)
+ end
+ end
+
+ explosion.Hit:Connect(onExplosionHit)
+ end
+end
+
+local function onDescendantAdded(desc)
+ if desc:IsA("Explosion") then
+ local pressure = desc.BlastPressure
+
+ if pressure > 0 then
+ local blastPressure = Instance.new("NumberValue")
+ blastPressure.Name = "BLAST_PRESSURE"
+ blastPressure.Value = pressure
+ blastPressure.Parent = desc
+
+ desc.BlastPressure = 0
+ end
+
+ processExplosion(desc)
+ end
+end
+
+workspace.DescendantAdded:Connect(onDescendantAdded)
+
+
+ -
+
+ ForceFields
+ local ffAdorns = Instance.new("Folder")
+ffAdorns.Name = "_ForceFieldAdorns"
+ffAdorns.Parent = workspace
+
+local hide = false
+if game.ServerStorage:FindFirstChild("HideForceFields") then
+ hide = true
+end
+
+local ignoreNames =
+{
+ HumanoidRootPart = true;
+ DebugAdorn = true;
+ NoForceField = true;
+}
+
+local function onDescendantAdded(desc)
+ if desc:IsA("ForceField") then
+ desc.Visible = false
+ if hide then return end
+
+ local adorns = {}
+ local char = desc.Parent
+
+ local function registerAdorn(child)
+ if child:IsA("BasePart") and not ignoreNames[child.Name] then
+ local adorn = Instance.new("SelectionBox")
+ adorn.Transparency = 1
+ adorn.Adornee = child
+ adorn.Parent = ffAdorns
+ table.insert(adorns,adorn)
+ end
+ end
+
+ for _,part in pairs(char:GetDescendants()) do
+ registerAdorn(part)
+ end
+
+ local regSignal = char.DescendantAdded:Connect(registerAdorn)
+
+
+ while desc:IsDescendantOf(workspace) do
+ desc.AncestryChanged:Wait()
+ end
+
+ for _,adorn in pairs(adorns) do
+ adorn:Destroy()
+ end
+
+ adorns = nil
+ regSignal:Disconnect()
+
+ end
+end
+
+for _,v in pairs(workspace:GetDescendants()) do
+ onDescendantAdded(v)
+end
+
+workspace.DescendantAdded:Connect(onDescendantAdded)
+
+
+ -
+
+ HatGranter
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local itemData = ReplicatedStorage:WaitForChild("ItemData")
+local hatData = require(itemData:WaitForChild("Hat"))
+
+local ServerStorage = game:GetService("ServerStorage")
+local grantHatToUser = ServerStorage:WaitForChild("GrantHatToUser")
+
+local authTable =
+{
+ ["1073469644"] = { ["96"] = true };
+
+ ["1081616136"] = { ["97"] = true,
+ ["98"] = true };
+
+ ["2421080323"] = { ["100"] = true };
+
+ ["2471146032"] = { ["101"] = true,
+ ["102"] = true };
+}
+
+local playerDataGet = { Success = false }
+
+pcall(function ()
+ playerDataGet = require(ServerStorage:WaitForChild("PlayerDataStore"))
+end)
+
+if not playerDataGet.Success then
+ warn("Failed to load PlayerData. HatGranter will not work.")
+end
+
+local playerData = playerDataGet.DataStore
+
+local function onGrantHat(player,hatId)
+ local userId = player.UserId
+
+ if userId > 0 then
+ local auth = authTable[tostring(game.PlaceId)]
+
+ if auth then
+ local canGiveHat = auth[tostring(hatId)]
+
+ if canGiveHat and playerData then
+ local hatInfo = hatData[hatId]
+ local hatAsset = hatInfo.AssetId
+
+ local myData = playerData:GetSaveData(player)
+ local items = myData:Get("Items")
+ local loadout = myData:Get("Loadout")
+
+ local id = tostring(hatAsset)
+
+ if not items.Hat[id] then
+ items.Hat[id] = true
+ myData:Set("Items", items)
+ end
+
+ loadout.Hat = hatAsset
+ myData:Set("Loadout", loadout)
+ end
+ end
+ end
+end
+
+grantHatToUser.Event:Connect(onGrantHat)
+
+
+ -
+
+ Heads
+ local function processObject(obj)
+ if obj:IsA("SpecialMesh") and obj.MeshType == Enum.MeshType.Head then
+ local head = obj.Parent
+
+ local col = math.min(head.Size.X,head.Size.Z)
+ local thickness = head.Size.Y/col
+
+ if math.abs(thickness-1) <= 0.01 then
+ local face = head:FindFirstChild("face")
+ if face and face.Texture:lower() == "rbxasset://textures/face.png" then
+ face.Texture = "rbxassetid://1104210678"
+ end
+ obj.Name = "MeshHead"
+ obj.MeshId = "rbxassetid://1104623876"
+ obj.Scale = obj.Scale * head.Size.Y
+ for _,surface in pairs(Enum.NormalId:GetEnumItems()) do
+ head[surface.Name .. "Surface"] = 0
+ end
+ end
+ end
+end
+
+for _,desc in pairs(workspace:GetDescendants()) do
+ processObject(desc)
+end
+
+workspace.DescendantAdded:Connect(processObject)
+
+
+ -
+
+ InputGateway
+ local self = script.Parent
+local remote = self:WaitForChild("Gateway")
+
+local tool = self.Parent
+tool.ManualActivationOnly = true
+
+local keyEvent = Instance.new("BindableEvent")
+keyEvent.Name = "KeyEvent"
+keyEvent.Parent = tool
+
+local function onGatewayReceive(sendingPlayer, request, ...)
+ local char = tool.Parent
+
+ if char and char:IsA("Model") then
+ local humanoid = char:FindFirstChild("Humanoid")
+
+ if humanoid then
+ local player = game.Players:GetPlayerFromCharacter(char)
+ assert(sendingPlayer == player)
+
+ if request == "SetActive" then
+ local down, target = ...
+ assert(typeof(target) == "CFrame","Expected CFrame")
+
+ humanoid.TargetPoint = target.p
+
+ if humanoid.Health > 0 and tool:IsDescendantOf(char) then
+ if down then
+ tool:Activate()
+ else
+ tool:Deactivate()
+ end
+ end
+ elseif request == "SetTarget" then
+ local target = ...
+ assert(typeof(target) == "CFrame","Expected CFrame")
+ humanoid.TargetPoint = target.p
+ elseif request == "KeyEvent" then
+ local key, down = ...
+ assert(typeof(key) == "string","bad key cast")
+ assert(typeof(down) == "boolean","bad down state cast")
+ keyEvent:Fire(key, down)
+ end
+ end
+ end
+end
+
+remote.OnServerEvent:Connect(onGatewayReceive)
+
+
+ -
+
+ Leaderboard
+ 0) then CTF_mode = true end
+
+for _,v in pairs(game.Players:GetPlayers()) do
+ onPlayerEntered(v)
+end
+
+game.Players.ChildAdded:connect(onPlayerEntered)
+
+
+]]>
+
+
+ -
+
+ LoadTools
+ local ServerStorage = game:GetService("ServerStorage")
+local StarterPack = game:GetService("StarterPack")
+local Players = game:GetService("Players")
+
+local standardTools = ServerStorage:WaitForChild("StandardTools")
+local loadTools = ServerStorage:FindFirstChild("LoadTools")
+
+if loadTools then
+ for toolName in loadTools.Value:gmatch("[^;]+") do
+ local tool = standardTools:WaitForChild(toolName)
+ tool:Clone().Parent = StarterPack
+
+ for _,v in pairs(Players:GetPlayers()) do
+ if v:FindFirstChild("Backpack") and not v:FindFirstChild(tool.Name) then
+ tool:Clone().Parent = v.Backpack
+ end
+ end
+ end
+end
+
+
+ -
+
+ Part
+
+
+
+ -
+
+ Regeneration
+ local CollectionService = game:GetService("CollectionService")
+local ServerStorage = game:GetService("ServerStorage")
+
+local regen = ServerStorage:FindFirstChild("Regeneration")
+
+if regen then
+ local REGEN_TIME = 800
+ local REGEN_DELAY = 4
+
+ if regen:FindFirstChild("RegenTime") then
+ REGEN_TIME = regen.RegenTime.Value
+ end
+
+ if regen:FindFirstChild("RegenDelay") then
+ REGEN_DELAY = regen.RegenDelay.Value
+ end
+
+ local ready = Instance.new("BoolValue")
+ ready.Name = "Ready"
+ ready.Parent = regen
+
+ local bevelCache = ServerStorage:WaitForChild("BevelCache")
+ local bevelsReady = bevelCache:WaitForChild("BevelsReady")
+
+ local function isLodPart(part)
+ return part and CollectionService:HasTag(part, "PartLOD")
+ end
+
+ local function setupModelRegen(model, title)
+ local title = title or model.Name
+
+ local parent = model.Parent
+ local backup = model:Clone()
+
+ local message, text
+
+ if typeof(title) == "string" then
+ message = Instance.new("Message")
+ text = "Regenerating " .. title .. "..."
+ end
+
+ spawn(function ()
+ while not bevelsReady.Value do
+ bevelsReady.Changed:Wait()
+ end
+
+ local requestSolve = bevelCache:FindFirstChild("RequestSolve")
+ if requestSolve then
+ requestSolve:Invoke(backup)
+ end
+
+ while wait(REGEN_TIME * (1 - (math.random() * .8))) do
+ local cooldown = 0
+
+ if message then
+ message.Text = text
+ message.Parent = workspace
+ end
+
+ model:Destroy()
+ wait(REGEN_DELAY)
+
+ model = backup:Clone()
+ model.Parent = parent
+
+ for _,inst in pairs(model:GetDescendants()) do
+ if inst:IsA("BasePart") then
+ workspace:JoinToOutsiders({inst}, Enum.JointCreationMode.All)
+ end
+ end
+
+ for _,joint in pairs(model:GetDescendants()) do
+ if joint:IsA("JointInstance") and joint.Name:sub(-12) == "Strong Joint" then
+ if isLodPart(joint.Part0) or isLodPart(joint.Part1) then
+ joint:Destroy()
+ end
+ end
+ end
+
+ if message then
+ message.Parent = nil
+ end
+ end
+ end)
+ end
+
+ for _,v in pairs(regen:GetChildren()) do
+ if v:IsA("ObjectValue") then
+ if v.Name == "" then
+ setupModelRegen(v.Value, true)
+ else
+ setupModelRegen(v.Value, v.Name)
+ end
+ end
+ end
+
+ ready.Value = true
+end
+
+
+ -
+
+ SessionTracker
+ local MessagingService = game:GetService("MessagingService")
+local Players = game:GetService("Players")
+
+local jobId = game.JobId
+local placeId = game.PlaceId
+
+if jobId == "" then
+ return
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local closed = false
+
+local function publishUpdate()
+ local playerCount = #Players:GetPlayers()
+
+ local serverInfo =
+ {
+ JobId = jobId;
+ PlaceId = placeId;
+ Players = playerCount;
+ }
+
+ if closed then
+ serverInfo.Closed = true;
+ end
+
+ pcall(function ()
+ MessagingService:PublishAsync("ServerData", serverInfo)
+ end)
+end
+
+local function onGameClosing()
+ closed = true
+ publishUpdate()
+end
+
+game:BindToClose(onGameClosing)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+while #Players:GetPlayers() < 1 do
+ wait(1)
+end
+
+while not closed do
+ publishUpdate()
+ wait(5)
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+ -
+
+ Time
+
+
+
+
+ -
+
+ StarterCharacterScripts
+
+
-
+
+ Animate
+ local Players = game:GetService("Players")
+
+local character = script.Parent
+local player = Players:GetPlayerFromCharacter(character)
+
+local climbing = Instance.new("BoolValue")
+climbing.Name = "Climbing"
+climbing.Parent = character
+
+local setValue = Instance.new("RemoteEvent")
+setValue.Name = "SetValue"
+setValue.Parent = climbing
+
+local function onSetValue(requester, value)
+ if requester ~= player then
+ return
+ end
+
+ if typeof(value) ~= "boolean" then
+ return
+ end
+
+ climbing.Value = value
+end
+
+setValue.OnServerEvent:Connect(onSetValue)
+
+
+ -
+
+ Bounciness
+ local Debris = game:GetService("Debris")
+
+local char = script.Parent
+local humanoid = char:WaitForChild("Humanoid")
+local head = char:WaitForChild("Head")
+
+local function onStateChanged(old,new)
+ if new.Name == "Landed" then
+ local velocity = humanoid.Torso.Velocity
+ local power = (-velocity.Y * workspace.Gravity) / 2
+
+ local force = Instance.new("BodyForce")
+ force.Name = "Bounce"
+ force.Force = Vector3.new(0,power,0)
+ force.Parent = head
+
+ Debris:AddItem(force, 1/30)
+ end
+end
+
+humanoid.StateChanged:connect(onStateChanged)
+
+
+ -
+
+ DropHats
+ local Players = game:GetService("Players")
+
+local char = script.Parent
+local torso = char:WaitForChild("HumanoidRootPart")
+
+local humanoid = char:WaitForChild("Humanoid")
+local hatPickup = script:WaitForChild("HatPickup")
+
+local dropHat = Instance.new("RemoteEvent")
+dropHat.Name = "DropHat"
+dropHat.Parent = script
+
+local function onDropHat(player)
+ local myPlayer = Players:GetPlayerFromCharacter(char)
+ assert(player == myPlayer, "Cannot drop hats unless it is your character.")
+
+ local dropPos = torso.CFrame * CFrame.new(0, 5.4, -8)
+
+ for _,hat in pairs(humanoid:GetAccessories()) do
+ local handle = hat:FindFirstChild("Handle")
+
+ if handle then
+ local newHandle = handle:Clone()
+
+ for _,joint in pairs(newHandle:GetJoints()) do
+ joint:Destroy()
+ end
+
+ newHandle.CFrame = dropPos
+ newHandle.Anchored = true
+ newHandle.CanCollide = false
+ newHandle.Parent = workspace
+
+ handle:Destroy()
+ hat.Parent = newHandle
+
+ wait(.1)
+
+ newHandle.Anchored = false
+ newHandle.CanCollide = true
+ newHandle:SetNetworkOwner(nil)
+
+ local pickup = hatPickup:Clone()
+ pickup.Parent = newHandle
+ pickup.Disabled = false
+ end
+ end
+end
+
+dropHat.OnServerEvent:Connect(onDropHat)
+
+
-
+
+ HatPickup
+
+
+
+ -
+
+ LocalDropHat
+ local UserInputService = game:GetService("UserInputService")
+
+local server = script.Parent
+local dropHat = server:WaitForChild("DropHat")
+
+local function onInputBegan(input, gameProcessed)
+ if not gameProcessed then
+ local keyCode = input.KeyCode.Name
+ if keyCode == "Equals" or keyCode == "DPadDown" then
+ dropHat:FireServer()
+ end
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+
+
+
+ -
+
+ EdgeWalking
+ local RunService = game:GetService("RunService")
+
+local char = script.Parent
+local rootPart = char:WaitForChild("HumanoidRootPart")
+
+local platform = Instance.new("Part")
+platform.Name = "NoForceField"
+platform.TopSurface = 0
+platform.BottomSurface = 0
+platform.BrickColor = BrickColor.new("Bright orange")
+platform.Size = Vector3.new(5, 1, 2)
+platform.Anchored = true
+platform.Transparency = 1
+
+local down = Vector3.new(0, -100, 0)
+local platformOffset = Vector3.new(0, -.5, 0)
+
+while wait() do
+ local start = rootPart.CFrame
+ local startPos = start.p
+ local startRay = Ray.new(startPos, start.lookVector * 5)
+
+ local hit, pos, norm = workspace:FindPartOnRay(startRay, char)
+ local floorCheckRay
+
+ local pass = false
+
+ if hit and hit.CanCollide and hit:IsGrounded() then
+ if hit:IsA("UnionOperation") or (not hit:IsA("Part") or hit.Shape.Name == "Block") then
+ local floorCheckRay = Ray.new(pos - (norm / 5), down)
+ local floor, floorPos = workspace:FindPartOnRayWithIgnoreList(floorCheckRay, {char, hit})
+
+ if floor and floor.CanCollide and startPos.Y - 2 > floorPos.Y then
+ floorPos = floorPos + platformOffset
+ platform.Parent = char
+ platform.CFrame = CFrame.new(Vector3.new(pos.X + norm.X, floorPos.Y, pos.Z + norm.Z),floorPos)
+ pass = true
+ end
+ end
+ end
+
+ if not pass then
+ platform.Parent = nil
+ end
+end
+
+
+ -
+
+ FloorDrag
+ local RunService = game:GetService("RunService")
+
+local char = script.Parent
+local humanoid = char:WaitForChild("Humanoid")
+local rootPart = char:WaitForChild("HumanoidRootPart")
+local climbForce = rootPart:WaitForChild("ClimbForce")
+local rayDown = Vector3.new(0, -5000, 0)
+
+local function moveTowards(value, goal, rate)
+ if value < goal then
+ return math.min(goal, value + rate)
+ elseif value > goal then
+ return math.max(goal, value - rate)
+ else
+ return goal
+ end
+end
+
+local lastFloorLevel = 0
+
+local function getFloorLevel()
+ local origin = rootPart.Position
+ local ray = Ray.new(origin, rayDown)
+ local hit, pos = workspace:FindPartOnRay(ray, char)
+ return pos.Y, math.clamp(math.abs(pos.Y - origin.Y), -1, 1)
+end
+
+local lastLevel = getFloorLevel()
+local updateCon
+
+local function update()
+ if humanoid.Health == 0 then
+ updateCon:Disconnect()
+ return
+ end
+
+ local level, dist = getFloorLevel()
+
+ if humanoid.SeatPart then
+ humanoid.HipHeight = 0
+ lastLevel = level
+ return
+ end
+
+ local yVel = rootPart.Velocity.Y
+
+ if math.abs(yVel) > 8 then
+ local goal = math.sign(yVel)
+ humanoid.HipHeight = moveTowards(humanoid.HipHeight, goal, 0.1)
+ elseif lastLevel ~= level then
+ humanoid.HipHeight = math.sign(lastLevel - level) * math.clamp(dist - 3, 0, 1)
+ lastLevel = level
+ else
+ humanoid.HipHeight = humanoid.HipHeight * 0.925
+ end
+end
+
+updateCon = RunService.RenderStepped:Connect(update)
+
+
+ -
+
+ GoofyBalance
+ local char = script.Parent
+local humanoid = char:WaitForChild("Humanoid")
+
+local function onStateChanged(old,new)
+ if new == Enum.HumanoidStateType.RunningNoPhysics then
+ humanoid:ChangeState(Enum.HumanoidStateType.Running)
+ elseif new == Enum.HumanoidStateType.FallingDown then
+ humanoid:ChangeState("Ragdoll")
+
+ while wait(0.5) do
+ if humanoid.RootPart then
+ local velocity = humanoid.RootPart.Velocity
+
+ if velocity.Magnitude < 0.1 then
+ wait(2)
+ humanoid:ChangeState("GettingUp")
+ break
+ end
+ else
+ break
+ end
+ end
+ end
+end
+
+humanoid.StateChanged:Connect(onStateChanged)
+
+
+ -
+
+ GoofyMotion
+ local RunService = game:GetService("RunService")
+local GameSettings = UserSettings():GetService("UserGameSettings")
+
+local char = script.Parent
+local humanoid = char:WaitForChild("Humanoid")
+local climbing = char:WaitForChild("Climbing")
+local rootPart = humanoid.RootPart
+
+local c = workspace.CurrentCamera
+local blankV3 = Vector3.new()
+local xz = Vector3.new(1,0,1)
+local bg = rootPart:FindFirstChild("FirstPersonGyro")
+
+local runState = Enum.HumanoidStateType.Running
+
+if not bg then
+ bg = Instance.new("BodyGyro")
+ bg.Name = "FirstPersonGyro"
+ bg.MaxTorque = Vector3.new(0,10e6,0)
+ bg.D = 100
+end
+
+local function toRotation(dir)
+ return CFrame.new(blankV3,dir)
+end
+
+local velocityThreshold = 200
+spawn(function ()
+ local threshold = char:WaitForChild("VelocityThreshold",5)
+ if threshold then
+ velocityThreshold = threshold.Value
+ end
+end)
+
+local function update()
+ local rotationType = GameSettings.RotationType
+ local seatPart = humanoid.SeatPart
+
+ if rotationType.Name == "CameraRelative" and not seatPart then
+ local dir = c.CFrame.lookVector * xz
+ bg.CFrame = toRotation(dir)
+ bg.Parent = rootPart
+ humanoid.AutoRotate = false
+ else
+ local state = humanoid:GetState()
+ local isRunning = (state == runState)
+ local isClimbing = climbing.Value
+ humanoid.AutoRotate = (isRunning or isClimbing)
+ bg.Parent = nil
+ end
+
+ if rootPart.Velocity.Magnitude > velocityThreshold and not seatPart then
+ humanoid:ChangeState("FallingDown")
+ end
+end
+
+humanoid.AutoRotate = false
+humanoid:SetStateEnabled("Climbing",false)
+RunService.RenderStepped:connect(update)
+c.FieldOfView = 65
+
+
+ -
+
+ InputGateway
+ local ServerStorage = game:GetService("ServerStorage")
+local inputGateway = ServerStorage:WaitForChild("InputGateway")
+local char = script.Parent
+
+local function onChildAdded(child)
+ if child:IsA("Tool") and not child:FindFirstChild("InputGateway") then
+ wait(.1)
+ local gateway = inputGateway:Clone()
+ gateway.Parent = child
+ end
+end
+
+local tool = char:FindFirstChildWhichIsA("Tool")
+if tool then
+ onChildAdded(tool)
+end
+
+char.ChildAdded:Connect(onChildAdded)
+
+
+ -
+
+ JumpLimiter
+
+
+
+ -
+
+ RetroClimbing
+ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Setup
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local char = script.Parent
+
+local humanoid = char:WaitForChild("Humanoid")
+humanoid:SetStateEnabled("Climbing", false)
+
+local rootPart = humanoid.RootPart
+local bv = rootPart:FindFirstChild("ClimbForce")
+
+if not bv then
+ bv = Instance.new("BodyVelocity")
+ bv.Name = "ClimbForce"
+ bv.Parent = humanoid.RootPart
+end
+
+bv.MaxForce = Vector3.new()
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Climbing State
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local climbing = char:WaitForChild("Climbing")
+local setValue = climbing:WaitForChild("SetValue")
+
+local function onClimbing(value)
+ setValue:FireServer(value)
+end
+
+climbing.Changed:Connect(onClimbing)
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Debug Visuals
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local Debris = game:GetService("Debris")
+local isDevTest = false
+
+local DEBUG_COLOR_RED = Color3.new(1, 0, 0)
+local DEBUG_COLOR_YLW = Color3.new(1, 1, 0)
+local DEBUG_COLOR_GRN = Color3.new(0, 1, 0)
+
+local debugBox = Instance.new("BoxHandleAdornment")
+debugBox.Adornee = workspace.Terrain
+debugBox.Color3 = DEBUG_COLOR_RED
+debugBox.Visible = false
+debugBox.Parent = script
+
+local debugCylinder = Instance.new("CylinderHandleAdornment")
+debugCylinder.Color = BrickColor.new("Bright violet")
+debugCylinder.Adornee = workspace.Terrain
+debugCylinder.Height = 0.2
+debugCylinder.Radius = 1.0
+debugCylinder.Visible = false
+debugCylinder.Parent = script
+
+local debugSBox = Instance.new("SelectionBox")
+debugSBox.Color3 = DEBUG_COLOR_RED
+debugSBox.Parent = script
+
+local function drawRayIfDebugging(rayStart, look, length, color)
+ if isDevTest then
+ local line = Instance.new("LineHandleAdornment")
+ line.CFrame = CFrame.new(rayStart, rayStart + (look.Unit * length))
+ line.Adornee = workspace.Terrain
+ line.Length = length
+ line.Color3 = color
+ line.Thickness = 4
+ line.Parent = script
+
+ local cone = Instance.new("ConeHandleAdornment")
+ cone.CFrame = CFrame.new(rayStart + (look.Unit * (length - 0.32)), rayStart + (look.Unit * length))
+ cone.Adornee = workspace.Terrain
+ cone.Color3 = color
+ cone.Radius = 1 / 10
+ cone.Height = 1 / 3
+ cone.Parent = script
+
+ Debris:AddItem(line, .5)
+ Debris:AddItem(cone, .5)
+ end
+end
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Main Climbing Logic
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local searchDepth = 0.7
+local maxClimbDist = 2.45
+local sampleSpacing = 1 / 7
+local lowLadderSearch = 2.7
+local stepForwardFrames = 0
+local ladderSearchDist = 2.0
+
+local running = Enum.HumanoidStateType.Running
+local freefall = Enum.HumanoidStateType.Freefall
+
+local function findPartInLadderZone()
+ debug.profilebegin("FastClimbCheck")
+ --
+
+ local cf = rootPart.CFrame
+
+ local top = -humanoid.HipHeight
+ local bottom = -lowLadderSearch + top
+ local radius = 0.5 * ladderSearchDist
+
+ local center = cf.Position + (cf.LookVector * ladderSearchDist * 0.5)
+ local min = Vector3.new(-radius, bottom, -radius)
+ local max = Vector3.new(radius, top, radius)
+
+ local extents = Region3.new(center + min, center + max)
+ local parts = workspace:FindPartsInRegion3(extents, char)
+
+ if isDevTest then
+ if #parts > 0 then
+ debugBox.Visible = false
+ debugSBox.Visible = true
+ debugSBox.Adornee = parts[1]
+ else
+ debugBox.Visible = true
+ debugSBox.Visible = false
+
+ debugBox.Size = extents.Size
+ debugBox.CFrame = extents.CFrame
+
+ debugCylinder.Visible = false
+ end
+ end
+
+ --
+ debug.profileend()
+ return #parts > 0
+end
+
+local function findLadder()
+ if not findPartInLadderZone() then
+ return false
+ end
+
+ debug.profilebegin("ExpensiveClimbCheck")
+
+ local torsoCoord = rootPart.CFrame
+ local torsoLook = torsoCoord.LookVector
+
+ local firstSpace = 0
+ local firstStep = 0
+
+ local lookForSpace = true
+ local lookForStep = false
+
+ local debugColor = DEBUG_COLOR_YLW
+ local topRay = math.floor(lowLadderSearch / sampleSpacing)
+
+ for i = 1, topRay do
+ local distFromBottom = i * sampleSpacing
+ local originOnTorso = Vector3.new(0, -lowLadderSearch + distFromBottom, 0)
+
+ local casterOrigin = torsoCoord.Position + originOnTorso
+ local casterDirection = torsoLook * ladderSearchDist
+
+ local ray = Ray.new(casterOrigin, casterDirection)
+ local hitPrim, hitLoc = workspace:FindPartOnRay(ray, char)
+
+ -- make trusses climbable.
+ if hitPrim and hitPrim:IsA("TrussPart") then
+ return true
+ end
+
+ local mag = (hitLoc - casterOrigin).Magnitude
+
+ if mag < searchDepth then
+ if lookForSpace then
+ debugColor = DEBUG_COLOR_GRN
+ firstSpace = distFromBottom
+
+ lookForSpace = false
+ lookForStep = true
+ end
+ elseif lookForStep then
+ firstStep = distFromBottom - firstSpace
+ debugColor = DEBUG_COLOR_RED
+ lookForStep = false
+ end
+
+ drawRayIfDebugging(casterOrigin, casterDirection, mag, debugColor)
+ end
+
+ local found = (firstSpace < maxClimbDist and firstStep > 0 and firstStep < maxClimbDist)
+ debugCylinder.Visible = isDevTest and found
+
+ if debugCylinder.Visible then
+ local y = Vector3.FromAxis('Y')
+ local pos = torsoCoord.Position + Vector3.new(0, 5, 0)
+ debugCylinder.CFrame = CFrame.new(pos, pos + y)
+ end
+
+ debug.profileend()
+ return found
+end
+
+while wait() do
+ local canClimb = false
+
+ local state = humanoid:GetState()
+ local speed = humanoid.WalkSpeed
+
+ if state == freefall or state == running then
+ canClimb = findLadder()
+ end
+
+ if canClimb then
+ local climbSpeed = speed * 0.7
+ bv.Velocity = Vector3.new(0, climbSpeed, 0)
+ bv.MaxForce = Vector3.new(climbSpeed * 100, 10e6, climbSpeed * 100)
+ else
+ if climbing.Value then
+ stepForwardFrames = 2
+ end
+
+ bv.MaxForce = Vector3.new()
+ end
+
+ if stepForwardFrames > 0 then
+ local cf = rootPart.CFrame
+ humanoid:Move(cf.LookVector)
+ stepForwardFrames = stepForwardFrames - 1
+ end
+
+ climbing.Value = canClimb
+end
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+ -
+
+ Sound
+ 0.1)
+end
+
+-- connect up
+
+Humanoid.Died:connect(onDied)
+Humanoid.Running:connect(onRunning)
+Humanoid.Jumping:connect(onJumping)
+Humanoid.GettingUp:connect(function(state) onState(state, sGettingUp) end)
+Humanoid.FreeFalling:connect(function(state)
+ --if not Climbing.Value then
+ onState(state, sFreeFalling)
+ --end
+end)
+
+Humanoid.FallingDown:connect(function(state) onState(state, sFallingDown) end)
+]]>
+
+
+ -
+
+ TeamColors
+ local CollectionService = game:GetService("CollectionService")
+local Players = game:GetService("Players")
+
+local char = script.Parent
+local player = Players:GetPlayerFromCharacter(char)
+local teamListener = player:GetPropertyChangedSignal("TeamColor")
+local bodyColors = char:WaitForChild("BodyColors")
+
+local teamColors = Instance.new("BodyColors")
+teamColors.Name = "TeamColors"
+teamColors.HeadColor = BrickColor.new("Bright yellow")
+teamColors.LeftArmColor = BrickColor.Black()
+teamColors.LeftLegColor = BrickColor.Black()
+teamColors.RightArmColor = BrickColor.Black()
+teamColors.RightLegColor = BrickColor.Black()
+
+CollectionService:AddTag(teamColors, "RespectCharacterAsset")
+
+local function onTeamChanged()
+ local team = player.Team
+ if team then
+ teamColors.TorsoColor = player.TeamColor
+ bodyColors.Parent = nil
+
+ if not CollectionService:HasTag(team, "NoAutoColor") then
+ teamColors.Parent = char
+ end
+ else
+ teamColors.Parent = nil
+ bodyColors.Parent = char
+ end
+end
+
+onTeamChanged()
+teamListener:Connect(onTeamChanged)
+
+
+ -
+
+ ToolSoundGlitch
+ -- This replicates an old sound bug that used to occur with tools back then.
+
+local CollectionService = game:GetService("CollectionService")
+local Debris = game:GetService("Debris")
+
+local char = script.Parent
+local torso = char:WaitForChild("Torso")
+local marked = {}
+
+local function processHandle(handle)
+ for _,child in pairs(handle:GetChildren()) do
+ if child:IsA("Sound") then
+ if not marked[child.SoundId] then
+ marked[child.SoundId] = true
+ else
+ local replica = child:Clone()
+ replica.Name = "ToolSoundGlitch"
+ replica.MaxDistance = 0
+ replica.Parent = torso
+
+ CollectionService:AddTag(replica, "ToolSoundGlitch")
+ replica:Play()
+
+ replica.Ended:connect(function ()
+ Debris:AddItem(replica, 1)
+ end)
+ end
+ end
+ end
+end
+
+local function onChild(child)
+ if child:IsA("Tool") then
+ local handle = child:FindFirstChild("Handle")
+
+ if handle then
+ processHandle(handle)
+ end
+ end
+end
+
+char.ChildAdded:connect(onChild)
+char.ChildRemoved:connect(onChild)
+
+
+
+ -
+
+ StarterGui
+
+
-
+
+ UI
+
+
-
+
+ Backpack
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- @CloneTrooper1019, 2015
+-- Backpack
+-- Simulates the 2008 backpack from scratch.
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Setup
+
+local ui = script.Parent
+local rootFrame = ui:WaitForChild("RootFrame")
+
+local self = rootFrame:WaitForChild("Backpack")
+local slotTemp = script:WaitForChild("SlotTemp")
+
+local backdrop = self:WaitForChild("Backdrop")
+local slotsBin = self:WaitForChild("Slots")
+
+local player = game.Players.LocalPlayer
+local UserInputService = game:GetService("UserInputService")
+
+local toolIndex = 0
+
+local tools = {}
+local slots = {}
+local tokens =
+{
+ One = 1;
+ Two = 2;
+ Three = 3;
+ Four = 4;
+ Five = 5;
+ Six = 6;
+ Seven = 7;
+ Eight = 8;
+ Nine = 9;
+ Zero = 10; -- shhh not a hack
+}
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Key Hookup
+
+local eNumPress = Instance.new("BindableEvent")
+local numPress = eNumPress.Event
+
+-- Hack to work around the inputs being overridden while the Plane tool is active.
+local function allowGameProcessedBypassHack()
+ local lastInputType = UserInputService:GetLastInputType()
+ if lastInputType.Name == "Gamepad1" then
+ local char = player.Character
+ if char then
+ local tool = char:FindFirstChildWhichIsA("Tool")
+ if tool and not tool.Enabled then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+local function onInputBegan(input,gameProcessed)
+ if not gameProcessed or allowGameProcessedBypassHack() then
+ local name = input.UserInputType.Name
+ local keyCode = input.KeyCode.Name
+ if name == "Keyboard" then
+ local toIndex = tokens[keyCode]
+ if toIndex then
+ eNumPress:Fire(toIndex)
+ end
+ elseif name == "Gamepad1" then
+ if keyCode == "ButtonL1" or keyCode == "ButtonR1" then
+ local nextIndex = toolIndex
+ if keyCode == "ButtonL1" then
+ nextIndex = nextIndex - 1
+ elseif keyCode == "ButtonR1" then
+ nextIndex = nextIndex + 1
+ end
+ print(nextIndex,#tools)
+ if nextIndex > 0 and nextIndex <= #tools then
+ eNumPress:Fire(nextIndex)
+ else
+ eNumPress:Fire(toolIndex)
+ end
+ end
+ end
+ end
+end
+
+UserInputService.InputBegan:connect(onInputBegan)
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function resortSlots()
+ for index,tool in ipairs(tools) do
+ local slot = slots[tool]
+ slot.Index.Text = index
+ slot.LayoutOrder = index
+ slot.Visible = true
+ end
+ backdrop.Size = UDim2.new(#tools,0,1,0)
+end
+
+local function createSlot(tool)
+ if not slots[tool] then
+ local index = #tools+1
+ tools[index] = tool
+
+ local slot = slotTemp:clone()
+ slot.Name = tool.Name
+ slot.Parent = slotsBin
+
+ local textHover = slot:WaitForChild("TextHover")
+ local selectionOutline = slot:WaitForChild("SelectionOutline")
+ local toolIcon = slot:WaitForChild("ToolIcon")
+ local indexLbl = slot:WaitForChild("Index")
+ local toolName = slot:WaitForChild("ToolName")
+
+ local isHovering = false
+ local isDown = false
+
+ local backpack = player:WaitForChild("Backpack")
+ local char = player.Character or player.CharacterAdded:Wait()
+
+ local humanoid = char:WaitForChild("Humanoid")
+ local conReg = {}
+
+ local function killTool()
+ local currentIndex = tonumber(indexLbl.Text)
+ table.remove(tools, currentIndex)
+
+ for _,con in pairs(conReg) do
+ con:disconnect()
+ end
+
+ slots[tool] = nil
+ slot:Destroy()
+
+ resortSlots()
+ end
+
+ local function checkParent()
+ if tool.Parent == char then
+ selectionOutline.Visible = true
+ elseif tool.Parent == backpack then
+ selectionOutline.Visible = false
+ else
+ killTool()
+ end
+ end
+
+ local function toggleTool()
+ if tool.Parent == char then
+ humanoid:UnequipTools()
+ else
+ toolIndex = tonumber(indexLbl.Text)
+ humanoid:EquipTool(tool)
+ end
+ end
+
+ local function renderUpdate()
+ if tool.TextureId ~= "" then
+ toolName.Visible = false
+ toolIcon.Visible = true
+ toolIcon.Image = tool.TextureId
+ else
+ toolIcon.Visible = false
+ toolName.Visible = true
+ toolName.Text = tool.Name
+ end
+ if tool.TextureId ~= "" then
+ textHover.Visible = false
+ if isHovering then
+ toolIcon.BackgroundTransparency = 0
+ if isDown then
+ toolIcon.BackgroundColor3 = Color3.new(0,0,1)
+ else
+ toolIcon.BackgroundColor3 = Color3.new(1,1,0)
+ end
+ else
+ toolIcon.BackgroundTransparency = 1
+ end
+ else
+ textHover.Visible = true
+ if isHovering then
+ textHover.BackgroundTransparency = 0
+ if isDown then
+ textHover.BackgroundColor3 = Color3.new(1,1,0)
+ else
+ textHover.BackgroundColor3 = Color3.new(0.706,0.706,0.706)
+ end
+ else
+ textHover.BackgroundTransparency = 1
+ end
+ end
+ end
+
+ local function onInputBegan(input)
+ if input.UserInputType.Name == "MouseButton1" then
+ isDown = true
+ elseif input.UserInputType.Name == "MouseMovement" or input.UserInputType.Name == "Touch" then
+ isHovering = true
+ end
+ renderUpdate()
+ end
+
+ local function onInputEnded(input)
+ if input.UserInputType.Name == "MouseButton1" then
+ isDown = false
+ if isHovering then
+ toggleTool()
+ end
+ elseif input.UserInputType.Name == "MouseMovement" then
+ isHovering = false
+ elseif input.UserInputType.Name == "Touch" then
+ isHovering = false
+ if humanoid.MoveDirection == Vector3.new() then
+ toggleTool()
+ end
+ end
+
+ renderUpdate()
+ end
+
+ local function onNumDown(num)
+ local currentIndex = tonumber(indexLbl.Text)
+
+ if num == currentIndex then
+ toggleTool()
+ end
+ end
+
+ local function onToolChanged(property)
+ if property == "TextureId" or property == "Name" then
+ renderUpdate()
+ elseif property == "Parent" then
+ checkParent()
+ end
+ end
+
+ local eventMounts =
+ {
+ [numPress] = onNumDown;
+ [tool.Changed] = onToolChanged;
+ [slot.InputBegan] = onInputBegan;
+ [slot.InputEnded] = onInputEnded;
+ [humanoid.Died] = killTool;
+ }
+
+ renderUpdate()
+ checkParent()
+
+ for event, func in pairs(eventMounts) do
+ local connection = event:Connect(func)
+ table.insert(conReg, connection)
+ end
+
+ slots[tool] = slot
+ resortSlots()
+ end
+end
+
+local currentChar
+
+local function onCharacterAdded(char)
+ if currentChar ~= char then
+ currentChar = char
+
+ for _,v in pairs(slots) do
+ v:Destroy()
+ end
+
+ slots = {}
+ tools = {}
+
+ local function onChildAdded(child)
+ if child:IsA("Tool") then
+ createSlot(child)
+ end
+ end
+
+ local backpack = player:WaitForChild("Backpack")
+
+ for _,v in pairs(backpack:GetChildren()) do
+ onChildAdded(v)
+ end
+
+ for _,v in pairs(char:GetChildren()) do
+ onChildAdded(v)
+ end
+
+ char.ChildAdded:connect(onChildAdded)
+ backpack.ChildAdded:connect(onChildAdded)
+ end
+end
+
+if player.Character then
+ onCharacterAdded(player.Character)
+end
+
+player.CharacterAdded:connect(onCharacterAdded)
+
+game.StarterGui.ResetPlayerGuiOnSpawn = false
+
+
-
+
+ SlotTemp
+ true
+
+ 0
+ 0
+
+ true
+
+ false
+ true
+
+ 0.7058823704719543
+ 0.7058823704719543
+ 0.7058823704719543
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 3
+ 0
+ 1
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ false
+ 14
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ false
+ 2
+ 1
+ false
+ 1
+
+ -
+
+ Index
+ true
+
+ 0
+ 1
+
+ true
+
+ true
+
+ 0.6666666865348816
+ 0.6666666865348816
+ 0.6666666865348816
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.029999999329447746
+ 2
+ 0.9700000286102295
+ -2
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.20000000298023224
+ 0
+ 0.20000000298023224
+ 0
+
+ 2
+
+ 1
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 1
+ 1
+ 1
+
+ 0.800000011920929
+ 0
+ 0
+ true
+ 2
+ 1
+ true
+ 3
+
+ -
+
+ UITextSizeConstraint
+ true
+
+ 20
+ 1
+
+
+
+
+ -
+
+ SelectionOutline
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 1
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ -1
+ 0
+ -1
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 2
+ 1
+ 2
+
+ 0
+ 0
+
+ false
+ 1
+
+ -
+
+ Outline
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 1
+ 0
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0.9700000286102295
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.029999999329447746
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+
+
+ -
+
+ Outline
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 1
+ 0
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.9700000286102295
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.029999999329447746
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+
+
+ -
+
+ Outline
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 1
+ 0
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.029999999329447746
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+
+
+ -
+
+ Outline
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 1
+ 0
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.029999999329447746
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+
+
+
+ -
+
+ TextHover
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0.7058823704719543
+ 0.7058823704719543
+ 0.7058823704719543
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ false
+ 3
+
+
+ -
+
+ ToolIcon
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+
+
+
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.15000000596046448
+ 0
+ 0.15000000596046448
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 0.699999988079071
+ 0
+ 0.699999988079071
+ 0
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 4
+
+
+ -
+
+ ToolName
+ true
+
+ 0
+ 0.5
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.10000000149011612
+ 0
+ 0.4749999940395355
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 2
+ 0
+ 0.17000000178813934
+ 0
+
+ 0
+
+ Tool
+
+ 1
+ 1
+ 1
+
+ true
+ 100
+
+ 0.498039186000824
+ 0.498039186000824
+ 0.498039186000824
+
+ 0.5
+ 0.20000000298023224
+ 0
+ true
+ 0
+ 1
+ true
+ 4
+
+
+
+
+ -
+
+ Chat
+ --------------------------------------------------------------------------------------------
+-- Constants
+--------------------------------------------------------------------------------------------
+
+local Debris = game:GetService("Debris")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local RunService = game:GetService("RunService")
+local TextService = game:GetService("TextService")
+local UserInputService = game:GetService("UserInputService")
+
+local LinkedList = require(script:WaitForChild("LinkedList"))
+
+local ui = script.Parent
+local rootFrame = ui:WaitForChild("RootFrame")
+
+local chat = rootFrame:WaitForChild("Chat")
+local chatBar = chat:WaitForChild("ChatBar")
+local chatOutput = chat:WaitForChild("ChatOutput")
+local chatRemote = ReplicatedStorage:WaitForChild("ChatRemote")
+
+local focusBackdrop = chatBar:WaitForChild("FocusBackdrop")
+local mainBackdrop = chat:WaitForChild("MainBackdrop")
+local messageTemplate = script:WaitForChild("MessageTemplate")
+
+local hasCoreGateway, coreGateway = pcall(function ()
+ local getCoreGateway = script:WaitForChild("GetCoreGateway")
+ return require(getCoreGateway)
+end)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Player Colors
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local PLAYER_COLORS =
+{
+ [0] = Color3.fromRGB(173, 35, 35); -- red
+ [1] = Color3.fromRGB( 42, 75, 215); -- blue
+ [2] = Color3.fromRGB( 29, 105, 20); -- green
+ [3] = Color3.fromRGB(129, 38, 192); -- purple
+ [4] = Color3.fromRGB(255, 146, 51); -- orange
+ [5] = Color3.fromRGB(255, 238, 51); -- yellow
+ [6] = Color3.fromRGB(255, 205, 243); -- pink
+ [7] = Color3.fromRGB(233, 222, 187); -- tan
+}
+
+local function computePlayerColor(player)
+ if player.Team then
+ return player.TeamColor.Color
+ else
+ local pName = player.Name
+ local length = #pName
+
+ local oddShift = (1 - (length % 2))
+ local value = 0
+
+ for i = 1,length do
+ local char = pName:sub(i, i):byte()
+ local rev = (length - i) + oddShift
+
+ if (rev % 4) >= 2 then
+ value = value - char
+ else
+ value = value + char
+ end
+ end
+
+ return PLAYER_COLORS[value % 8]
+ end
+end
+
+--------------------------------------------------------------------------------------------
+-- Chat Input
+--------------------------------------------------------------------------------------------
+
+local function beginChatting()
+ focusBackdrop.Visible = true
+
+ if not chatBar:IsFocused() then
+ chatBar.TextTransparency = 1
+ chatBar:CaptureFocus()
+ wait()
+ chatBar.Text = ""
+ chatBar.TextTransparency = 0
+ end
+end
+
+local function onInputBegan(input, processed)
+ if not processed and input.UserInputType == Enum.UserInputType.Keyboard then
+ if input.KeyCode == Enum.KeyCode.Slash then
+ beginChatting()
+ end
+ end
+end
+
+local function onChatFocusLost(enterPressed)
+ local msg = chatBar.Text
+
+ if enterPressed and #msg > 0 then
+ if #msg > 128 then
+ msg = msg:sub(1, 125) .. "..."
+ end
+
+ chatRemote:FireServer(msg)
+
+ if hasCoreGateway then
+ coreGateway.ChatWindow.MessagePosted:Fire(msg)
+ end
+ end
+
+ chatBar.Text = ""
+ focusBackdrop.Visible = false
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+
+chatBar.Focused:Connect(beginChatting)
+chatBar.FocusLost:Connect(onChatFocusLost)
+
+--------------------------------------------------------------------------------------------
+-- Chat Output
+--------------------------------------------------------------------------------------------
+
+local messageId = 0
+local blank_v2 = Vector2.new()
+local chatQueue = LinkedList.new()
+
+local function computeTextBounds(label)
+ local bounds = TextService:GetTextSize(label.Text, label.TextSize, label.Font, blank_v2)
+ return UDim2.new(0, bounds.X, 0, bounds.Y)
+end
+
+local function getMessageId()
+ messageId = messageId + 1
+ return messageId
+end
+
+local function onReceiveChat(player, message, wasFiltered)
+ -- Process the message
+ if message:sub(1, 1) == "%" then
+ message = "(TEAM) " .. message:sub(2)
+ end
+
+ if wasFiltered then
+ message = message:gsub("#[# ]+#", "[ Content Deleted ]")
+ end
+
+ -- Create the message
+ local msg = messageTemplate:Clone()
+
+ local playerLbl = msg:WaitForChild("PlayerName")
+ playerLbl.TextColor3 = computePlayerColor(player)
+ playerLbl.TextStrokeColor3 = playerLbl.TextColor3
+ playerLbl.Text = player.Name .. "; "
+ playerLbl.Size = computeTextBounds(playerLbl)
+
+ local msgLbl = msg:WaitForChild("Message")
+ msgLbl.Text = message
+ msgLbl.Size = computeTextBounds(msgLbl)
+
+ local width = playerLbl.AbsoluteSize.X + msgLbl.AbsoluteSize.X
+
+ msg.Size = msg.Size + UDim2.new(0, width, 0, 0)
+ msg.LayoutOrder = getMessageId()
+
+ msg.Name = "Message" .. msg.LayoutOrder
+ msg.Parent = chatOutput
+
+ if chatQueue.size == 6 then
+ local front = chatQueue.front
+ front.data:Destroy()
+
+ chatQueue:Remove(front.id)
+ end
+
+ chatQueue:Add(msg)
+ Debris:AddItem(msg, 60)
+end
+
+chatRemote.OnClientEvent:Connect(onReceiveChat)
+
+--------------------------------------------------------------------------------------------
+
+
-
+
+ GetCoreGateway
+ local ChatConnections = {}
+
+local function AddObjects(bindableClass,targetName,...)
+ local target = ChatConnections[targetName]
+ if not target then
+ target = {}
+ ChatConnections[targetName] = target
+ end
+ local names = {...}
+ for _,name in pairs(names) do
+ local signal = Instance.new(bindableClass)
+ signal.Name = targetName .. "_" .. name
+ signal.Parent = script
+ target[name] = signal
+ end
+end
+
+AddObjects("BindableEvent","ChatWindow",
+ ---------------------------
+ -- Fired from the CoreGui
+ ---------------------------
+ "ToggleVisibility", -- Fired when the CoreGui chat button is pressed.
+ "SetVisible", -- Fired when the CoreGui wants to directly change the visiblity state of the chat window.
+ "FocusChatBar", -- Fired when the CoreGui wants to capture the Chatbar's Focus.
+ "TopbarEnabledChanged", -- Fired when the visibility of the Topbar is changed.
+ "SpecialKeyPressed", -- Fired when the reserved ChatHotkey is pressed.
+ "CoreGuiEnabled", -- Fired when a user changes the SetCoreGuiEnabled state of the Chat Gui.
+
+ ---------------------------
+ -- Fired to the CoreGui
+ ---------------------------
+ "ChatBarFocusChanged",
+ -- ^ Fire this with 'true' when you want to assure the CoreGui that the ChatBar is being focused on.
+
+ "VisibilityStateChanged",
+ -- ^ Fire this with 'true' when the user shows or hides the chat.
+
+ "MessagesChanged",
+ -- ^ Fire this with a number to change the number of messages that have been recorded by the chat window.
+ -- If the CoreGui thinks the chat window isn't visible, it will display the recorded difference between
+ -- the number of messages that was displayed when it was visible, and the number you supply.
+
+ "MessagePosted"
+ -- ^ Fire this to make the player directly chat under ROBLOX's C++ API.
+ -- This will fire the LocalPlayer's Chatted event.
+ -- Please only fire this on the player's behalf. If you attempt to spoof a player's chat
+ -- to get them in trouble, you could face serious moderation action.
+)
+
+AddObjects("BindableFunction","ChatWindow",
+ "IsFocused" -- This will be invoked by the CoreGui when it wants to check if the chat window is active.
+)
+
+-- The following events are fired if the user calls StarterGui:SetCore(string name, Variant data)
+-- Note that you can only hook onto these ones specifically.
+AddObjects("BindableEvent","SetCore",
+ "ChatMakeSystemMessage",
+ "ChatWindowPosition",
+ "ChatWindowSize",
+ "ChatBarDisabled"
+)
+
+-- The following functions are invoked if the user calls StarterGui:GetCore(string name)
+-- Note that you can only hook onto these ones specifically.
+AddObjects("BindableFunction","GetCore",
+ "ChatWindowPosition", -- Should return a UDim2 representing the position of the chat window.
+ "ChatWindowSize", -- Should return a UDim2 representing the size of the chat window.
+ "ChatBarDisabled" -- Should return true if the chat bar is currently disabled.
+)
+
+-- Connect ChatConnections to the CoreGui.
+local StarterGui = game:GetService("StarterGui")
+local GuiService = game:GetService("GuiService")
+
+if not GuiService:IsTenFootInterface() then
+ local tries = 0
+ local maxAttempts = 30
+
+ while (tries < maxAttempts) do
+ local success,result = pcall(function ()
+ StarterGui:SetCore("CoreGuiChatConnections", ChatConnections)
+ end)
+ if success then
+ break
+ else
+ tries = tries + 1
+ if tries == maxAttempts then
+ error("Error calling SetCore CoreGuiChatConnections: " .. result)
+ else
+ wait()
+ end
+ end
+ end
+end
+
+return ChatConnections
+
+
+ -
+
+ LinkedList
+ local LinkedList = {}
+LinkedList.__index = LinkedList
+
+function LinkedList:Add(data)
+ local node = {}
+ node.data = data
+ node.id = tostring(node):sub(8)
+
+ local back = self.back
+ if back then
+ back.next = node
+ node.prev = back
+ end
+
+ if not self.front then
+ self.front = node
+ end
+
+ self.back = node
+ self.size = self.size + 1
+
+ self.nodes[node.id] = node
+ self.lookup[data] = node
+
+ return node.id
+end
+
+function LinkedList:Get(id)
+ local node = self.nodes[id]
+ if node then
+ return node.data
+ end
+end
+
+function LinkedList:Remove(id)
+ local node = self.nodes[id]
+
+ if node then
+ if node.prev then
+ node.prev.next = node.next
+ end
+
+ if node.next then
+ node.next.prev = node.prev
+ end
+
+ if node == self.front then
+ self.front = node.next
+ end
+
+ if node == self.back then
+ self.back = node.prev
+ end
+
+ if node.data then
+ node.data = nil
+ end
+
+ self.size = self.size - 1
+ end
+end
+
+function LinkedList:GetEnumerator()
+ return coroutine.wrap(function ()
+ local node = self.front
+ while node ~= nil do
+ coroutine.yield(node.id, node.data)
+ node = node.next
+ end
+ end)
+end
+
+function LinkedList.new()
+ local list =
+ {
+ nodes = {};
+ lookup = {};
+ size = 0;
+ }
+
+ return setmetatable(list, LinkedList)
+end
+
+return LinkedList
+
+
+ -
+
+ MessageTemplate
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0.6000000238418579
+ 0.6000000238418579
+ 0.6000000238418579
+
+ 0.6000000238418579
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 0
+ 16
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ Message
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 1
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ 0
+
+
+
+ 1
+ 1
+ 1
+
+ false
+ 16
+
+ 1
+ 1
+ 1
+
+ 0.8999999761581421
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 1
+
+
+ -
+
+ PlayerName
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+ false
+ 16
+
+ 0
+ 0
+ 0
+
+ 0.8999999761581421
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 1
+
+
+ -
+
+ AutoLayout
+ true
+
+ 0
+ 1
+
+
0
+ 0
+
+ 2
+
+ 1
+
+
+
+
+ -
+
+ Health
+ local StarterGui = game:GetService("StarterGui")
+StarterGui:SetCoreGuiEnabled("All",false)
+
+local health = script.Parent
+local redBar = health:WaitForChild("RedBar")
+local greenBar = redBar:WaitForChild("GreenBar")
+
+local player = game.Players.LocalPlayer
+local c = workspace.CurrentCamera
+
+if c.ViewportSize.Y < 600 then
+ local scale = Instance.new("UIScale")
+ scale.Scale = 0.6
+ scale.Parent = health
+end
+
+local function onCharacterAdded(char)
+ local humanoid = char:WaitForChild("Humanoid")
+
+ local function updateHealth(health)
+ greenBar.Size = UDim2.new(1, 0, health / humanoid.MaxHealth, 0)
+ end
+
+ updateHealth(humanoid.MaxHealth)
+ humanoid.HealthChanged:Connect(updateHealth)
+end
+
+if player.Character then
+ onCharacterAdded(player.Character)
+end
+
+player.CharacterAdded:Connect(onCharacterAdded)
+
+
+ -
+
+ Messages
+ local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+
+local gui = script.Parent
+local player = Players.LocalPlayer
+
+local hintBin = Instance.new("Folder")
+local msgNameFmt = "MsgLbl_%s [%s]"
+
+local function addMessage(sourceMsg,msgType)
+ local isInPlayer = (sourceMsg.Parent == player)
+ local msgType = sourceMsg.ClassName
+
+ if msgType == "Message" and isInPlayer then
+ msgType = "Player"
+ end
+
+ local msgTemp = script:WaitForChild(msgType)
+
+ local msg = msgTemp:Clone()
+ msg.Name = msgNameFmt:format(msgType, sourceMsg:GetFullName())
+
+ local textUpdater = sourceMsg:GetPropertyChangedSignal("Text")
+ local isUpdating = false
+
+ local function updateText()
+ if not isUpdating then
+ isUpdating = true
+
+ msg.Text = sourceMsg.Text
+ sourceMsg.Text = ""
+
+ if msgType ~= "Hint" then
+ msg.Visible = (#msg.Text > 0)
+ end
+
+ isUpdating = false
+ end
+ end
+
+ local function onAncestryChanged()
+ local desiredAncestor
+
+ if msgType == "Hint" then
+ desiredAncestor = hintBin
+ elseif isInPlayer then
+ desiredAncestor = player
+ else
+ desiredAncestor = workspace
+ end
+
+ if not sourceMsg:IsDescendantOf(desiredAncestor) then
+ msg:Destroy()
+ end
+ end
+
+ --[[
+ I have to parent the Hint somewhere where it won't render since it
+ draws even if the Hint has no text. The server will remove the object
+ by it's reference address even if I change the parent, so this isn't a
+ problem online. But I can't rely on this in a non-network scenario so
+ regular Hints will still be visible offline if they're in the Workspace :(
+ --]]
+
+ if msgType == "Hint" then
+ wait()
+ sourceMsg.Parent = hintBin
+ end
+
+ updateText()
+ textUpdater:Connect(updateText)
+ sourceMsg.AncestryChanged:Connect(onAncestryChanged)
+
+ msg.Parent = gui
+end
+
+local function registerMessage(obj)
+ if obj:IsA("Message") then
+ addMessage(obj)
+ end
+end
+
+for _,v in pairs(workspace:GetDescendants()) do
+ registerMessage(v)
+end
+
+for _,v in pairs(player:GetChildren()) do
+ registerMessage(v)
+end
+
+player.ChildAdded:Connect(registerMessage)
+workspace.DescendantAdded:Connect(registerMessage)
+
+
-
+
+ Hint
+ false
+
+ 0
+ 1
+
+ true
+
+ true
+
+ 0
+ 0
+ 0
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0
+ 20
+
+ 0
+
+
+
+ 0.8823530077934265
+ 0.8823530077934265
+ 0.8823530077934265
+
+ false
+ 16
+
+ 0.7843137979507446
+ 0.7843137979507446
+ 0.7843137979507446
+
+ 0.8999999761581421
+ 0
+ 0
+ false
+ 2
+ 2
+ true
+ 1
+
+
+ -
+
+ Message
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0.498039186000824
+ 0.498039186000824
+ 0.498039186000824
+
+ 0.5
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ -36
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 36
+
+ 0
+
+
+
+ 1
+ 1
+ 1
+
+ false
+ 18
+
+ 0
+ 0
+ 0
+
+ 0.10000000149011612
+ 0.10000000149011612
+ 0
+ true
+ 2
+ 1
+ false
+ 1
+
+
+ -
+
+ Player
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0.5882353186607361
+ 0.5882353186607361
+ 0.5882353186607361
+
+ 0.5
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 32
+ 0
+ 5
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.25
+ 0
+ 0.25
+ 0
+
+ 0
+
+ Test
+
+ 1
+ 1
+ 1
+
+ false
+ 18
+
+ 0
+ 0
+ 0
+
+ 0.10000000149011612
+ 0.10000000149011612
+ 0
+ true
+ 2
+ 1
+ true
+ 10
+
+
+
+ -
+
+ PlayerList
+ local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+local Teams = game:GetService("Teams")
+
+spawn(function ()
+ local StarterGui = game:GetService("StarterGui")
+ StarterGui:SetCoreGuiEnabled("PlayerList", false)
+
+ local player = Players.LocalPlayer
+
+ local playerGui = player:WaitForChild("PlayerGui")
+ playerGui:SetTopbarTransparency(1)
+end)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Setup
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local playerStates = {}
+local teamGroups = {}
+
+local statLookup = {}
+local statNames = {}
+
+local inTeamMode = false
+
+local basePlayerLbl = script:WaitForChild("BasePlayerLbl")
+local baseGroup = script:WaitForChild("BaseGroup")
+local baseStat = script:WaitForChild("BaseStat")
+
+local playerList = script.Parent
+local backdrop = playerList:WaitForChild("Backdrop")
+local container = playerList:WaitForChild("Container")
+
+local coreGroup = baseGroup:Clone()
+coreGroup.Name = "Default"
+coreGroup.Parent = container
+
+local coreFooter = coreGroup.Footer
+local coreHeader = coreGroup.Header
+
+local eUpdateStatLayout = Instance.new("BindableEvent")
+local updateStatLayout = eUpdateStatLayout.Event
+
+local eUpdateTeamTotal = Instance.new("BindableEvent")
+local updateTeamTotal = eUpdateTeamTotal.Event
+
+local ePlayerTeamChanged = Instance.new("BindableEvent")
+local playerTeamChanged = ePlayerTeamChanged.Event
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Player Colors
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local PLAYER_COLORS =
+{
+ [0] = Color3.fromRGB(173, 35, 35); -- red
+ [1] = Color3.fromRGB( 42, 75, 215); -- blue
+ [2] = Color3.fromRGB( 29, 105, 20); -- green
+ [3] = Color3.fromRGB(129, 38, 192); -- purple
+ [4] = Color3.fromRGB(255, 146, 51); -- orange
+ [5] = Color3.fromRGB(255, 238, 51); -- yellow
+ [6] = Color3.fromRGB(255, 205, 243); -- pink
+ [7] = Color3.fromRGB(233, 222, 187); -- tan
+}
+
+local function computePlayerColor(player)
+ local pName = player.Name
+ local length = #pName
+ local oddShift = (1 - (length % 2))
+ local value = 0
+
+ for i = 1,length do
+ local char = pName:sub(i,i):byte()
+ local rev = (length - i) + oddShift
+ if (rev % 4) >= 2 then
+ value = value - char
+ else
+ value = value + char
+ end
+ end
+
+ return PLAYER_COLORS[value % 8]
+end
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Backdrop Handler
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local isTeamMode = false
+local hasStats = false
+
+local size1x1 = Vector2.new(1,1)
+local rawGroups = {}
+
+local function onContainerChildAdded(child)
+ if child:IsA("Frame") then
+ local listLayout = child:WaitForChild("ListLayout",2)
+ if listLayout then
+ rawGroups[child] = listLayout
+ end
+ end
+end
+
+local function onContainerChildRemoved(child)
+ if rawGroups[child] then
+ rawGroups[child] = nil
+ end
+end
+
+local function sortGroups(a,b)
+ if a == coreGroup then
+ return true
+ elseif b == coreGroup then
+ return false
+ else
+ local orderA,orderB = a.LayoutOrder,b.LayoutOrder
+ if orderA == orderB then
+ return a.Name < b.Name
+ else
+ return orderA < orderB
+ end
+ end
+end
+
+local function updateBackdrop()
+ local groups = {}
+ local at = 1
+
+ for group in pairs(rawGroups) do
+ if group.Visible then
+ groups[at] = group
+ at = at + 1
+ end
+ end
+
+ local height = 0
+ table.sort(groups,sortGroups)
+
+ for i = 1,#groups do
+ local group = groups[i]
+ local layout = rawGroups[group]
+ group.Position = UDim2.new(0,0,0,height)
+ height = height + layout.AbsoluteContentSize.Y
+ end
+
+ if #statNames > 0 and not hasStats then
+ hasStats = true
+ container.AnchorPoint = Vector2.new(1,0)
+ for _,group in pairs(groups) do
+ group.Header.TeamUnderline.Size = UDim2.new(2,-4,0,1)
+ end
+ eUpdateStatLayout:Fire()
+ elseif #statNames == 0 and hasStats then
+ hasStats = false
+ container.AnchorPoint = Vector2.new(0,0)
+ for _,group in pairs(groups) do
+ group.Header.TeamUnderline.Size = UDim2.new(1,-4,0,1)
+ end
+ eUpdateStatLayout:Fire()
+ end
+
+ if isTeamMode then
+ height = height + coreHeader.AbsoluteSize.Y
+ end
+
+ local width = container.AbsoluteSize.X * (container.AnchorPoint.X+1)
+ backdrop.Size = UDim2.new(0,width,0,height)
+end
+
+for _,child in pairs(container:GetChildren()) do
+ onContainerChildAdded(child)
+end
+
+container.ChildAdded:Connect(onContainerChildAdded)
+container.ChildRemoved:Connect(onContainerChildRemoved)
+RunService.Heartbeat:Connect(updateBackdrop)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Header Size Stuff
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local function switchHeaderMode(isTeamMode)
+ if isTeamMode then
+ coreHeader.Size = UDim2.new(1,0,1/3,0)
+ coreHeader.Stats.Size = UDim2.new(0.75,0,1,0)
+ coreHeader.Title.Size = UDim2.new(1,0,1,0)
+ else
+ coreHeader.Size = UDim2.new(1,0,0.4,0)
+ coreHeader.Stats.Size = UDim2.new(0.75,0,0.85,0)
+ coreHeader.Title.Size = UDim2.new(1,0,0.85,0)
+ end
+end
+
+switchHeaderMode(false)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Player Stats
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local function incrementStat(statName)
+ if not statLookup[statName] then
+ statLookup[statName] = 1
+ table.insert(statNames,statName)
+ else
+ statLookup[statName] = statLookup[statName] + 1
+ end
+ eUpdateStatLayout:Fire()
+end
+
+local function decrementStat(statName)
+ if statLookup[statName] then
+ statLookup[statName] = statLookup[statName] - 1
+
+ if statLookup[statName] == 0 then
+ statLookup[statName] = nil
+ for i,name in ipairs(statNames) do
+ if name == statName then
+ table.remove(statNames,i)
+ break
+ end
+ end
+ end
+
+ eUpdateStatLayout:Fire()
+ end
+end
+
+local function getPlayerStateFromStat(stat)
+ local leaderstats = stat.Parent
+ if leaderstats then
+ local player = leaderstats.Parent
+ if player then
+ return playerStates[player]
+ end
+ end
+end
+
+local function refreshTeamStats()
+ for _,team in pairs(Teams:GetTeams()) do
+ eUpdateTeamTotal:Fire(team)
+ end
+end
+
+local function onStatRemoved(stat,statName)
+ if stat.ClassName == "IntValue" then
+ local statName = statName or stat.Name
+ local playerState = getPlayerStateFromStat(stat)
+ if playerState and playerState.Stats[statName] then
+ playerState.Stats[statName]:Destroy()
+ playerState.Stats[statName] = nil
+ end
+ decrementStat(statName)
+ refreshTeamStats()
+ end
+end
+
+local function onStatAdded(stat)
+ if stat.ClassName == "IntValue" then
+ local statName = stat.Name
+ local playerState = getPlayerStateFromStat(stat)
+ if playerState then
+ local changeSignal
+
+ if not playerState.Stats[statName] then
+ local statLbl = baseStat:Clone()
+ statLbl.Name = statName
+
+ local function updateStat()
+ statLbl.Text = stat.Value
+ if isTeamMode then
+ local team = playerState.Player.Team
+ if team then
+ eUpdateTeamTotal:Fire(team)
+ end
+ end
+ end
+
+ updateStat()
+ changeSignal = stat.Changed:Connect(updateStat)
+
+ statLbl.Parent = playerState.Label.Stats
+ playerState.Stats[statName] = statLbl
+ end
+
+ local nameSignal do
+ local function onNameChanged()
+ if changeSignal then
+ changeSignal:Disconnect()
+ changeSignal = nil
+ end
+ nameSignal:Disconnect()
+ nameSignal = nil
+
+ -- Rebuild the stat
+ onStatRemoved(stat,statName)
+ onStatAdded(stat)
+ end
+
+ nameSignal = stat:GetPropertyChangedSignal("Name"):Connect(onNameChanged)
+ end
+ end
+
+ incrementStat(statName)
+ refreshTeamStats()
+ end
+end
+
+local function onPlayerChildAdded(leaderstats)
+ if leaderstats.Name == "leaderstats" then
+ local player = leaderstats.Parent
+ local playerState = playerStates[player]
+ if playerState and not playerState.leaderstats then
+ playerState.leaderstats = leaderstats
+ for _,stat in pairs(leaderstats:GetChildren()) do
+ onStatAdded(stat)
+ end
+ leaderstats.ChildAdded:Connect(onStatAdded)
+ leaderstats.ChildRemoved:Connect(onStatRemoved)
+ end
+ end
+end
+
+local function onPlayerChildRemoved(child)
+ if child.Name == "leaderstats" then
+ for _,stat in pairs(child:GetChildren()) do
+ onStatRemoved(stat)
+ end
+ for player,playerState in pairs(playerStates) do
+ if playerState.leaderstats == child then
+ playerState.leaderstats = nil
+ break
+ end
+ end
+ end
+end
+
+local function updateStatLbl(statLbl,index)
+ statLbl.Size = UDim2.new(1/#statNames,0,1,0)
+ statLbl.Position = UDim2.new((index-1)/#statNames)
+end
+
+local function onUpdateStatLayout()
+ local statBin = coreHeader.Stats
+
+ for _,statLbl in pairs(statBin:GetChildren()) do
+ if statLbl:IsA("TextLabel") and not statLookup[statLbl.Name] then
+ statLbl:Destroy()
+ end
+ end
+
+ for i,statName in pairs(statNames) do
+ local statLbl = statBin:FindFirstChild(statName)
+ if not statLbl then
+ statLbl = baseStat:Clone()
+ statLbl.Name = statName
+ statLbl.Text = statName
+ statLbl.Parent = statBin
+ end
+ updateStatLbl(statLbl,i)
+ end
+
+ for player,playerState in pairs(playerStates) do
+ for statName,statLbl in pairs(playerState.Stats) do
+ if not statLookup[statName] then
+ statLbl:Destroy()
+ playerState.Stats[statName] = nil
+ end
+ end
+
+ for i,statName in pairs(statNames) do
+ local statLbl = playerState.Stats[statName]
+ if statLbl then
+ if player.Team then
+ statLbl.TextColor = player.Team.TeamColor
+ else
+ statLbl.TextColor3 = Color3.new(1,1,1)
+ end
+ updateStatLbl(statLbl,i)
+ end
+ end
+ end
+
+ isTeamMode = (#Teams:GetTeams() > 0)
+
+ if isTeamMode then
+ coreHeader.Visible = hasStats
+ coreHeader.Title.Text = "Team"
+ else
+ coreHeader.Visible = true
+ if hasStats then
+ coreHeader.Title.Text = "Players"
+ else
+ coreHeader.Title.Text = "Player List"
+ end
+ end
+
+ switchHeaderMode(isTeamMode)
+end
+
+updateStatLayout:Connect(onUpdateStatLayout)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Player States
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local function onPlayerAdded(player)
+ local playerState = {}
+ local name = player.Name
+
+ local lbl = basePlayerLbl:Clone()
+ lbl.Name = name
+ lbl.PlayerName.Text = name
+ lbl.PlayerName.TextColor3 = computePlayerColor(player)
+ lbl.Parent = coreGroup
+
+ playerState.Player = player
+ playerState.Label = lbl
+ playerState.Stats = {}
+ playerStates[player] = playerState
+
+ for _,child in pairs(player:GetChildren()) do
+ onPlayerChildAdded(child)
+ end
+
+ player.ChildAdded:Connect(onPlayerChildAdded)
+ player.ChildRemoved:Connect(onPlayerChildRemoved)
+
+ player.Changed:Connect(function (property)
+ if property == "Team" then
+ ePlayerTeamChanged:Fire(player)
+ end
+ end)
+
+ ePlayerTeamChanged:Fire(player)
+end
+
+local function onPlayerRemoved(player)
+ local state = playerStates[player]
+ playerStates[player] = nil
+
+ if state and state.Label then
+ state.Label:Destroy()
+ end
+end
+
+for _,player in pairs(Players:GetPlayers()) do
+ onPlayerAdded(player)
+end
+
+Players.PlayerAdded:Connect(onPlayerAdded)
+Players.PlayerRemoving:Connect(onPlayerRemoved)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Teams
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local function neutralizePlayer(player)
+ local playerState = playerStates[player]
+ if playerState then
+ local playerLbl = playerState.Label
+ playerLbl.PlayerName.Text = player.Name
+ playerLbl.PlayerName.TextColor3 = computePlayerColor(player)
+ playerLbl.PlayerName.Position = UDim2.new(0,0,0,0)
+ for stat,statLbl in pairs(playerState.Stats) do
+ statLbl.TextColor3 = Color3.new(1,1,1)
+ end
+ playerLbl.Visible = (not isTeamMode)
+ playerLbl.Parent = coreGroup
+ end
+end
+
+local function onPlayerAddedToTeam(player)
+ local team = player.Team
+ local group = teamGroups[team]
+ if group then
+ local playerState = playerStates[player]
+ if playerState then
+ local playerLbl = playerState.Label
+ playerLbl.PlayerName.TextColor = team.TeamColor
+ playerLbl.PlayerName.Position = UDim2.new(0,4,0,0)
+ for stat,statLbl in pairs(playerState.Stats) do
+ statLbl.TextColor = team.TeamColor
+ end
+ playerLbl.Parent = group
+ playerLbl.Visible = true
+ eUpdateStatLayout:Fire()
+ refreshTeamStats()
+ end
+ end
+end
+
+local function onPlayerRemovedFromTeam(player)
+ if not player.Team then
+ neutralizePlayer(player)
+ refreshTeamStats()
+ end
+end
+
+local function onUpdateTeamTotal(team)
+ local teamGroup = teamGroups[team]
+ if teamGroup then
+ local teamStats = teamGroup.Header.Stats
+ local totals = {}
+
+ for i,statName in ipairs(statNames) do
+ local total = totals[i]
+ if not total then
+ total = { Name = statName, Value = 0 }
+ totals[i] = total
+ end
+ for _,player in pairs(team:GetPlayers()) do
+ local playerState = playerStates[player]
+ if playerState then
+ local leaderstats = playerState.leaderstats
+ if leaderstats then
+ local stat = leaderstats:FindFirstChild(statName)
+ if stat then
+ total.Value = total.Value + stat.Value
+ end
+ end
+ end
+ end
+ end
+
+ local numStats = #statNames
+
+ for i,statRecord in ipairs(totals) do
+ local statName = statRecord.Name
+ local statLbl = teamStats:FindFirstChild(statName)
+ if not statLbl then
+ statLbl = baseStat:Clone()
+ statLbl.Name = statName
+ statLbl.TextColor = team.TeamColor
+ statLbl.TextStrokeTransparency = 0.5
+ statLbl.Parent = teamStats
+ end
+ statLbl.Text = statRecord.Value
+ updateStatLbl(statLbl,i)
+ end
+
+ for _,statLbl in pairs(teamStats:GetChildren()) do
+ if not statLookup[statLbl.Name] then
+ statLbl:Destroy()
+ end
+ end
+ end
+end
+
+local function onTeamAdded(team)
+ if team.ClassName == "Team" then
+ local teamGroup = baseGroup:Clone()
+ teamGroup.Name = team.Name
+ teamGroup.Footer.Visible = true
+
+ local teamHeader = teamGroup.Header
+
+ local teamUnderline = teamHeader.TeamUnderline
+ teamUnderline.Visible = true
+ teamUnderline.BackgroundColor = team.TeamColor
+
+ if hasStats then
+ teamUnderline.Size = teamUnderline.Size + UDim2.new(1,0,0,0)
+ end
+
+ local teamTitle = teamHeader.Title
+ teamTitle.Text = team.Name
+ teamTitle.TextColor = team.TeamColor
+ teamTitle.TextStrokeTransparency = 0.5
+
+ teamGroup.Parent = container
+ teamGroups[team] = teamGroup
+
+ for _,player in pairs(team:GetPlayers()) do
+ onPlayerAddedToTeam(player)
+ end
+
+ eUpdateTeamTotal:Fire(team)
+ eUpdateStatLayout:Fire()
+ end
+ if #Teams:GetTeams() > 0 and not isTeamMode then
+ isTeamMode = true
+ for _,player in pairs(Players:GetPlayers()) do
+ if not player.Team then
+ neutralizePlayer(player)
+ end
+ end
+ end
+end
+
+local function onTeamRemoved(team)
+ if teamGroups[team] then
+ for _,player in pairs(Players:GetPlayers()) do
+ if player.TeamColor == team.TeamColor then
+ neutralizePlayer(player)
+ end
+ end
+ teamGroups[team]:Destroy()
+ teamGroups[team] = nil
+ eUpdateStatLayout:Fire()
+ end
+ if #Teams:GetTeams() == 0 then
+ isTeamMode = false
+ for _,player in pairs(Players:GetPlayers()) do
+ neutralizePlayer(player)
+ end
+ end
+end
+
+local function onPlayerTeamChange(player)
+ local team = player.Team
+ if team then
+ onPlayerAddedToTeam(player)
+ else
+ onPlayerRemovedFromTeam(player)
+ end
+end
+
+for _,team in pairs(Teams:GetTeams()) do
+ onTeamAdded(team)
+end
+
+for _,player in pairs(Players:GetPlayers()) do
+ onPlayerTeamChange(player)
+end
+
+Teams.ChildAdded:Connect(onTeamAdded)
+Teams.ChildRemoved:Connect(onTeamRemoved)
+updateTeamTotal:Connect(onUpdateTeamTotal)
+playerTeamChanged:Connect(onPlayerTeamChange)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+
+
-
+
+ BaseGroup
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ -9999
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ Footer
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 1
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 999999999
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.16500000655651093
+ 0
+
+ 0
+ 0
+
+ false
+ 1
+
+
+ -
+
+ Header
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ -9999999
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.3330000042915344
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ Title
+ false
+
+ 1
+ 0
+
+ true
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+
+ Player List
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ true
+ 0
+ 1
+ true
+ 2
+
+
+ -
+
+ Stats
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.75
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+
+ -
+
+ TeamUnderline
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ -1
+ 2
+ 0.875
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ -4
+ 0
+ 1
+
+ 0
+ 0
+
+ false
+ 2
+
+
+
+ -
+
+ ListLayout
+ true
+
+ 1
+ 1
+
+
0
+ 0
+
+ 2
+
+ 1
+
+
+
+ -
+
+ BasePlayerLbl
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 1
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.33329999446868896
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ PlayerName
+ false
+
+ 1
+ 0
+
+ true
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+
+ OnlyTwentyCharacters
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ true
+ 0
+ 1
+ true
+ 2
+
+
+ -
+
+ Stats
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.75
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+
+
+ -
+
+ BaseStat
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+
+ 0
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ true
+ 0
+ 1
+ true
+ 2
+
+
+
+ -
+
+ RootFrame
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ ChatPadding
+
+
+
0
+ 20
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+ ClassicMouse
+ false
+
+ 0.5
+ 0.5
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+
+ rbxassetid://334630296
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 0
+ 80
+ 0
+ 80
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 10
+
+
+ -
+
+ SafeChat
+ true
+
+ 0
+ 1
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+
+
+
+
+
+ rbxassetid://991182833
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 22
+ 0.75
+ 0
+
+
+
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 32
+ 0
+ 30
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+ -
+
+ GamepadHint
+ false
+
+ 0.5
+ 0.75
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+
+ rbxasset://textures/ui/Settings/Help/XButtonDark@2x.png
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 5
+ 0
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 0.75
+ 0
+ 0.75
+ 0
+
+ 2
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ false
+ 1
+
+
+
+ -
+
+ ZoomControls
+ false
+
+ 1
+ 1
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 70
+ 0
+ 70
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ RotateUp
+ true
+
+ 0
+ 0
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+
+
+
+
+
+ rbxassetid://598662248
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+
+ -
+
+ RotateDown
+ true
+
+ 0
+ 0
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+
+
+
+
+
+ rbxassetid://598662248
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 35
+
+
+
+
+
+ [null]
+ -180
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+
+ -
+
+ ZoomIn
+ true
+
+ 0
+ 0
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+
+
+
+
+
+ rbxassetid://598663795
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 35
+ 0
+ 0
+
+
+
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+ -
+
+ Lock
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0.5
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+ 0
+
+ false
+ 3
+
+
+
+ -
+
+ ZoomOut
+ true
+
+ 0
+ 0
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+
+
+
+
+
+ rbxassetid://598665130
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+
+
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+
+ -
+
+ FirstPersonIndicator
+ false
+
+ 1
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+
+ rbxassetid://598702035
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 0
+ 0
+ -97
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 0
+ 168
+ 0
+ 42
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ false
+ 2
+
+
+
+ -
+
+ Health
+ false
+
+ 1
+ 0.5
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ -31
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 66
+ 0
+ 137
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ RedBar
+ false
+
+ 0.5
+ 0
+
+
+ true
+
+ 1
+ 0
+ 0
+
+ 0
+
+ 1
+ 0
+ 0
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 12
+ 0
+ 112
+
+ 0
+ 0
+
+ true
+ 2
+
+ -
+
+ GreenBar
+ false
+
+ 0.5
+ 1
+
+
+ true
+
+ 0.5058823823928833
+ 0.7725490927696228
+ 0.08627451211214066
+
+ 0
+
+ 0.5058823823928833
+ 0.7725490927696228
+ 0.08627451211214066
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+
+
+
+ -
+
+ HealthLbl
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 1
+ -24
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0
+ 24
+
+ 0
+
+ Health
+
+ 0
+ 0
+ 1
+
+ true
+ 18
+
+ 0
+ 0
+ 1
+
+ 0.8999999761581421
+ 0
+ 0
+ true
+ 2
+ 1
+ true
+ 2
+
+
+
+ -
+
+ Chat
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ ChatBar
+ true
+
+ 1
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.16862750053405762
+ 0.16862750053405762
+ 0.16862750053405762
+
+ 0
+ 0
+ true
+ false
+ false
+ 4
+ 0
+ 1
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 1
+ 0.7843137979507446
+
+ To chat click here or press the "/" key
+
+ 1
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ true
+ [null]
+ true
+
+ 1
+ -3
+ 0
+ 15
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+ true
+ false
+ 15
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 3
+
+ -
+
+ FocusBackdrop
+ false
+
+ 1
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 3
+ 1
+ 0
+
+ 0
+ 0
+
+ false
+ 2
+
+
+
+ -
+
+ MainBackdrop
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.250980406999588
+ 0.250980406999588
+ 0.250980406999588
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0
+ 20
+
+ 0
+ 0
+
+ true
+ 1
+
+
+ -
+
+ ChatOutput
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ true
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 23
+ 0
+ 29
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0
+ 96
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ Stream
+
+ 1
+ 1
+
+
0
+ 0
+
+ 2
+
+ 2
+
+
+
+
+ -
+
+ Backpack
+ true
+
+ 0
+ 1
+
+
+ true
+
+ 0.498039186000824
+ 0.498039186000824
+ 0.498039186000824
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 2
+ 0
+
+ true
+ 1
+
+ -
+
+ UIScale
+
+ 0.13300000131130219
+
+
+
+ -
+
+ Backdrop
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.7058823704719543
+ 0.7058823704719543
+ 0.7058823704719543
+
+ 0.5
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 2
+
+
+ -
+
+ Slots
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ UIListLayout
+
+ 0
+ 1
+
+
0
+ 0
+
+ 2
+
+ 1
+
+
+
+
+ -
+
+ PlayerList
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ -36
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 36
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ Backdrop
+ false
+
+ 1
+ 0
+
+
+ true
+
+ 0.6000000238418579
+ 0.6000000238418579
+ 0.6000000238418579
+
+ 0.4000000059604645
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ -10
+ 0
+ 10
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 10
+ 0
+ 10
+
+ 0
+ 0
+
+ true
+ 1
+
+
+ -
+
+ Container
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ -10
+ 0
+ 10
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.16500000655651093
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ AspectRatio
+ 3
+ 0
+
+ 0
+
+
+
+
+
+
+ -
+
+ SafeChat
+ local UserInputService = game:GetService("UserInputService")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local GuiService = game:GetService("GuiService")
+
+local c = workspace.CurrentCamera
+local resUpdate = c:GetPropertyChangedSignal("ViewportSize")
+local safeChat = script.Parent
+local click = script:WaitForChild("Click")
+
+local IMG_CHAT = "rbxassetid://991182833"
+local IMG_CHAT_DN = "rbxassetid://991182832"
+local IMG_CHAT_OVR = "rbxassetid://991182834"
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Fetch Tree Data
+
+local mSafeChatTree = ReplicatedStorage:WaitForChild("SafeChatTree")
+local safeChatTree = require(mSafeChatTree)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Button Positioning
+
+local IS_PHONE = c.ViewportSize.Y < 600
+
+local function onResolutionUpdate()
+ local viewPort = c.ViewportSize
+ local chatX = math.min(25,viewPort.Y/40)
+ local chatY = (viewPort.X/viewPort.Y) * (viewPort.Y * 0.225)
+ safeChat.Position = UDim2.new(0,chatX,1,-chatY)
+end
+
+onResolutionUpdate()
+resUpdate:Connect(onResolutionUpdate)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Safe Chat Tree
+
+local chatRemote = ReplicatedStorage:WaitForChild("ChatRemote")
+local tempBranch = script:WaitForChild("TempBranch")
+local tempButton = script:WaitForChild("TempButton")
+local isActivated = false
+local rootTree
+
+local function recursivelyDeactivateTree(obj)
+ if obj:IsA("Frame") then
+ obj.Visible = false
+ elseif obj:IsA("TextButton") then
+ obj.BackgroundColor3 = Color3.new(1,1,1)
+ end
+ for _,v in pairs(obj:GetChildren()) do
+ recursivelyDeactivateTree(v)
+ end
+end
+
+local function deactivateRootTree()
+ isActivated = false
+ safeChat.Image = IMG_CHAT
+ recursivelyDeactivateTree(rootTree)
+
+ if GuiService.SelectedObject then
+ GuiService.SelectedObject = nil
+ GuiService:RemoveSelectionGroup("SafechatNav")
+ end
+end
+
+local function activateRootTree()
+ isActivated = true
+ rootTree.Visible = true
+ safeChat.Image = IMG_CHAT_DN
+
+ if UserInputService:GetLastInputType() == Enum.UserInputType.Gamepad1 then
+ GuiService:AddSelectionParent("SafechatNav",safeChat)
+ GuiService.SelectedObject = safeChat
+ end
+end
+
+local function assembleTree(tree)
+ local treeFrame = tempBranch:Clone()
+ treeFrame.Name = "Branches"
+
+ local currentBranch
+
+ for i,branch in ipairs(tree.Branches) do
+ local label = branch.Label
+ local branches = branch.Branches
+ local button = tempButton:Clone()
+ button.Name = label
+ button.Text = label
+ button.LayoutOrder = i
+ local branchFrame = assembleTree(branch)
+ branchFrame.Parent = button
+ button.Parent = treeFrame
+
+ local function onEnter()
+ if currentBranch then
+ recursivelyDeactivateTree(currentBranch)
+ end
+ currentBranch = button
+ button.BackgroundColor3 = Color3.new(0.7,0.7,0.7)
+ branchFrame.Visible = true
+ end
+
+ local function onActivate()
+ local submit = true
+ if UserInputService.TouchEnabled then
+ if not branchFrame.Visible and #branchFrame:GetChildren() > 1 then
+ branchFrame.Visible = true
+ submit = false
+ end
+ end
+ if submit then
+ deactivateRootTree()
+ chatRemote:FireServer(label)
+ click:Play()
+ end
+ end
+
+ button.MouseEnter:Connect(onEnter)
+ button.SelectionGained:Connect(onEnter)
+ button.MouseButton1Down:Connect(onActivate)
+ end
+
+ return treeFrame
+end
+
+rootTree = assembleTree(safeChatTree)
+rootTree.Parent = safeChat
+
+if IS_PHONE then
+ local uiScale = Instance.new("UIScale")
+ uiScale.Parent = rootTree
+ uiScale.Scale = 0.7
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Button State
+
+local isActivated = false
+local isHovering = false
+
+do
+ local function onMouseEnter()
+ if not isActivated then
+ safeChat.Image = IMG_CHAT_OVR
+ end
+ isHovering = true
+ end
+
+ local function onMouseLeave()
+ if not isActivated then
+ safeChat.Image = IMG_CHAT
+ end
+ isHovering = false
+ end
+
+ local function onMouseDown()
+ safeChat.Image = IMG_CHAT_DN
+ end
+
+ local function onMouseUp()
+ if isHovering then
+ activateRootTree()
+ end
+ end
+
+ local function onInputBegan(input,gameProcessed)
+ if input.UserInputType == Enum.UserInputType.MouseButton1 and not gameProcessed and isActivated then
+ deactivateRootTree()
+ end
+ end
+
+ safeChat.MouseEnter:Connect(onMouseEnter)
+ safeChat.MouseLeave:Connect(onMouseLeave)
+ safeChat.MouseButton1Down:Connect(onMouseDown)
+ safeChat.MouseButton1Up:Connect(onMouseUp)
+
+ UserInputService.InputBegan:Connect(onInputBegan)
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Gamepad Stuff
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local gamepadHint = safeChat:WaitForChild("GamepadHint")
+
+if GuiService:IsTenFootInterface() then
+ gamepadHint.Visible = true
+else
+ local function onLastInputTypeChanged(inputType)
+ gamepadHint.Visible = (inputType.Name == "Gamepad1")
+ end
+
+ onLastInputTypeChanged(UserInputService:GetLastInputType())
+ UserInputService.LastInputTypeChanged:Connect(onLastInputTypeChanged)
+end
+
+
+local function onInputBegan(input)
+ if input.KeyCode == Enum.KeyCode.ButtonX then
+ activateRootTree()
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+
+
-
+
+ Click
+ true
+
+ 10
+ false
+ 10000
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/switch.mp3
+
+
+ 0
+ 0.5
+ 10
+
+
+ -
+
+ NullSelectionImageObject
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+
+ -
+
+ TempBranch
+ false
+
+ 0
+ 0.5
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 5
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 140
+ 0
+ 24
+
+ 0
+ 0
+
+ false
+ 2
+
+ -
+
+ UIListLayout
+ true
+
+ 1
+ 1
+
+
0
+ 5
+
+ 2
+
+ 0
+
+
+
+ -
+
+ TempButton
+ true
+
+ 0
+ 0
+
+ true
+
+ false
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.24705879390239716
+ 0.24705879390239716
+ 0.24705879390239716
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ false
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 140
+ 0
+ 24
+
+ 0
+ 0
+
+ ROOT
+
+ 0
+ 0
+ 0
+
+ false
+ 12
+
+ 0
+ 0
+ 0
+
+ 0.949999988079071
+ 0.25
+ 0
+ false
+ 2
+ 1
+ true
+ 2
+
+
+
+
+
+ -
+
+ StarterPlayerScripts
+
+
-
+
+ Animator
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Services
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local CollectionService = game:GetService("CollectionService")
+local RunService = game:GetService("RunService")
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Animator Data
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local Animators = {}
+
+local function createAnimator(humanoid)
+ local Figure = humanoid.Parent
+ local Torso = Figure:WaitForChild("Torso")
+ local Climbing = Figure:WaitForChild("Climbing")
+
+ local animator = {}
+ animator.Joints = {}
+
+ do
+ local joints =
+ {
+ RightShoulder = Torso:WaitForChild("Right Shoulder", 5);
+ LeftShoulder = Torso:WaitForChild("Left Shoulder", 5);
+ RightHip = Torso:WaitForChild("Right Hip", 5);
+ LeftHip = Torso:WaitForChild("Left Hip", 5);
+ }
+
+ if not (joints.RightShoulder and joints.LeftShoulder) then
+ return
+ end
+
+ if not (joints.RightHip and joints.LeftHip) then
+ return
+ end
+
+ for name, joint in pairs(joints) do
+ local object =
+ {
+ JointObject = joint;
+ MaxVelocity = joint.MaxVelocity;
+ DesiredAngle = joint.DesiredAngle;
+ CurrentAngle = joint.CurrentAngle;
+ }
+
+ animator.Joints[name] = object
+ end
+ end
+
+ local joints = animator.Joints
+
+ local pi = math.pi
+ local sin = math.sin
+
+ local pose = "Standing"
+ local toolAnim = "None"
+ local toolAnimTime = 0
+
+ local RightShoulder = joints.RightShoulder
+ local LeftShoulder = joints.LeftShoulder
+
+ local RightHip = joints.RightHip
+ local LeftHip = joints.LeftHip
+
+ function animator:SetMaxVelocities(value)
+ RightShoulder.MaxVelocity = value
+ LeftShoulder.MaxVelocity = value
+
+ RightHip.MaxVelocity = value
+ LeftHip.MaxVelocity = value
+ end
+
+ function animator:Update()
+ local now = tick()
+
+ if Climbing.Value then
+ pose = "Climbing"
+ else
+ local stateType = humanoid:GetState()
+ pose = stateType.Name
+
+ if pose == "Running" then
+ local speed = humanoid.WalkSpeed
+ local movement = (Torso.Velocity * Vector3.new(1, 0, 1)).Magnitude
+
+ if (speed * movement) < 1 then
+ pose = "Standing"
+ end
+ end
+ end
+
+ if pose == "Jumping" then
+ self:SetMaxVelocities(.5)
+
+ RightShoulder.DesiredAngle = 1
+ LeftShoulder.DesiredAngle = -1
+
+ RightHip.DesiredAngle = 0
+ LeftHip.DesiredAngle = 0
+ elseif pose == "Freefall" then
+ self:SetMaxVelocities(.5)
+
+ RightShoulder.DesiredAngle = pi
+ LeftShoulder.DesiredAngle = -pi
+
+ RightHip.DesiredAngle = 0
+ LeftHip.DesiredAngle = 0
+ elseif pose == "Seated" then
+ self:SetMaxVelocities(.15)
+
+ RightShoulder.DesiredAngle = pi / 2
+ LeftShoulder.DesiredAngle = -pi / 2
+
+ RightHip.DesiredAngle = pi / 2
+ LeftHip.DesiredAngle = -pi / 2
+ else
+ local climbFudge = 0
+ local amplitude = .1
+ local frequency = 1
+
+ if pose == "Running" then
+ self:SetMaxVelocities(0.15)
+ amplitude = 1
+ frequency = 9
+ elseif pose == "Climbing" then
+ self:SetMaxVelocities(0.5)
+ climbFudge = pi
+
+ amplitude = 1
+ frequency = 9
+ end
+
+ local desiredAngle = amplitude * sin(now * frequency)
+
+ RightShoulder.DesiredAngle = desiredAngle + climbFudge
+ LeftShoulder.DesiredAngle = desiredAngle - climbFudge
+
+ RightHip.DesiredAngle = -desiredAngle
+ LeftHip.DesiredAngle = -desiredAngle
+
+ local tool = Figure:FindFirstChildWhichIsA("Tool")
+
+ if tool and tool.RequiresHandle and not CollectionService:HasTag(tool, "Flag") then
+ local animString = tool:FindFirstChild("toolanim")
+
+ if animString and animString:IsA("StringValue") then
+ -- apply tool animation
+ toolAnim = animString.Value
+ toolAnimTime = now + .3
+
+ -- delete event sender
+ animString:Destroy()
+ end
+
+ if now > toolAnimTime then
+ toolAnimTime = 0
+ toolAnim = "None"
+ end
+
+ if toolAnim == "None" then
+ RightShoulder.DesiredAngle = pi / 2
+ elseif toolAnim == "Slash" then
+ RightShoulder.MaxVelocity = 0.5
+ RightShoulder.DesiredAngle = 0
+ elseif toolAnim == "Lunge" then
+ self:SetMaxVelocities(0.5)
+
+ RightShoulder.DesiredAngle = pi / 2
+ RightHip.DesiredAngle = pi / 2
+
+ LeftShoulder.DesiredAngle = 1
+ LeftHip.DesiredAngle = 1
+ end
+ else
+ toolAnim = "None"
+ toolAnimTime = 0
+ end
+ end
+ end
+
+ return animator
+end
+
+local function onAnimatorAdded(humanoid)
+ if humanoid:IsA("Humanoid") then
+ local animator = createAnimator(humanoid)
+ Animators[humanoid] = animator
+ end
+end
+
+local function onAnimatorRemoved(humanoid)
+ if Animators[humanoid] then
+ Animators[humanoid] = nil
+ end
+end
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Collection Handler
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local animTag = "Animator"
+
+local animAdded = CollectionService:GetInstanceAddedSignal(animTag)
+local animRemoved = CollectionService:GetInstanceRemovedSignal(animTag)
+
+for _,humanoid in pairs(CollectionService:GetTagged(animTag)) do
+ spawn(function ()
+ onAnimatorAdded(humanoid)
+ end)
+end
+
+animAdded:Connect(onAnimatorAdded)
+animRemoved:Connect(onAnimatorRemoved)
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Motor Angle Updater
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local desiredFPS = 1 / 30 -- The framerate that would be expected given the MaxVelocity in use.
+local lastUpdate = tick()
+
+local function updateAnimations(deltaTime)
+ local velocityAdjust = (1 / desiredFPS) * deltaTime
+
+ for humanoid, animator in pairs(Animators) do
+ -- Update the motor states
+ animator:Update()
+
+ -- Step the motor angles
+ for name, jointData in pairs(animator.Joints) do
+ local joint = jointData.JointObject
+ local maxVelocity = jointData.MaxVelocity
+
+ local desiredAngle = jointData.DesiredAngle
+ local currentAngle = jointData.CurrentAngle
+
+ -- Adjust the MaxVelocity based on the current framerate
+ maxVelocity = math.abs(maxVelocity * velocityAdjust)
+
+ -- Update the CurrentAngle
+ local delta = (desiredAngle - currentAngle)
+
+ if math.abs(delta) < maxVelocity then
+ currentAngle = desiredAngle
+ elseif delta > 0 then
+ currentAngle = currentAngle + maxVelocity
+ else
+ currentAngle = currentAngle - maxVelocity
+ end
+
+ -- Apply the motor transform
+ joint.Transform = CFrame.Angles(0, 0, currentAngle)
+ jointData.CurrentAngle = currentAngle
+ end
+ end
+end
+
+RunService:BindToRenderStep("UpdateAnimations", 301, updateAnimations)
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+ -
+
+ Camera
+
+
-
+
+ Main
+ local Players = game:GetService("Players")
+local UserInputService = game:GetService("UserInputService")
+local StarterGui = game:GetService("StarterGui")
+local GuiService = game:GetService("GuiService")
+local ContextActionService = game:GetService("ContextActionService")
+local TeleportService = game:GetService("TeleportService")
+local Debris = game:GetService("Debris")
+
+local LocalPlayer = Players.LocalPlayer
+local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")
+local GameSettings = UserSettings():GetService("UserGameSettings")
+
+local math_abs = math.abs
+local math_asin = math.asin
+local math_atan2 = math.atan2
+local math_floor = math.floor
+local math_min = math.min
+local math_max = math.max
+local math_pi = math.pi
+local math_rad = math.rad
+local Vector2_new = Vector2.new
+local Vector3_new = Vector3.new
+local CFrame_Angles = CFrame.Angles
+local CFrame_new = CFrame.new
+
+local MIN_Y = math_rad(-80)
+local MAX_Y = math_rad(80)
+
+local ZERO_VECTOR2 = Vector2_new()
+local ZERO_VECTOR3 = Vector3_new()
+local UP_VECTOR = Vector3_new(0, 1, 0)
+local XZ_VECTOR = Vector3_new(1, 0, 1)
+
+local TOUCH_SENSITIVTY = Vector2_new(math_pi*2.25, math_pi*2)
+local MOUSE_SENSITIVITY = Vector2_new(math_pi*4, math_pi*1.9)
+
+local THUMBSTICK_DEADZONE = 0.2
+local DEADZONE = 0.1
+local ZOOM_FACTOR = 0.25
+
+local humanoid
+
+local function findPlayerHumanoid(player)
+ local character = player and player.Character
+ if character then
+ if not (humanoid and humanoid.Parent == character) then
+ humanoid = character:FindFirstChildOfClass("Humanoid")
+ end
+ end
+ return humanoid
+end
+
+local function clamp(low, high, num)
+ return (num > high and high or num < low and low or num)
+end
+
+local function findAngleBetweenXZVectors(vec2, vec1)
+ return math_atan2(vec1.X*vec2.Z-vec1.Z*vec2.X, vec1.X*vec2.X + vec1.Z*vec2.Z)
+end
+
+local function IsFinite(num)
+ return num == num and num ~= 1/0 and num ~= -1/0
+end
+
+local function SCurveTranform(t)
+ t = clamp(-1,1,t)
+ if t >= 0 then
+ return (.35*t) / (.35 - t + 1)
+ end
+ return -((.8*-t) / (.8 + t + 1))
+end
+
+local function toSCurveSpace(t)
+ return (1 + DEADZONE) * (2*math.abs(t) - 1) - DEADZONE
+end
+
+local function fromSCurveSpace(t)
+ return t/2 + 0.5
+end
+
+local function gamepadLinearToCurve(thumbstickPosition)
+ local function onAxis(axisValue)
+ local sign = 1
+ if axisValue < 0 then
+ sign = -1
+ end
+ local point = fromSCurveSpace(SCurveTranform(toSCurveSpace(math_abs(axisValue))))
+ point = point * sign
+ return clamp(-1, 1, point)
+ end
+ return Vector2_new(onAxis(thumbstickPosition.x), onAxis(thumbstickPosition.y))
+end
+
+-- Reset the camera look vector when the camera is enabled for the first time
+local SetCameraOnSpawn = true
+local this = {}
+
+local isFirstPerson = false
+local isRightMouseDown = false
+local isMiddleMouseDown = false
+
+this.Enabled = false
+this.RotateInput = ZERO_VECTOR2
+this.DefaultZoom = 10
+this.activeGamepad = nil
+this.PartSubjectHack = nil
+
+function this:GetHumanoid()
+ local player = Players.LocalPlayer
+ return findPlayerHumanoid(player)
+end
+
+function this:GetHumanoidRootPart()
+ local humanoid = this:GetHumanoid()
+ return humanoid and humanoid.Torso
+end
+
+function this:GetSubjectPosition()
+ local camera = workspace.CurrentCamera
+ local result = camera.Focus.p
+
+ local cameraSubject = camera and camera.CameraSubject
+ if cameraSubject then
+ if cameraSubject:IsA("Humanoid") then
+ local char = cameraSubject.Parent
+ if char then
+ local head = char:FindFirstChild("Head")
+ if head and head:IsA("BasePart") then
+ result = head.Position
+ end
+ end
+ if this.PartSubjectHack then
+ this:ZoomCamera(this.PartSubjectHack)
+ this.PartSubjectHack = nil
+ this:UpdateMouseBehavior()
+ end
+ elseif cameraSubject:IsA("BasePart") then
+ result = cameraSubject.Position
+ if not this.PartSubjectHack then
+ this.PartSubjectHack = this:GetCameraZoom()
+ this:ZoomCamera(10)
+ this:UpdateMouseBehavior()
+ end
+ end
+ end
+
+ return result
+end
+
+function this:GetCameraLook()
+ return workspace.CurrentCamera and workspace.CurrentCamera.CFrame.lookVector or Vector3.new(0,0,1)
+end
+
+function this:GetCameraZoom()
+ if this.currentZoom == nil then
+ local player = Players.LocalPlayer
+ this.currentZoom = player and clamp(player.CameraMinZoomDistance, player.CameraMaxZoomDistance, this.DefaultZoom) or this.DefaultZoom
+ end
+ return this.currentZoom
+end
+
+function this:GetCameraActualZoom()
+ local camera = workspace.CurrentCamera
+ if camera then
+ return (camera.CFrame.p - camera.Focus.p).Magnitude
+ end
+end
+
+function this:ViewSizeX()
+ local result = 1024
+ local camera = workspace.CurrentCamera
+ if camera then
+ result = camera.ViewportSize.X
+ end
+ return result
+end
+
+function this:ViewSizeY()
+ local result = 768
+ local camera = workspace.CurrentCamera
+ if camera then
+ result = camera.ViewportSize.Y
+ end
+ return result
+end
+
+function this:ScreenTranslationToAngle(translationVector)
+ local screenX = this:ViewSizeX()
+ local screenY = this:ViewSizeY()
+ local xTheta = (translationVector.x / screenX)
+ local yTheta = (translationVector.y / screenY)
+ return Vector2_new(xTheta, yTheta)
+end
+
+function this:MouseTranslationToAngle(translationVector)
+ local xTheta = (translationVector.x / 1920)
+ local yTheta = (translationVector.y / 1200)
+ return Vector2_new(xTheta, yTheta)
+end
+
+function this:RotateVector(startVector, xyRotateVector)
+ local startCFrame = CFrame_new(ZERO_VECTOR3, startVector)
+ local resultLookVector = (CFrame_Angles(0, -xyRotateVector.x, 0) * startCFrame * CFrame_Angles(-xyRotateVector.y,0,0)).lookVector
+ return resultLookVector, Vector2_new(xyRotateVector.x, xyRotateVector.y)
+end
+
+function this:RotateCamera(startLook, xyRotateVector)
+ local startVertical = math_asin(startLook.y)
+ local yTheta = clamp(-MAX_Y + startVertical, -MIN_Y + startVertical, xyRotateVector.y)
+ return self:RotateVector(startLook, Vector2_new(xyRotateVector.x, yTheta))
+end
+
+function this:IsInFirstPerson()
+ return isFirstPerson
+end
+
+function this:UpdateMouseBehavior()
+ if isFirstPerson or this.PartSubjectHack then
+ GameSettings.RotationType = Enum.RotationType.CameraRelative
+ UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
+ else
+ GameSettings.RotationType = Enum.RotationType.MovementRelative
+ if isRightMouseDown or isMiddleMouseDown then
+ UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
+ else
+ UserInputService.MouseBehavior = Enum.MouseBehavior.Default
+ end
+ end
+end
+
+function this:PlayTick()
+ local now = tick()
+ local lastTickSound = this.LastTickSound
+ if not lastTickSound then
+ lastTickSound = 0
+ end
+
+ if (now - lastTickSound) > .03 then
+ local s = Instance.new("Sound")
+ s.SoundId = "rbxasset://sounds/switch3.wav"
+ s.Parent = script
+ s:Play()
+ Debris:AddItem(s,1)
+ this.LastTickSound = now
+ end
+end
+
+function this:ZoomCamera(desiredZoom)
+ this.currentZoom = clamp(0.25, 400, desiredZoom)
+ isFirstPerson = self:GetCameraZoom() < 1.5
+
+ -- set mouse behavior
+ self:UpdateMouseBehavior()
+ return self:GetCameraZoom()
+end
+
+function this:ZoomCameraBy(input)
+ if TeleportService:GetTeleportSetting("FPSCapTo30") then
+ input = input * 1.5
+ end
+
+ local zoom = this:GetCameraActualZoom()
+ if zoom then
+ if input > 0 then
+ zoom = math.max( 1, zoom / (1 + ZOOM_FACTOR*input))
+ elseif input < 0 then
+ zoom = math.min(5000, zoom * (1 - ZOOM_FACTOR*input))
+ end
+ self:ZoomCamera(zoom)
+ end
+
+ self:PlayTick()
+ return self:GetCameraZoom()
+end
+
+function this:ZoomCameraFixedBy(zoomIncrement)
+ return self:ZoomCamera(self:GetCameraZoom() + zoomIncrement)
+end
+
+------------------------
+---- Input Events ----
+------------------------
+
+do
+ local startPos = nil
+ local lastPos = nil
+ local panBeginLook = nil
+ local lastTapTime = nil
+
+ local fingerTouches = {}
+ local NumUnsunkTouches = 0
+
+ local inputStartPositions = {}
+ local inputStartTimes = {}
+
+ local StartingDiff = nil
+ local pinchBeginZoom = nil
+
+ local dynamicThumbstickFrame = nil
+ local flaggedDynamic = {}
+
+ local function getDynamicThumbstickFrame()
+ if dynamicThumbstickFrame and dynamicThumbstickFrame:IsDescendantOf(game) then
+ return dynamicThumbstickFrame
+ else
+ local touchGui = PlayerGui:FindFirstChild("TouchGui")
+ if not touchGui then return nil end
+
+ local touchControlFrame = touchGui:FindFirstChild("TouchControlFrame")
+ if not touchControlFrame then return nil end
+
+ dynamicThumbstickFrame = touchControlFrame:FindFirstChild("DynamicThumbstickFrame")
+ return dynamicThumbstickFrame
+ end
+ end
+
+ this.ZoomEnabled = true
+ this.PanEnabled = true
+ this.KeyPanEnabled = true
+
+ local function inputIsDynamic(input)
+ if flaggedDynamic[input] ~= nil then
+ return flaggedDynamic[input]
+ end
+
+ if GameSettings.TouchMovementMode ~= Enum.TouchMovementMode.DynamicThumbstick then
+ return false
+ end
+
+ local df = getDynamicThumbstickFrame()
+ if not df then return end
+
+ local pos = input.Position
+ local p0 = df.AbsolutePosition
+ local p1 = p0 + df.AbsoluteSize
+
+ if p0.X <= pos.X and p0.Y <= pos.Y then
+ if pos.X <= p1.X and pos.Y <= p1.Y then
+ flaggedDynamic[input] = true
+ return true
+ end
+ end
+
+ flaggedDynamic[input] = false
+ return false
+ end
+
+ local function OnTouchBegan(input, processed)
+ if not inputIsDynamic(input) then
+ fingerTouches[input] = processed
+ if not processed then
+
+ inputStartPositions[input] = input.Position
+ inputStartTimes[input] = tick()
+ NumUnsunkTouches = NumUnsunkTouches + 1
+ end
+ end
+ end
+
+ local function OnTouchChanged(input, processed)
+ if inputIsDynamic(input) then
+ return
+ end
+
+ if fingerTouches[input] == nil then
+ fingerTouches[input] = processed
+ if not processed then
+ NumUnsunkTouches = NumUnsunkTouches + 1
+ end
+ end
+
+ if NumUnsunkTouches == 1 then
+ if fingerTouches[input] == false then
+ panBeginLook = panBeginLook or this:GetCameraLook()
+ startPos = startPos or input.Position
+ lastPos = lastPos or startPos
+ this.UserPanningTheCamera = true
+
+ local delta = input.Position - lastPos
+
+ delta = Vector2.new(delta.X, delta.Y * GameSettings:GetCameraYInvertValue())
+
+ if this.PanEnabled then
+ local desiredXYVector = this:ScreenTranslationToAngle(delta) * TOUCH_SENSITIVTY
+ this.RotateInput = this.RotateInput + desiredXYVector
+ end
+
+ lastPos = input.Position
+ end
+ else
+ panBeginLook = nil
+ startPos = nil
+ lastPos = nil
+ this.UserPanningTheCamera = false
+ end
+
+ if NumUnsunkTouches == 2 then
+ local unsunkTouches = {}
+ for touch, wasSunk in pairs(fingerTouches) do
+ if not wasSunk then
+ table.insert(unsunkTouches, touch)
+ end
+ end
+ if #unsunkTouches == 2 then
+ local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
+ if StartingDiff and pinchBeginZoom then
+ local scale = difference / math_max(0.01, StartingDiff)
+ local clampedScale = clamp(0.1, 10, scale)
+ if this.ZoomEnabled then
+ this:ZoomCamera(pinchBeginZoom / clampedScale)
+ this:PlayTick()
+ end
+ else
+ StartingDiff = difference
+ pinchBeginZoom = this:GetCameraActualZoom()
+ end
+ end
+ else
+ StartingDiff = nil
+ pinchBeginZoom = nil
+ end
+ end
+
+ local function calcLookBehindRotateInput(torso)
+ if torso then
+ local newDesiredLook = (torso.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
+ local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
+ local vertShift = math_asin(this:GetCameraLook().y) - math_asin(newDesiredLook.y)
+ if not IsFinite(horizontalShift) then
+ horizontalShift = 0
+ end
+ if not IsFinite(vertShift) then
+ vertShift = 0
+ end
+
+ return Vector2.new(horizontalShift, vertShift)
+ end
+ return nil
+ end
+
+ local function OnTouchEnded(input, processed)
+ if fingerTouches[input] == false then
+ if NumUnsunkTouches == 1 then
+ panBeginLook = nil
+ startPos = nil
+ lastPos = nil
+ elseif NumUnsunkTouches == 2 then
+ StartingDiff = nil
+ pinchBeginZoom = nil
+ end
+ end
+
+ if fingerTouches[input] ~= nil and fingerTouches[input] == false then
+ NumUnsunkTouches = NumUnsunkTouches - 1
+ end
+ fingerTouches[input] = nil
+ inputStartPositions[input] = nil
+ inputStartTimes[input] = nil
+ flaggedDynamic[input] = nil
+ end
+
+ local function OnMousePanButtonPressed(input, processed)
+ if processed then return end
+ this:UpdateMouseBehavior()
+ panBeginLook = panBeginLook or this:GetCameraLook()
+ startPos = startPos or input.Position
+ lastPos = lastPos or startPos
+ this.UserPanningTheCamera = true
+ end
+
+ local function OnMousePanButtonReleased(input, processed)
+ this:UpdateMouseBehavior()
+ if not (isRightMouseDown or isMiddleMouseDown) then
+ panBeginLook = nil
+ startPos = nil
+ lastPos = nil
+ this.UserPanningTheCamera = false
+ end
+ end
+
+ local function OnMouse2Down(input, processed)
+ if processed then return end
+
+ isRightMouseDown = true
+ OnMousePanButtonPressed(input, processed)
+ end
+
+ local function OnMouse2Up(input, processed)
+ isRightMouseDown = false
+ OnMousePanButtonReleased(input, processed)
+ end
+
+ local function OnMouse3Down(input, processed)
+ if processed then return end
+
+ isMiddleMouseDown = true
+ OnMousePanButtonPressed(input, processed)
+ end
+
+ local function OnMouse3Up(input, processed)
+ isMiddleMouseDown = false
+ OnMousePanButtonReleased(input, processed)
+ end
+
+ local function OnMouseMoved(input, processed)
+
+ local inputDelta = input.Delta
+ inputDelta = Vector2.new(inputDelta.X, inputDelta.Y * GameSettings:GetCameraYInvertValue())
+
+ if startPos and lastPos and panBeginLook then
+ local currPos = lastPos + input.Delta
+ local totalTrans = currPos - startPos
+ if this.PanEnabled then
+ local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
+ this.RotateInput = this.RotateInput + desiredXYVector
+ end
+ lastPos = currPos
+ elseif (this:IsInFirstPerson() or this.PartSubjectHack) and this.PanEnabled then
+ local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
+ this.RotateInput = this.RotateInput + desiredXYVector
+ end
+ end
+
+ local function OnMouseWheel(input, processed)
+ if not processed then
+ if this.ZoomEnabled then
+ this:ZoomCameraBy(clamp(-1, 1, input.Position.Z))
+ end
+ end
+ end
+
+ local function round(num)
+ return math_floor(num + 0.5)
+ end
+
+ local eight2Pi = math_pi / 4
+
+ local function rotateVectorByAngleAndRound(camLook, rotateAngle, roundAmount)
+ if camLook ~= ZERO_VECTOR3 then
+ camLook = camLook.unit
+ local currAngle = math_atan2(camLook.z, camLook.x)
+ local newAngle = round((math_atan2(camLook.z, camLook.x) + rotateAngle) / roundAmount) * roundAmount
+ return newAngle - currAngle
+ end
+ return 0
+ end
+
+ local function OnKeyDown(input, processed)
+ if processed then return end
+ if this.ZoomEnabled then
+ if input.KeyCode == Enum.KeyCode.I then
+ this:ZoomCameraBy(1)
+ elseif input.KeyCode == Enum.KeyCode.O then
+ this:ZoomCameraBy(-1)
+ end
+ end
+ if panBeginLook == nil and this.KeyPanEnabled then
+ if input.KeyCode == Enum.KeyCode.Left then
+ this.TurningLeft = true
+ elseif input.KeyCode == Enum.KeyCode.Right then
+ this.TurningRight = true
+ elseif input.KeyCode == Enum.KeyCode.Comma then
+ local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), -eight2Pi * (3/4), eight2Pi)
+ if angle ~= 0 then
+ this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
+ this.LastUserPanCamera = tick()
+ this.LastCameraTransform = nil
+ end
+ this:PlayTick()
+ elseif input.KeyCode == Enum.KeyCode.Period then
+ local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), eight2Pi * (3/4), eight2Pi)
+ if angle ~= 0 then
+ this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
+ this.LastUserPanCamera = tick()
+ this.LastCameraTransform = nil
+ end
+ this:PlayTick()
+ elseif input.KeyCode == Enum.KeyCode.PageUp then
+ this.RotateInput = this.RotateInput + Vector2.new(0,math_pi/12)
+ this.LastCameraTransform = nil
+ this:PlayTick()
+ elseif input.KeyCode == Enum.KeyCode.PageDown then
+ this.RotateInput = this.RotateInput + Vector2.new(0,-math_pi/12)
+ this.LastCameraTransform = nil
+ this:PlayTick()
+ end
+ end
+ end
+
+ local function OnKeyUp(input, processed)
+ if input.KeyCode == Enum.KeyCode.Left then
+ this.TurningLeft = false
+ elseif input.KeyCode == Enum.KeyCode.Right then
+ this.TurningRight = false
+ end
+ end
+
+ local lastThumbstickRotate = nil
+ local numOfSeconds = 0.7
+ local currentSpeed = 0
+ local maxSpeed = 6
+ local lastThumbstickPos = Vector2.new(0,0)
+ local ySensitivity = 0.65
+ local lastVelocity = nil
+
+ function this:UpdateGamepad()
+ local gamepadPan = this.GamepadPanningCamera
+ if gamepadPan then
+ gamepadPan = gamepadLinearToCurve(gamepadPan)
+ local currentTime = tick()
+ if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then
+ this.userPanningTheCamera = true
+ elseif gamepadPan == ZERO_VECTOR2 then
+ lastThumbstickRotate = nil
+ if lastThumbstickPos == ZERO_VECTOR2 then
+ currentSpeed = 0
+ end
+ end
+
+ local finalConstant = 0
+
+ if lastThumbstickRotate then
+ local elapsed = (currentTime - lastThumbstickRotate) * 10
+ currentSpeed = currentSpeed + (maxSpeed * ((elapsed*elapsed)/numOfSeconds))
+
+ if currentSpeed > maxSpeed then currentSpeed = maxSpeed end
+
+ if lastVelocity then
+ local velocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
+ local velocityDeltaMag = (velocity - lastVelocity).magnitude
+
+ if velocityDeltaMag > 12 then
+ currentSpeed = currentSpeed * (20/velocityDeltaMag)
+ if currentSpeed > maxSpeed then currentSpeed = maxSpeed end
+ end
+ end
+
+ local gamepadCameraSensitivity = GameSettings.GamepadCameraSensitivity
+ finalConstant = (gamepadCameraSensitivity * currentSpeed)
+ lastVelocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
+ end
+
+ lastThumbstickPos = gamepadPan
+ lastThumbstickRotate = currentTime
+
+ return Vector2_new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * ySensitivity * GameSettings:GetCameraYInvertValue())
+ end
+
+ return ZERO_VECTOR2
+ end
+
+ local InputEvents = {}
+
+ function this:DisconnectInputEvents()
+ -- Disconnect all input events.
+ while true do
+ local signalName = next(InputEvents)
+ if signalName then
+ InputEvents[signalName]:Disconnect()
+ InputEvents[signalName] = nil
+ else
+ break
+ end
+ end
+
+ this.TurningLeft = false
+ this.TurningRight = false
+ this.LastCameraTransform = nil
+ this.UserPanningTheCamera = false
+ this.RotateInput = ZERO_VECTOR2
+ this.GamepadPanningCamera = ZERO_VECTOR2
+
+ -- Reset input states
+ startPos = nil
+ lastPos = nil
+ panBeginLook = nil
+ isRightMouseDown = false
+ isMiddleMouseDown = false
+
+ fingerTouches = {}
+ NumUnsunkTouches = 0
+
+ StartingDiff = nil
+ pinchBeginZoom = nil
+
+ -- Unlock mouse for example if right mouse button was being held down
+ if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
+ UserInputService.MouseBehavior = Enum.MouseBehavior.Default
+ end
+ end
+
+ local function resetInputStates()
+ isRightMouseDown = false
+ isMiddleMouseDown = false
+ OnMousePanButtonReleased() -- this function doesn't seem to actually need parameters
+
+ if UserInputService.TouchEnabled then
+ --[[menu opening was causing serious touch issues
+ this should disable all active touch events if
+ they're active when menu opens.]]
+ for inputObject, value in pairs(fingerTouches) do
+ fingerTouches[inputObject] = nil
+ end
+ panBeginLook = nil
+ startPos = nil
+ lastPos = nil
+ this.UserPanningTheCamera = false
+ StartingDiff = nil
+ pinchBeginZoom = nil
+ NumUnsunkTouches = 0
+ end
+ end
+
+ local function getGamepadPan(name, state, input)
+ if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
+
+ if state == Enum.UserInputState.Cancel then
+ this.GamepadPanningCamera = ZERO_VECTOR2
+ return
+ end
+
+ local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
+ if inputVector.magnitude > THUMBSTICK_DEADZONE then
+ this.GamepadPanningCamera = Vector2_new(input.Position.X, -input.Position.Y)
+ else
+ this.GamepadPanningCamera = ZERO_VECTOR2
+ end
+ end
+ end
+
+ local function doGamepadZoom(name, state, input)
+ if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.ButtonR3 and state == Enum.UserInputState.Begin then
+ if this.ZoomEnabled then
+ if this:GetCameraZoom() > 0.5 then
+ this:ZoomCamera(0)
+ else
+ this:ZoomCamera(10)
+ end
+ end
+ end
+ end
+
+ local function assignActivateGamepad()
+ local connectedGamepads = UserInputService:GetConnectedGamepads()
+ if #connectedGamepads > 0 then
+ for i = 1, #connectedGamepads do
+ if this.activeGamepad == nil then
+ this.activeGamepad = connectedGamepads[i]
+ elseif connectedGamepads[i].Value < this.activeGamepad.Value then
+ this.activeGamepad = connectedGamepads[i]
+ end
+ end
+ end
+
+ if this.activeGamepad == nil then -- nothing is connected, at least set up for gamepad1
+ this.activeGamepad = Enum.UserInputType.Gamepad1
+ end
+ end
+
+ local function onInputBegan(input, processed)
+ if input.UserInputType == Enum.UserInputType.Touch then
+ OnTouchBegan(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
+ OnMouse2Down(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
+ OnMouse3Down(input, processed)
+ end
+ -- Keyboard
+ if input.UserInputType == Enum.UserInputType.Keyboard then
+ OnKeyDown(input, processed)
+ end
+ end
+
+ local function onInputChanged(input, processed)
+ if input.UserInputType == Enum.UserInputType.Touch then
+ OnTouchChanged(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseMovement then
+ OnMouseMoved(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseWheel then
+ OnMouseWheel(input, processed)
+ end
+ end
+
+ local function onInputEnded(input, processed)
+ if input.UserInputType == Enum.UserInputType.Touch then
+ OnTouchEnded(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
+ OnMouse2Up(input, processed)
+ elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
+ OnMouse3Up(input, processed)
+ end
+ -- Keyboard
+ if input.UserInputType == Enum.UserInputType.Keyboard then
+ OnKeyUp(input, processed)
+ end
+ end
+
+ local inputPassCmds =
+ {
+ ZoomIn = Enum.KeyCode.I;
+ ZoomOut = Enum.KeyCode.O;
+ RotateUp = Enum.KeyCode.PageUp;
+ RotateDown = Enum.KeyCode.PageDown;
+ }
+
+ local function onInputPassed(command)
+ local passKey = inputPassCmds[command]
+ if passKey then
+ OnKeyDown({KeyCode = passKey}, false)
+ end
+ end
+
+ local function onGamepadConnected(gamepadEnum)
+ if this.activeGamepad == nil then
+ assignActivateGamepad()
+ end
+ end
+
+ local function onGamepadDisconnected(gamepadEnum)
+ if this.activeGamepad ~= gamepadEnum then return end
+ this.activeGamepad = nil
+ assignActivateGamepad()
+ end
+
+ function this:ConnectInputEvents()
+ local player = Players.LocalPlayer
+ local playerScripts = player:WaitForChild("PlayerScripts")
+ local passCameraEvent = playerScripts:WaitForChild("PassCameraEvent")
+
+ this.RotateInput = ZERO_VECTOR2
+ this.activeGamepad = nil
+
+ InputEvents =
+ {
+ InputBegan = UserInputService.InputBegan:Connect(onInputBegan);
+ InputChanged = UserInputService.InputChanged:Connect(onInputChanged);
+ InputEnded = UserInputService.InputEnded:Connect(onInputEnded);
+ MenuOpened = GuiService.MenuOpened:Connect(resetInputStates);
+ MenuOpenedConn = GuiService.MenuOpened:Connect(resetInputStates);
+ GamepadConnected = UserInputService.GamepadConnected:Connect(onGamepadConnected);
+ GamepadDisconnected = UserInputService.GamepadDisconnected:Connect(onGamepadDisconnected);
+ InputPassed = passCameraEvent.Event:Connect(onInputPassed);
+ }
+
+ ContextActionService:BindAction("RootCamGamepadPan", getGamepadPan, false, Enum.KeyCode.Thumbstick2)
+ ContextActionService:BindAction("RootCamGamepadZoom", doGamepadZoom, false, Enum.KeyCode.ButtonR3)
+
+ assignActivateGamepad()
+
+ -- set mouse behavior
+ self:UpdateMouseBehavior()
+ end
+
+ function this:SetEnabled(newState)
+ if newState ~= self.Enabled then
+ self.Enabled = newState
+ if self.Enabled then
+ self:ConnectInputEvents()
+ else
+ self:DisconnectInputEvents()
+ end
+ end
+ end
+
+ local function OnPlayerAdded(player)
+ player.Changed:Connect(function (prop)
+ if this.Enabled then
+ if prop == "CameraMode" or prop == "CameraMaxZoomDistance" or prop == "CameraMinZoomDistance" then
+ this:ZoomCameraFixedBy(0)
+ end
+ end
+ end)
+
+ local function OnCharacterAdded(newCharacter)
+ local humanoid = findPlayerHumanoid(player)
+ local start = tick()
+ while tick() - start < 0.3 and (humanoid == nil or humanoid.Torso == nil) do
+ wait()
+ humanoid = findPlayerHumanoid(player)
+ end
+
+ if humanoid and humanoid.Torso and player.Character == newCharacter then
+ local newDesiredLook = (humanoid.Torso.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
+ local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
+ local vertShift = math_asin(this:GetCameraLook().y) - math_asin(newDesiredLook.y)
+ if not IsFinite(horizontalShift) then
+ horizontalShift = 0
+ end
+ if not IsFinite(vertShift) then
+ vertShift = 0
+ end
+ this.RotateInput = Vector2.new(horizontalShift, vertShift)
+
+ -- reset old camera info so follow cam doesn't rotate us
+ this.LastCameraTransform = nil
+ end
+
+ -- Need to wait for camera cframe to update before we zoom in
+ -- Not waiting will force camera to original cframe
+ wait()
+ this:ZoomCamera(this.DefaultZoom)
+ end
+
+ player.CharacterAdded:Connect(function (character)
+ if this.Enabled or SetCameraOnSpawn then
+ OnCharacterAdded(character)
+ SetCameraOnSpawn = false
+ end
+ end)
+
+ if player.Character then
+ spawn(function () OnCharacterAdded(player.Character) end)
+ end
+ end
+
+ if Players.LocalPlayer then
+ OnPlayerAdded(Players.LocalPlayer)
+ end
+
+ Players.ChildAdded:Connect(function (child)
+ if child and Players.LocalPlayer == child then
+ OnPlayerAdded(Players.LocalPlayer)
+ end
+ end)
+
+end
+
+------------------------
+---- Main Updater ----
+------------------------
+
+do
+ local tweenAcceleration = math_rad(220)
+ local tweenSpeed = math_rad(0)
+ local tweenMaxSpeed = math_rad(250)
+ local timeBeforeAutoRotate = 2
+
+ local lastUpdate = tick()
+ this.LastUserPanCamera = lastUpdate
+
+
+ function this:Update()
+ local now = tick()
+ local timeDelta = (now - lastUpdate)
+
+ local userPanningTheCamera = (self.UserPanningTheCamera == true)
+ local camera = workspace.CurrentCamera
+ local humanoid = self:GetHumanoid()
+ local cameraSubject = camera and camera.CameraSubject
+ local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')
+ local isOnASkateboard = cameraSubject and cameraSubject:IsA('SkateboardPlatform')
+
+ if isInVehicle and cameraSubject.Occupant == humanoid then
+ cameraSubject = humanoid
+ camera.CameraSubject = humanoid
+ isInVehicle = false
+ end
+
+ if lastUpdate == nil or (now - lastUpdate) > 1 then
+ self.LastCameraTransform = nil
+ end
+
+ if lastUpdate then
+ local gamepadRotation = self:UpdateGamepad()
+
+ -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
+ local delta = math_min(0.1, now - lastUpdate)
+
+ if gamepadRotation ~= ZERO_VECTOR2 then
+ userPanningTheCamera = true
+ self.RotateInput = self.RotateInput + (gamepadRotation * delta)
+ end
+
+ local angle = 0
+ if not (isInVehicle or isOnASkateboard) then
+ angle = angle + (self.TurningLeft and -120 or 0)
+ angle = angle + (self.TurningRight and 120 or 0)
+ end
+
+ if angle ~= 0 then
+ self.RotateInput = self.RotateInput + Vector2.new(math_rad(angle * delta), 0)
+ userPanningTheCamera = true
+ end
+ end
+
+ -- Reset tween speed if user is panning
+ if userPanningTheCamera then
+ tweenSpeed = 0
+ self.LastUserPanCamera = now
+ end
+
+ local userRecentlyPannedCamera = now - self.LastUserPanCamera < timeBeforeAutoRotate
+ local subjectPosition = self:GetSubjectPosition()
+
+ if subjectPosition and camera then
+ local zoom = self:GetCameraZoom()
+ if zoom < 0.25 then
+ zoom = 0.25
+ end
+
+ if TeleportService:GetTeleportSetting("FollowCamera") then
+ if self.LastCameraTransform and not self:IsInFirstPerson() then
+ local lastVec = -(self.LastCameraTransform.p - subjectPosition)
+ local y = findAngleBetweenXZVectors(lastVec, self:GetCameraLook())
+ -- Check for NaNs
+ if IsFinite(y) and math.abs(y) > 0.0001 then
+ self.RotateInput = self.RotateInput + Vector2.new(y, 0)
+ end
+ end
+ end
+
+ camera.Focus = CFrame_new(subjectPosition)
+
+ local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput)
+ self.RotateInput = ZERO_VECTOR2
+
+ if self.LastZoom ~= zoom then
+ self.LastZoom = zoom
+
+ if camera.CameraSubject and camera.CameraSubject:IsA("Humanoid") then
+ -- Flatten the lookVector
+ newLookVector = (newLookVector * XZ_VECTOR).Unit
+
+ -- Apply upwards tilt
+ local upY = -math_min(6, zoom/40)
+ newLookVector = (newLookVector + (UP_VECTOR * upY)).Unit
+ end
+ end
+
+ local newCF = CFrame_new(subjectPosition - (zoom * newLookVector), subjectPosition)
+ camera.CFrame = camera.CFrame:Lerp(newCF,.8)
+ self.LastCameraTransform = camera.CFrame
+ end
+
+ lastUpdate = now
+ end
+
+ GameSettings:SetCameraYInvertVisible()
+ GameSettings:SetGamepadCameraSensitivityVisible()
+end
+
+return this
+
+
+ -
+
+ Opacity
+ -- SolarCrane
+
+local module = {}
+
+local LastUpdate = tick()
+local TransparencyDirty = false
+local Enabled = false
+local LastTransparency = nil
+
+local DescendantAddedConn, DescendantRemovingConn = nil, nil
+local ToolDescendantAddedConns = {}
+local ToolDescendantRemovingConns = {}
+local CachedParts = {}
+
+local function HasToolAncestor(object)
+ return (object:FindFirstAncestorWhichIsA("Tool")) ~= nil
+end
+
+local function IsValidPartToModify(part)
+ if part:IsA('BasePart') or part:IsA('Decal') then
+ return not HasToolAncestor(part)
+ end
+ return false
+end
+
+local function TeardownTransparency()
+ for child, _ in pairs(CachedParts) do
+ child.LocalTransparencyModifier = 0
+ end
+
+ CachedParts = {}
+ TransparencyDirty = true
+ LastTransparency = nil
+
+ if DescendantAddedConn then
+ DescendantAddedConn:disconnect()
+ DescendantAddedConn = nil
+ end
+
+ if DescendantRemovingConn then
+ DescendantRemovingConn:disconnect()
+ DescendantRemovingConn = nil
+ end
+
+ for object, conn in pairs(ToolDescendantAddedConns) do
+ conn:disconnect()
+ ToolDescendantAddedConns[object] = nil
+ end
+
+ for object, conn in pairs(ToolDescendantRemovingConns) do
+ conn:disconnect()
+ ToolDescendantRemovingConns[object] = nil
+ end
+end
+
+local function SetupTransparency(character)
+ TeardownTransparency()
+
+ if DescendantAddedConn then
+ DescendantAddedConn:Disconnect()
+ end
+
+ DescendantAddedConn = character.DescendantAdded:connect(function (object)
+ -- This is a part we want to invisify
+ if IsValidPartToModify(object) then
+ CachedParts[object] = true
+ TransparencyDirty = true
+ -- There is now a tool under the character
+ elseif object:IsA('Tool') then
+ if ToolDescendantAddedConns[object] then
+ ToolDescendantAddedConns[object]:Disconnect()
+ end
+
+ ToolDescendantAddedConns[object] = object.DescendantAdded:connect(function (toolChild)
+ CachedParts[toolChild] = nil
+ if toolChild:IsA('BasePart') or toolChild:IsA('Decal') then
+ -- Reset the transparency
+ toolChild.LocalTransparencyModifier = 0
+ end
+ end)
+
+ if ToolDescendantRemovingConns[object] then
+ ToolDescendantRemovingConns[object]:Disconnect()
+ end
+
+ ToolDescendantRemovingConns[object] = object.DescendantRemoving:connect(function (formerToolChild)
+ wait() -- wait for new parent
+ if character and formerToolChild and formerToolChild:IsDescendantOf(character) then
+ if IsValidPartToModify(formerToolChild) then
+ CachedParts[formerToolChild] = true
+ TransparencyDirty = true
+ end
+ end
+ end)
+ end
+ end)
+
+ if DescendantRemovingConn then
+ DescendantRemovingConn:Disconnect()
+ end
+
+ DescendantRemovingConn = character.DescendantRemoving:connect(function (object)
+ if CachedParts[object] then
+ CachedParts[object] = nil
+ -- Reset the transparency
+ object.LocalTransparencyModifier = 0
+ end
+ end)
+
+ for _,desc in pairs(character:GetDescendants()) do
+ if IsValidPartToModify(desc) then
+ CachedParts[desc] = true
+ TransparencyDirty = true
+ end
+ end
+end
+
+
+function module:SetEnabled(newState)
+ if Enabled ~= newState then
+ Enabled = newState
+ self:Update()
+ end
+end
+
+function module:SetSubject(subject)
+ local character = nil
+ if subject and subject:IsA("Humanoid") then
+ character = subject.Parent
+ end
+ if subject and subject:IsA("VehicleSeat") and subject.Occupant then
+ character = subject.Occupant.Parent
+ end
+ if character then
+ SetupTransparency(character)
+ else
+ TeardownTransparency()
+ end
+end
+
+function module:Update()
+ local instant = false
+ local now = tick()
+ local currentCamera = workspace.CurrentCamera
+
+ if currentCamera then
+ local transparency = 0
+ if not Enabled then
+ instant = true
+ else
+ local distance = (currentCamera.Focus.p - currentCamera.CFrame.p).magnitude
+ if distance < 2 then
+ transparency = 1
+ elseif distance < 6 then
+ transparency = 0.5
+ else
+ transparency = 0
+ end
+ end
+
+ if TransparencyDirty or LastTransparency ~= transparency then
+ for child in pairs(CachedParts) do
+ if child.ClassName == "Decal" then
+ child.LocalTransparencyModifier = math.floor(transparency)
+ else
+ child.LocalTransparencyModifier = transparency
+ end
+ end
+ TransparencyDirty = false
+ LastTransparency = transparency
+ end
+ end
+ LastUpdate = now
+end
+
+return module
+
+
+ -
+
+ Popper
+ -- PopperCam Version 16
+-- OnlyTwentyCharacters
+
+local PopperCam = {} -- Guarantees your players won't see outside the bounds of your map!
+
+-----------------
+--| Constants |--
+-----------------
+
+local POP_RESTORE_RATE = 0.3
+local MIN_CAMERA_ZOOM = 0.5
+
+local VALID_SUBJECTS = {
+ 'Humanoid',
+ 'VehicleSeat',
+ 'SkateboardPlatform',
+}
+
+-----------------
+--| Variables |--
+-----------------
+
+local Players = game:GetService('Players')
+
+local Camera = nil
+local CameraSubjectChangeConn = nil
+
+local SubjectPart = nil
+
+local PlayerCharacters = {} -- For ignoring in raycasts
+local VehicleParts = {} -- Also just for ignoring
+
+local LastPopAmount = 0
+local LastZoomLevel = 0
+local PopperEnabled = true
+
+local CFrame_new = CFrame.new
+
+-----------------------
+--| Local Functions |--
+-----------------------
+
+local math_abs = math.abs
+
+local function OnCharacterAdded(player, character)
+ PlayerCharacters[player] = character
+end
+
+local function OnPlayersChildAdded(child)
+ if child:IsA('Player') then
+ child.CharacterAdded:connect(function(character)
+ OnCharacterAdded(child, character)
+ end)
+ if child.Character then
+ OnCharacterAdded(child, child.Character)
+ end
+ end
+end
+
+local function OnPlayersChildRemoved(child)
+ if child:IsA('Player') then
+ PlayerCharacters[child] = nil
+ end
+end
+
+-------------------------
+--| Exposed Functions |--
+-------------------------
+
+function PopperCam:Update()
+ if PopperEnabled then
+ -- First, prep some intermediate vars
+ local Camera = workspace.CurrentCamera
+ local cameraCFrame = Camera.CFrame
+ local focusPoint = Camera.Focus.p
+
+ if SubjectPart then
+ focusPoint = SubjectPart.CFrame.p
+ end
+
+ local ignoreList = {}
+ for _, character in pairs(PlayerCharacters) do
+ ignoreList[#ignoreList + 1] = character
+ end
+ for i = 1, #VehicleParts do
+ ignoreList[#ignoreList + 1] = VehicleParts[i]
+ end
+
+ -- Get largest cutoff distance
+ local largest = Camera:GetLargestCutoffDistance(ignoreList)
+
+ -- Then check if the player zoomed since the last frame,
+ -- and if so, reset our pop history so we stop tweening
+ local zoomLevel = (cameraCFrame.p - focusPoint).Magnitude
+ if math_abs(zoomLevel - LastZoomLevel) > 0.001 then
+ LastPopAmount = 0
+ end
+
+ -- Finally, zoom the camera in (pop) by that most-cut-off amount, or the last pop amount if that's more
+ local popAmount = largest
+ if LastPopAmount > popAmount then
+ popAmount = LastPopAmount
+ end
+
+ if popAmount > 0 then
+ Camera.CFrame = cameraCFrame + (cameraCFrame.lookVector * popAmount)
+ LastPopAmount = popAmount - POP_RESTORE_RATE -- Shrink it for the next frame
+ if LastPopAmount < 0 then
+ LastPopAmount = 0
+ end
+ end
+
+ LastZoomLevel = zoomLevel
+ end
+end
+
+--------------------
+--| Script Logic |--
+--------------------
+
+
+-- Connect to all Players so we can ignore their Characters
+Players.ChildRemoved:connect(OnPlayersChildRemoved)
+Players.ChildAdded:connect(OnPlayersChildAdded)
+for _, player in pairs(Players:GetPlayers()) do
+ OnPlayersChildAdded(player)
+end
+
+return PopperCam
+
+
+
+ -
+
+ Camera
+ local RunService = game:GetService("RunService")
+local UserInputService = game:GetService("UserInputService")
+local Players = game:GetService("Players")
+
+local playerScripts = script.Parent
+local playerModule = require(playerScripts:WaitForChild("PlayerModule"))
+
+local cameraSystem = playerModule:GetCameras()
+
+local main = require(script:WaitForChild("Main"))
+local popper = require(script:WaitForChild("Popper"))
+local opacity = require(script:WaitForChild("Opacity"))
+
+local cameraSubjectChangedConn = nil
+local renderSteppedConn = nil
+
+local function onCameraSubjectChanged()
+ local currentCamera = workspace.CurrentCamera
+ if currentCamera then
+ local newSubject = currentCamera.CameraSubject
+ opacity:SetSubject(newSubject)
+ end
+end
+
+local function onNewCamera()
+ local currentCamera = workspace.CurrentCamera
+ if currentCamera then
+ if cameraSubjectChangedConn then
+ cameraSubjectChangedConn:Disconnect()
+ end
+
+ local cameraSubjectChanged = currentCamera:GetPropertyChangedSignal("CameraSubject")
+ cameraSubjectChangedConn = cameraSubjectChanged:Connect(onCameraSubjectChanged)
+
+ onCameraSubjectChanged()
+ end
+end
+
+-- Initialize cameras.
+local cameraUpdated = workspace:GetPropertyChangedSignal("CurrentCamera")
+cameraUpdated:Connect(onNewCamera)
+
+onNewCamera()
+main:SetEnabled(true)
+opacity:SetEnabled(true)
+
+-- Overload the camera update function.
+function cameraSystem:Update()
+ if cameraSystem.activeCameraController then
+ cameraSystem.activeCameraController:Enable(false)
+ cameraSystem.activeCameraController = nil
+ end
+
+ main:Update()
+ popper:Update()
+ opacity:Update()
+end
+
+playerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Default)
+playerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Default)
+
+
+ -
+
+ CharacterBevels
+
+
+
+ -
+
+ ClickToMove
+ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- @CloneTrooper1019, 2018
+-- ClickToMove
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Constants
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local ContextActionService = game:GetService("ContextActionService")
+local GuiService = game:GetService("GuiService")
+local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+local TeleportService = game:GetService("TeleportService")
+local UserInputService = game:GetService("UserInputService")
+
+local IS_TOUCH = UserInputService.TouchEnabled
+
+local ICON_IDLE = "rbxassetid://334630296"
+local ICON_HOVER = "rbxassetid://1000000"
+local ICON_CLICK = "rbxasset://textures/DragCursor.png"
+
+local DISK_OFFSET = CFrame.Angles(math.pi / 2,0,0)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Character Listener
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local player = game.Players.LocalPlayer
+local character,humanoid
+
+local function onCharacterAdded(char)
+ humanoid = char:WaitForChild("Humanoid")
+ character = char
+end
+
+if player.Character then
+ onCharacterAdded(player.Character)
+end
+
+player.CharacterAdded:Connect(onCharacterAdded)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Gui Focus
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local isMouseHoveringUi = false
+
+local function onInputChanged(input, gameProcessed)
+ local inputType = input.UserInputType.Name
+
+ if inputType == "MouseMovement" then
+ isMouseHoveringUi = gameProcessed
+ end
+end
+
+local function isGuiFocused()
+ return isMouseHoveringUi or GuiService.SelectedObject ~= nil
+end
+
+UserInputService.InputChanged:Connect(onInputChanged)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Movement Goal
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local currentGoal, moveSignal
+
+local function findAngleBetweenXZVectors(vec2, vec1)
+ return math.atan2(vec1.X*vec2.Z-vec1.Z*vec2.X, vec1.X*vec2.X + vec1.Z*vec2.Z)
+end
+
+local function isFinite(num)
+ return num == num and num ~= 1/0 and num ~= -1/0
+end
+
+local function rotateCameraTowardsGoal()
+ local c = workspace.CurrentCamera
+ if c then
+ local cf = c.CFrame
+ local focus = c.Focus
+
+ local desiredAngle = CFrame.new(cf.p,currentGoal).lookVector
+ local currentAngle = cf.lookVector
+
+ local angleBetween = findAngleBetweenXZVectors(desiredAngle,currentAngle)
+
+ if isFinite(angleBetween) then
+ local abs = math.abs(angleBetween)
+ local sign = math.sign(angleBetween)
+ local rotation = math.min(0.01,abs)
+
+ local cfLocal = focus:toObjectSpace(cf)
+ c.CFrame = focus * CFrame.Angles(0,-rotation*sign,0) * cfLocal
+ end
+ end
+end
+
+local function finishGoal()
+ if currentGoal then
+ currentGoal = nil
+ end
+ if moveSignal then
+ moveSignal:Disconnect()
+ moveSignal = nil
+ end
+end
+
+local function clickToMove(goal)
+ finishGoal()
+ currentGoal = goal
+
+ moveSignal = humanoid.MoveToFinished:Connect(finishGoal)
+
+ humanoid:Move(Vector3.new(1,1,1))
+ humanoid:MoveTo(currentGoal)
+end
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Green Disk
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local mouse = player:GetMouse()
+local mouseIcon = script.Parent
+mouse.TargetFilter = workspace.CurrentCamera
+
+local lastTarget
+local lastTargetCanClick = false
+
+local disk = Instance.new("CylinderHandleAdornment")
+disk.Name = "Disk"
+disk.Color3 = Color3.new(0,1,0)
+disk.Radius = 1
+disk.Height = 0.2
+disk.Visible = false
+disk.Adornee = workspace.Terrain
+disk.Parent = script
+
+local goalDisk = disk:Clone()
+goalDisk.Name = "Goal"
+goalDisk.Parent = script
+
+local function hasTool()
+ if character then
+ return character:FindFirstChildOfClass("Tool") ~= nil
+ end
+
+ return false
+end
+
+local function isFirstPerson()
+ if character then
+ local head = character:FindFirstChild("Head")
+ if head then
+ return head.LocalTransparencyModifier == 1
+ end
+ end
+ return false
+end
+
+local function canClickTarget()
+ local target = mouse.Target
+ if target then
+ if target ~= lastTarget then
+ local canClick = false
+ local clickDetector = target:FindFirstChildOfClass("ClickDetector")
+
+ if clickDetector then
+ local dist = player:DistanceFromCharacter(target.Position)
+ if dist <= clickDetector.MaxActivationDistance then
+ canClick = true
+ end
+ end
+
+ lastTarget = target
+ lastTargetCanClick = canClick
+ end
+
+ return lastTargetCanClick
+ end
+end
+
+local function canRenderDisk(rendering)
+ if not TeleportService:GetTeleportSetting("ClickToMove") then
+ return false
+ end
+
+ if rendering and IS_TOUCH then
+ return false
+ end
+
+ if humanoid then
+ local movement = humanoid.MoveDirection
+ if movement.Magnitude == 0 then
+ local pos = mouse.Hit.p
+ local dist = player:DistanceFromCharacter(pos)
+
+ if dist < 32 then
+ local blockers = {hasTool, isFirstPerson, canClickTarget, isGuiFocused}
+
+ for _,blocker in pairs(blockers) do
+ if blocker() then
+ return false
+ end
+ end
+
+ return true
+ end
+ else
+ finishGoal()
+ end
+ end
+
+ return false
+end
+
+local function render3dAdorn()
+ disk.Visible = canRenderDisk(true)
+
+ if disk.Visible then
+ disk.CFrame = CFrame.new(mouse.Hit.p) * DISK_OFFSET
+ mouseIcon.Image = ICON_HOVER
+ elseif canClickTarget() then
+ mouseIcon.Image = ICON_CLICK
+ elseif not hasTool() then
+ mouseIcon.Image = ICON_IDLE
+ end
+
+ if currentGoal then
+ goalDisk.Visible = true
+ goalDisk.CFrame = CFrame.new(currentGoal) * DISK_OFFSET
+ rotateCameraTowardsGoal()
+ else
+ goalDisk.Visible = false
+ end
+end
+
+RunService.Heartbeat:Connect(render3dAdorn)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Click Action
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function onInputBegan(input,gameProcessed)
+ local goal = mouse.Hit.p
+ if not gameProcessed and canRenderDisk() and humanoid then
+ local name = input.UserInputType.Name
+
+ if name == "MouseButton1" then
+ clickToMove(goal)
+ elseif name == "Touch" then
+ wait(.1)
+ if input.UserInputState == Enum.UserInputState.End then
+ clickToMove(goal)
+ end
+ elseif name == "Gamepad1" then
+ if input.KeyCode == Enum.KeyCode.ButtonR2 then
+ clickToMove(goal)
+ end
+ end
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- OBLITERATE the invasive click to move mode that Roblox provides
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+pcall(function ()
+ if IS_TOUCH then
+ local playerScripts = player:WaitForChild("PlayerScripts")
+ local no = function () end
+
+ spawn(function ()
+ local controlScript = playerScripts:WaitForChild("ControlScript", 86400)
+ if controlScript then
+ local masterControl = controlScript:WaitForChild("MasterControl")
+
+ local clickToMove = masterControl:WaitForChild("ClickToMoveController")
+ clickToMove = require(clickToMove)
+
+ clickToMove:Disable()
+ clickToMove.Enable = no
+ end
+ end)
+
+ spawn(function ()
+ local playerModule = playerScripts:WaitForChild("PlayerModule", 86400)
+ if playerModule then
+ local controlModule = playerModule:WaitForChild("ControlModule")
+
+ local clickToMove = controlModule:WaitForChild("ClickToMoveController")
+ clickToMove = require(clickToMove)
+
+ clickToMove:Stop()
+ clickToMove.Enable = no
+ end
+ end)
+ end
+end)
+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+ -
+
+ ConsoleTweaks
+ local UserInputService = game:GetService("UserInputService")
+local GuiService = game:GetService("GuiService")
+
+local function addUIScale(obj,scale)
+ local uiScale = Instance.new("UIScale")
+ uiScale.Scale = scale
+ uiScale.Parent = obj
+end
+
+if GuiService:IsTenFootInterface() then
+ local gui = script.Parent
+ local zoomControls = gui:WaitForChild("ZoomControls")
+ zoomControls.Visible = false
+
+ local backpack = gui:WaitForChild("Backpack")
+ backpack.Position = UDim2.new(0, 0, 1, 0)
+
+ local chat = gui:WaitForChild("Chat")
+ addUIScale(chat, 1.5)
+
+ local chatPadding = gui:WaitForChild("ChatPadding", 1)
+ if chatPadding then
+ chatPadding:Destroy()
+ end
+
+ local safeChat = gui:WaitForChild("SafeChat")
+ addUIScale(safeChat, 1.5)
+
+ local health = gui:WaitForChild("Health")
+ addUIScale(health, 1.5)
+end
+
+wait()
+script:Destroy()
+
+
+ -
+
+ Explosions
+ local TeleportService = game:GetService("TeleportService")
+
+local classicExp = script:WaitForChild("ClassicExp")
+local c = workspace.CurrentCamera
+
+local baseExpAdorn = Instance.new("UnionOperation")
+baseExpAdorn.Name = "ExplosionAdorn"
+baseExpAdorn.Anchored = true
+baseExpAdorn.CanCollide = false
+baseExpAdorn.Locked = true
+baseExpAdorn.Transparency = 1
+baseExpAdorn.Size = Vector3.new()
+
+local function onDescendantAdded(exp)
+ if exp:IsA("Explosion") then
+ local cf = CFrame.new(exp.Position)
+ local expAdorn = baseExpAdorn:Clone()
+ local lifeTime = 1.5
+ exp.Visible = false
+ if TeleportService:GetTeleportSetting("RetroExplosions") then
+ local expObj = Instance.new("SphereHandleAdornment")
+ expObj.Adornee = expAdorn
+ expObj.Radius = exp.BlastRadius
+ expObj.Color3 = Color3.new(1,0,0)
+ expObj.CFrame = cf
+ expObj.Parent = expAdorn
+ lifeTime = 1
+ if exp.BlastRadius > 1 then
+ lifeTime = lifeTime - (1/exp.BlastRadius)
+ end
+ else
+ spawn(function ()
+ local e = classicExp:Clone()
+ e.Parent = expAdorn
+ expAdorn.CFrame = cf
+ local lessParticles = TeleportService:GetTeleportSetting("ReducedParticles")
+ local count = lessParticles and 25 or 100
+ for i = 1,8 do
+ e:Emit(count)
+ wait(0.125)
+ end
+ end)
+ end
+ expAdorn.Parent = c
+ wait(lifeTime)
+ expAdorn:Destroy()
+ end
+end
+
+workspace.DescendantAdded:Connect(onDescendantAdded)
+
+
+ -
+
+ ForceFields
+ local RunService = game:GetService("RunService")
+
+local ffAdorns = workspace:WaitForChild("_ForceFieldAdorns")
+local registry = {}
+local cycleStates = {}
+local cycles = 60
+
+local function onChildAdded(child)
+ if child:IsA("SelectionBox") then
+ spawn(function ()
+ while not child.Adornee do
+ child.Changed:Wait()
+ end
+ registry[child] = child.Adornee
+ end)
+ end
+end
+
+local function onChildRemoved(child)
+ if registry[child] then
+ registry[child] = nil
+ end
+end
+
+local function update()
+ local now = tick()
+ for adorn,adornee in pairs(registry) do
+ local model = adornee:FindFirstAncestorWhichIsA("Model")
+ local key
+ if model then
+ local key = model:GetFullName()
+ local startTime = cycleStates[key]
+ if not startTime then
+ startTime = tick()
+ cycleStates[key] = startTime
+ end
+ local cycle = math.floor(((now-startTime)*2) * cycles) % (cycles*2)
+ if cycle > cycles then
+ cycle = cycles - (cycle - cycles)
+ end
+ local invertCycle = cycles - cycle
+ adorn.Color3 = Color3.new(cycle/cycles, 0, invertCycle/cycles)
+ adorn.Transparency = 0
+ end
+ adorn.Visible = adornee:IsDescendantOf(workspace) and adornee.LocalTransparencyModifier < 1
+ end
+end
+
+for _,v in pairs(ffAdorns:GetChildren()) do
+ onChildAdded(v)
+end
+
+RunService.Heartbeat:Connect(update)
+ffAdorns.ChildAdded:Connect(onChildAdded)
+ffAdorns.ChildRemoved:Connect(onChildRemoved)
+
+
+ -
+
+ GamepadPatch
+ -- Seriously Roblox?
+
+local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+local UserInputService = game:GetService("UserInputService")
+
+local player = Players.LocalPlayer
+local playerScripts = player:WaitForChild("PlayerScripts")
+
+local playerModule = playerScripts:WaitForChild("PlayerModule")
+local controlModule = playerModule:WaitForChild("ControlModule")
+local gamepad = require(controlModule:WaitForChild("Gamepad"))
+
+playerModule = require(playerModule)
+controlModule = playerModule:GetControls()
+
+local function fixGamepad()
+ local lastInputType = UserInputService:GetLastInputType()
+
+ if lastInputType.Name == "Gamepad1" then
+ local controllers = controlModule.controllers
+
+ if controlModule.activeController ~= controllers[gamepad] then
+ controlModule:SwitchToController(gamepad)
+ end
+ end
+end
+
+RunService:BindToRenderStep("GamepadPatch", 0, fixGamepad)
+
+
+ -
+
+ HumanoidLabels
+ local humanoids = {}
+local player = game.Players.LocalPlayer
+local pgui = player:WaitForChild("PlayerGui")
+local healthBase = script:WaitForChild("Health")
+local rs = game:GetService("RunService")
+
+local farStudsOffset = Vector3.new(0,2,0)
+local closeStudsOffset = Vector3.new(0,1,0)
+
+local farSize = UDim2.new(0,50,0,20)
+local closeSize = UDim2.new(0,100,0,30)
+
+local function isFinite(num)
+ return num == num and num ~= -1/0 and num ~= 1/0
+end
+
+local function setupHumanoid(h)
+ local updateCon = nil
+ local currentHealth = nil
+
+ local function onAncestryChanged()
+ if updateCon then
+ updateCon:disconnect()
+ updateCon = nil
+ end
+
+ if currentHealth then
+ currentHealth:Destroy()
+ currentHealth = nil
+ end
+
+ local char = h.Parent
+
+ if char then
+ while not char:FindFirstChild("Head") do
+ if h.Parent ~= char then break end
+ char.ChildAdded:wait()
+ end
+
+ local head = char:FindFirstChild("Head")
+
+ if head then
+ local health = healthBase:Clone()
+ local playerName = health:WaitForChild("PlayerName")
+ local redBar = health:WaitForChild("RedBar")
+ local greenBar = redBar:WaitForChild("GreenBar")
+ local inOverWrite = false
+ local overWriter = nil
+
+ local hPlayer = game.Players:GetPlayerFromCharacter(char)
+ playerName.Text = char.Name
+ health.Adornee = head
+ health.PlayerToHideFrom = hPlayer
+ health.Parent = head
+
+ local c = workspace.CurrentCamera
+
+ local function update()
+ local dist = (c.CFrame.p - head.Position).magnitude
+ local fontSize = 12
+
+ if dist < 20 then
+ fontSize = 24
+ elseif dist < 50 then
+ fontSize = 18
+ end
+
+ local ratio = h.Health / h.MaxHealth
+ redBar.Visible = isFinite(ratio)
+ redBar.BackgroundTransparency = math.floor(ratio)
+ redBar.Size = UDim2.new(0, fontSize * 4, 0, fontSize / 2)
+ greenBar.Size = UDim2.new(ratio, 0, 1, 0)
+
+ local width = fontSize * 4
+ health.Size = UDim2.new(0, width, 0, fontSize)
+ health.Enabled = (dist <= 100 and head.Transparency < 1)
+ health.StudsOffsetWorldSpace = Vector3.new(0, 1.5, 0)
+
+ if hPlayer and game:FindService("Teams") then
+ playerName.TextColor = hPlayer.TeamColor
+ else
+ playerName.TextColor3 = Color3.new(1, 1, 1)
+ end
+
+ local overWriter = char:FindFirstChild("NameOverwrite")
+ if overWriter and overWriter:IsA("StringValue") then
+ playerName.Text = overWriter.Value
+ else
+ playerName.Text = char.Name
+ end
+ end
+
+ updateCon = rs.RenderStepped:Connect(update)
+ currentHealth = health
+ h.DisplayDistanceType = "None"
+ end
+ end
+ end
+ onAncestryChanged()
+ h.AncestryChanged:Connect(onAncestryChanged)
+end
+
+local function recurse(obj)
+ for _,v in pairs(obj:GetChildren()) do
+ if v:IsA("Humanoid") then
+ humanoids[v] = true
+ else
+ recurse(v)
+ end
+ end
+end
+
+recurse(workspace)
+
+for h in pairs(humanoids) do
+ humanoids[h] = true
+
+ spawn(function ()
+ setupHumanoid(h)
+ end)
+end
+
+local function onDescendantAdded(child)
+ if child:IsA("Humanoid") then
+ humanoids[child] = true
+ setupHumanoid(child)
+ end
+end
+
+local function onDescendantRemoved(child)
+ if humanoids[child] then
+ humanoids[child] = nil
+ end
+end
+
+recurse(workspace)
+
+workspace.DescendantAdded:connect(onDescendantAdded)
+workspace.DescendantRemoving:connect(onDescendantRemoved)
+
+
-
+
+ Health
+ false
+
[null]
+ true
+ true
+
+ true
+ false
+ 0
+ 0
+ -1
+ true
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+ 0
+ INF
+ [null]
+ true
+ [null]
+
+ 0
+ 96
+ 0
+ 24
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 1.5
+ 0
+
+
+ 0
+
+ -
+
+ PlayerName
+ false
+
+ 0.5
+ 0.75
+
+ true
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 10
+ 0
+ 1
+ 0
+
+ 0
+
+ CloneTrooper1019
+
+ 1
+ 1
+ 1
+
+ true
+ 24
+
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+ true
+ 2
+ 1
+ true
+ 1
+
+
+ -
+
+ RedBar
+ false
+
+ 0.5
+ 0.5
+
+ true
+
+ true
+
+ 1
+ 0
+ 0
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.6000000238418579
+ 0
+ 0.30000001192092896
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ GreenBar
+ false
+
+ 0
+ 0
+
+ true
+
+ true
+
+ 0.5058823823928833
+ 0.7725490927696228
+ 0.08627451211214066
+
+ 0
+
+ 0.10588239878416061
+ 0.16470590233802795
+ 0.20784319937229156
+
+ 0
+ 0
+ false
+ false
+ 0
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+
+
+
+
+ -
+
+ InputGateway
+ local UserInputService = game:GetService("UserInputService")
+local ContextActionService = game:GetService("ContextActionService")
+local Debris = game:GetService("Debris")
+
+local gateway = script.Parent
+local tool = gateway.Parent
+local remote = gateway:WaitForChild("Gateway")
+local player = game.Players.LocalPlayer
+local mouse = player:GetMouse()
+local isActive = false
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Standard Input
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function activate(active,cf)
+ isActive = active
+ remote:FireServer("SetActive",active,cf)
+ while isActive do
+ wait(.1)
+ remote:FireServer("SetTarget",mouse.Hit)
+ end
+end
+
+local function onKey(input)
+ local keyCode = input.KeyCode.Name
+ local down = (input.UserInputState.Name == "Begin")
+ remote:FireServer("KeyEvent",keyCode,down)
+end
+
+local function onInputBegan(input,gameProcessed)
+ if not gameProcessed then
+ local name = input.UserInputType.Name
+ if name == "MouseButton1" then
+ activate(true,mouse.Hit)
+ elseif name == "Touch" then
+ wait(.1)
+ local state = input.UserInputState.Name
+ if state == "End" or state == "Cancel" then
+ activate(true,mouse.Hit)
+ end
+ elseif name == "Gamepad1" then
+ local keyCode = input.KeyCode.Name
+ if keyCode == "ButtonR2" then
+ activate(true,mouse.Hit)
+ end
+ elseif name == "Keyboard" then
+ onKey(input)
+ end
+ end
+end
+
+local function onInputEnded(input,gameProcessed)
+ if not gameProcessed and isActive then
+ local name = input.UserInputType.Name
+ if name == "MouseButton1" or name == "Touch" or name == "Gamepad1" then
+ activate(false,mouse.Hit)
+ elseif name == "Keyboard" then
+ onKey(input)
+ end
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+UserInputService.InputEnded:Connect(onInputEnded)
+
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Special case Input
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local mControlScheme = tool:WaitForChild("ControlScheme",5)
+
+if mControlScheme then
+ local controlSchemeData = require(mControlScheme)
+ local controlScheme = controlSchemeData.Buttons
+ local activateContext = controlSchemeData.ActivateContext
+ local keyEvent = tool:WaitForChild("KeyEvent")
+ local callbacks = {}
+
+ local hands = { L = "Left", R = "Right" }
+ local handTypes = {"Bumper","Trigger","Joystick (Press)"}
+
+ local schemeDocs =
+ {
+ Keyboard = {"Hold Left Mouse Button - " .. activateContext};
+ Gamepad = {"Hold Right Trigger - " .. activateContext};
+ }
+
+ for key,data in pairs(controlScheme) do
+ local down = false
+ callbacks[key] = function (actionName,inputState,inputObject)
+ if (inputState.Name == "Begin") and not down then
+ down = true
+ if data.Client then
+ keyEvent:Fire(key,true)
+ else
+ remote:FireServer("KeyEvent",key,true)
+ end
+ elseif (inputState.Name == "End") and down then
+ down = false
+ if data.Client then
+ keyEvent:Fire(key,false)
+ else
+ remote:FireServer("KeyEvent",key,false)
+ end
+ end
+ end
+
+ local xBtn = data.XboxButton:gsub("Button","")
+ if #xBtn == 2 then
+ local handId,hTypeId = xBtn:match("(%u)(%d)")
+ local hand = hands[handId]
+ local hType = handTypes[tonumber(hTypeId)]
+ xBtn = hand .. " " .. hType
+ else
+ xBtn = "(" .. xBtn .. ")"
+ end
+ table.insert(schemeDocs.Keyboard,key .. " - " .. data.Label)
+ table.insert(schemeDocs.Gamepad,xBtn .. " - " .. data.Label)
+ end
+
+ local currentSchemeDocMsg
+
+ local function onLastInputTypeChanged(inputType)
+ if currentSchemeDocMsg and not UserInputService.TouchEnabled and not controlSchemeData.HideControls then
+ local schemeDoc
+ if inputType.Name:find("Gamepad") then
+ schemeDoc = "Gamepad"
+ else
+ schemeDoc = "Keyboard"
+ end
+ currentSchemeDocMsg.Text = schemeDoc .. " Controls:\n\n" .. table.concat(schemeDocs[schemeDoc],"\n")
+ end
+ end
+
+ local diedCon
+ local equipped = false
+
+ local function onUnequipped()
+ if equipped then
+ equipped = false
+ for key,data in pairs(controlScheme) do
+ ContextActionService:UnbindAction(data.Label)
+ end
+ currentSchemeDocMsg:Destroy()
+ currentSchemeDocMsg = nil
+ end
+ end
+
+ local function onEquipped()
+ if not equipped then
+ equipped = true
+ for key,data in pairs(controlScheme) do
+ ContextActionService:BindAction(data.Label,callbacks[key],true,Enum.KeyCode[data.XboxButton])
+ ContextActionService:SetTitle(data.Label,data.Label)
+ end
+ if UserInputService.TouchEnabled then
+ spawn(function ()
+ local playerGui = player:WaitForChild("PlayerGui")
+ local contextActionGui = playerGui:WaitForChild("ContextActionGui")
+ local contextButtonFrame = contextActionGui:WaitForChild("ContextButtonFrame")
+ contextButtonFrame.Size = UDim2.new(3/8,0,3/8,0)
+ contextButtonFrame.AnchorPoint = Vector2.new(1,1)
+ contextButtonFrame.Position = UDim2.new(1,0,1,0)
+ end)
+ end
+ currentSchemeDocMsg = Instance.new("Message")
+ currentSchemeDocMsg.Parent = player
+ onLastInputTypeChanged(UserInputService:GetLastInputType())
+ if not diedCon then
+ local char = tool.Parent
+ if char then
+ local humanoid = char:FindFirstChildWhichIsA("Humanoid")
+ if humanoid then
+ diedCon = humanoid.Died:Connect(onUnequipped)
+ end
+ end
+ end
+ end
+ end
+
+ tool.Equipped:Connect(onEquipped)
+ tool.Unequipped:Connect(onUnequipped)
+ UserInputService.LastInputTypeChanged:Connect(onLastInputTypeChanged)
+end
+
+
+ -
+
+ Music
+ local TeleportService = game:GetService("TeleportService")
+local gameMusic = workspace:WaitForChild("GameMusic",10)
+if gameMusic and TeleportService:GetTeleportSetting("AllowMusic") then
+ gameMusic:Play()
+end
+
+
+ -
+
+ Shared
+ local TARGET = script.Name
+
+do
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
+ local client = ReplicatedStorage:WaitForChild("Client")
+ local targetScript = client:WaitForChild(TARGET)
+ local activation = require(targetScript)
+ activation(script)
+end
+
+
+ -
+
+ ToolSoundGlitch
+ local CollectionService = game:GetService("CollectionService")
+local TeleportService = game:GetService("TeleportService")
+
+local function onGlitchSoundAdded(glitchSound)
+ if TeleportService:GetTeleportSetting("SoundEquipBug") then
+ glitchSound.MaxDistance = 10000
+ glitchSound:Play()
+ end
+end
+
+local addSignal = CollectionService:GetInstanceAddedSignal("ToolSoundGlitch")
+addSignal:Connect(onGlitchSoundAdded)
+
+
+ -
+
+ ZoomControls
+ local UserInputService = game:GetService("UserInputService")
+local GuiService = game:GetService("GuiService")
+
+if GuiService:IsTenFootInterface() then
+ return
+end
+
+local IS_TOUCH = UserInputService.TouchEnabled
+
+local player = game.Players.LocalPlayer
+local playerScripts = player:WaitForChild("PlayerScripts")
+local passCameraEvent = playerScripts:WaitForChild("PassCameraEvent")
+
+local self = script.Parent
+local zoomIn = self:WaitForChild("ZoomIn")
+local zoomLock = zoomIn:WaitForChild("Lock")
+local firstPersonIndicator = self:WaitForChild("FirstPersonIndicator")
+
+local yellow = Color3.new(1,1,0)
+local cyan = Color3.new(0,1,1)
+local white = Color3.new(1,1,1)
+
+local c = workspace.CurrentCamera
+local currentlyDown
+
+local function updateCameraStatus()
+ local dist = (c.Focus.p - c.CFrame.p).magnitude
+ firstPersonIndicator.Visible = (dist <= 1.5)
+ zoomLock.Visible = (dist <= 1)
+end
+
+local function setupButton(btn)
+ local isDown = false
+ local inBounds = false
+ local lock = btn:FindFirstChild("Lock")
+ local mouse = player:GetMouse()
+ btn.MouseEnter:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ if (currentlyDown == nil or currentlyDown == btn) then
+ inBounds = true
+ if isDown then
+ btn.ImageColor3 = yellow
+ else
+ btn.ImageColor3 = cyan
+ end
+ end
+ end
+ end)
+ btn.MouseLeave:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ inBounds = false
+ if isDown then
+ btn.ImageColor3 = cyan
+ else
+ btn.ImageColor3 = white
+ end
+ end
+ end)
+ btn.MouseButton1Down:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ isDown = true
+ currentlyDown = btn
+ btn.ImageColor3 = yellow
+ end
+ end)
+ btn.MouseButton1Click:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ isDown = false
+ currentlyDown = nil
+ inBounds = false
+ passCameraEvent:Fire(btn.Name)
+ if IS_TOUCH then
+ btn.ImageColor3 = white
+ end
+ end
+ end)
+ mouse.Button1Up:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ if isDown then
+ isDown = false
+ currentlyDown = nil
+ if inBounds then
+ inBounds = false
+ passCameraEvent:Fire(btn.Name)
+ end
+ end
+ end
+ btn.ImageColor3 = white
+ end)
+ if lock then
+ lock.Changed:connect(function ()
+ if lock.Visible then
+ btn.ImageColor3 = white
+ isDown = false
+ if currentlyDown == btn then
+ currentlyDown = nil
+ end
+ end
+ end)
+ end
+ if IS_TOUCH then
+ btn.Modal = true
+ end
+end
+
+for _,v in pairs(script.Parent:GetChildren()) do
+ if v:IsA("ImageButton") then
+ setupButton(v)
+ end
+end
+
+c.Changed:connect(updateCameraStatus)
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/JetBoots.rbxmx b/Tools/Models/JetBoots.rbxmx
new file mode 100644
index 0000000..1596d01
--- /dev/null
+++ b/Tools/Models/JetBoots.rbxmx
@@ -0,0 +1,32 @@
+
+ -
+
+
+ true
+ true
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ false
+ JetBoots
+ false
+
+
+ rbxasset://Textures/Rocketboots.png
+
+
+ true
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/Multirocket.rbxmx b/Tools/Models/Multirocket.rbxmx
new file mode 100644
index 0000000..51a4867
--- /dev/null
+++ b/Tools/Models/Multirocket.rbxmx
@@ -0,0 +1,76 @@
+
+ -
+
+
+ true
+ true
+
+ 0.7
+ 0
+ -0.5
+ 0
+ 0
+ -1
+ -1
+ 0
+ 0
+ 0
+ 1
+ 0
+
+ false
+ Multirocket
+ false
+
+
+ rbxasset://Textures/Multirocket.png
+
+
+ true
+
+
-
+
+
+ 10
+ true
+ Swoosh
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/Rocket whoosh 01.wav
+
+
+ 0
+ 0.7
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 10
+ false
+ Explosion
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/collide.wav
+
+
+ 0
+ 1
+ 10000
+ 10
+ true
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/PaintballGun.rbxmx b/Tools/Models/PaintballGun.rbxmx
new file mode 100644
index 0000000..3bf9e28
--- /dev/null
+++ b/Tools/Models/PaintballGun.rbxmx
@@ -0,0 +1,163 @@
+
+ -
+
+
+ true
+ true
+
+ -0.4
+ -0.5
+ -0.07
+ 0
+ 0
+ -1
+ 0
+ 1
+ 0
+ 1
+ 0
+ 0
+
+ false
+ PaintballGun
+ true
+
+
+ rbxasset://Textures/PaintballIcon.png
+
+
+ true
+
+
-
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 488.7946
+ 3.313973
+ 438.8193
+ -0.3090168
+ 0.9510567
+ 0
+ 0
+ 0
+ -1
+ -0.9510566
+ -0.3090168
+ 0
+
+ true
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Handle
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 0
+ 1
+
+ 3
+ 2
+ 1
+
+ true
+
+
-
+
+
+ 10
+ false
+ Fire
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds//paintball.wav
+
+
+ 0
+ 1
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 2
+ 2
+
+ rbxassetid://998137795
+
+ 5
+ Mesh
+
+ 0
+ 0
+ 0
+
+
+ 1
+ 1
+ 1
+
+
+
+ rbxasset://textures/PaintballGunTex128.png
+
+
+ 1
+ 1
+ 1
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/Plane.rbxmx b/Tools/Models/Plane.rbxmx
new file mode 100644
index 0000000..4d9dbad
--- /dev/null
+++ b/Tools/Models/Plane.rbxmx
@@ -0,0 +1,171 @@
+
+ -
+
+
+ true
+ false
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ false
+ Plane
+ false
+
+
+
+
+
+ true
+
+
-
+
+
+ IconOverride
+
+ rbxassetid://1067894989
+ true
+
+
+ -
+
+ true
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ -561.6701
+ 6.200422
+ -528.76
+ 0
+ 1
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ -1
+
+ true
+ true
+ 0
+ 4279970357
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ true
+ false
+ 256
+ Rocket
+ 0.2
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 0
+ 1
+
+ 1
+ 1
+ 2
+
+ true
+
+
-
+
+
+ Owner
+
+
[null]
+ true
+
+
+ -
+
+
+ 10
+ false
+ Explosion
+ true
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/collide.wav
+
+
+ 0
+ 1
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 10
+ true
+ Swoosh
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/Rocket whoosh 01.wav
+
+
+ 0
+ 0.7
+ 10000
+ 10
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/Reset.rbxmx b/Tools/Models/Reset.rbxmx
new file mode 100644
index 0000000..17c6083
--- /dev/null
+++ b/Tools/Models/Reset.rbxmx
@@ -0,0 +1,41 @@
+
+ -
+
+
+ true
+ true
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ false
+ Reset
+ false
+
+
+
+
+
+ true
+
+
-
+
+
+ IconOverride
+
+ rbxassetid://1000000
+ true
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/RocketLauncher.rbxmx b/Tools/Models/RocketLauncher.rbxmx
new file mode 100644
index 0000000..97dacf4
--- /dev/null
+++ b/Tools/Models/RocketLauncher.rbxmx
@@ -0,0 +1,185 @@
+
+ -
+
+
+ true
+ true
+
+ 0.7
+ 0
+ -0.5
+ 0
+ 0
+ -1
+ -1
+ 0
+ 0
+ 0
+ 1
+ 0
+
+ false
+ RocketLauncher
+ true
+
+
+ rbxasset://Textures/Rocket.png
+
+
+ true
+
+
-
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 416.4301
+ 1.621786
+ 446.26
+ 1
+ 0
+ 0
+ 0
+ 6.301701E-05
+ 1
+ 0
+ -1
+ 6.301701E-05
+
+ false
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Handle
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 3
+ 1
+
+ 4.920006
+ 0.7400005
+ 0.8399998
+
+ true
+
+
-
+
+
+ 2
+ 2
+
+ rbxasset://fonts/rocketlauncher.mesh
+
+ 5
+ Mesh
+
+ 0
+ 0
+ 0
+
+
+ 0.75
+ 0.75
+ 0.75
+
+
+
+ rbxasset://textures/rocketlaunchertex.png
+
+
+ 1
+ 1
+ 1
+
+ true
+
+
+
+ -
+
+
+ 10
+ false
+ Explosion
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/collide.wav
+
+
+ 0
+ 1
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 10
+ true
+ Swoosh
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/Rocket whoosh 01.wav
+
+
+ 0
+ 0.7
+ 10000
+ 10
+ true
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/Slingshot.rbxmx b/Tools/Models/Slingshot.rbxmx
new file mode 100644
index 0000000..c9528ce
--- /dev/null
+++ b/Tools/Models/Slingshot.rbxmx
@@ -0,0 +1,163 @@
+
+ -
+
+
+ true
+ true
+
+ 0
+ -0.7
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ false
+ Slingshot
+ true
+
+
+ rbxasset://Textures/Slingshot.png
+
+
+ true
+
+
-
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 4
+ 0
+
+ 421.4299
+ 3.221793
+ 416.7602
+ 1
+ 0
+ -5.545024E-06
+ 0
+ 1
+ 0
+ 5.545024E-06
+ 0
+ 1
+
+ true
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Handle
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 1
+ 1
+
+ 2
+ 2.4
+ 1
+
+ true
+
+
-
+
+
+ 10
+ false
+ SlingshotSound
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds//Rubber band sling shot.mp3
+
+
+ 0
+ 1
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 2
+ 2
+
+ rbxasset://fonts/slingshot.mesh
+
+ 5
+ Mesh
+
+ 0
+ 0
+ 0
+
+
+ 0.5
+ 0.5
+ 0.5
+
+
+
+ rbxasset://textures/SlingshotTexture.png
+
+
+ 1
+ 1
+ 1
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/Superball.rbxmx b/Tools/Models/Superball.rbxmx
new file mode 100644
index 0000000..f4cca0f
--- /dev/null
+++ b/Tools/Models/Superball.rbxmx
@@ -0,0 +1,136 @@
+
+ -
+
+
+ true
+ true
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ false
+ Superball
+ true
+
+
+ rbxasset://Textures/Superball.png
+
+
+ true
+
+
-
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 393.3251
+ 6.242048
+ 433.0556
+ -0.3313951
+ 0.000743661
+ -0.9434918
+ -1.846121E-06
+ 0.9999996
+ 0.000788849
+ 0.943492
+ 0.0002631625
+ -0.331395
+
+ true
+ true
+ 0
+ 4291045404
+
+ true
+ 0.7
+ 0
+ 1
+ 1
+ 1
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Handle
+ 0.2
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 0
+ 0
+
+ 2
+ 2
+ 2
+
+ true
+
+
-
+
+
+ 10
+ false
+ Boing
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds//short spring sound.wav
+
+
+ 0
+ 1
+ 10000
+ 10
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/Sword.rbxmx b/Tools/Models/Sword.rbxmx
new file mode 100644
index 0000000..f4181f0
--- /dev/null
+++ b/Tools/Models/Sword.rbxmx
@@ -0,0 +1,207 @@
+
+ -
+
+
+ true
+ true
+
+ 0
+ 0
+ -1.5
+ 0
+ 0
+ 1
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+
+ false
+ Sword
+ true
+
+
+ rbxasset://Textures/Sword128.png
+
+
+ true
+
+
-
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 355.9418
+ 59.54162
+ 392.76
+ 5.960464E-08
+ -8.921718E-19
+ -0.9999999
+ -5.904603E-18
+ 1
+ -8.921718E-19
+ 0.9999999
+ 5.904603E-18
+ 5.960464E-08
+
+ true
+ true
+ 0
+ 4284702562
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Handle
+ 0.4
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 2
+ 1
+
+ 1
+ 0.8
+ 4
+
+ true
+
+
-
+
+
+ 10
+ false
+ Sound
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/unsheath.wav
+
+
+ 0
+ 1
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 10
+ false
+ Sound
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/swordlunge.wav
+
+
+ 0
+ 0.6
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 10
+ false
+ Sound
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/swordslash.wav
+
+
+ 0
+ 0.7
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 2
+ 2
+
+ rbxasset://fonts/sword.mesh
+
+ 5
+ Mesh
+
+ 0
+ 0
+ 0
+
+
+ 1
+ 1
+ 1
+
+
+
+ rbxasset://textures/SwordTexture.png
+
+
+ 1
+ 1
+ 1
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/Timebomb.rbxmx b/Tools/Models/Timebomb.rbxmx
new file mode 100644
index 0000000..58ac51c
--- /dev/null
+++ b/Tools/Models/Timebomb.rbxmx
@@ -0,0 +1,141 @@
+
+ -
+
+
+ true
+ true
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ -1
+ 0
+ 1
+ 0
+
+ false
+ Timebomb
+ true
+
+
+ rbxasset://Textures/Bomb.png
+
+
+ true
+
+
-
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 400.0722
+ 6.221753
+ 383.565
+ -0.9999679
+ -0.007971467
+ -6.34748E-06
+ -3.721468E-12
+ -0.0007962743
+ 0.9999996
+ -0.007971469
+ 0.9999677
+ 0.0007962488
+
+ true
+ true
+ 0
+ 4288914085
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Handle
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 0
+ 0
+
+ 2
+ 2
+ 2
+
+ true
+
+
-
+
+
+ 2
+ 2
+
+ rbxasset://fonts/timebomb.mesh
+
+ 5
+ Mesh
+
+ 0
+ 0
+ 0
+
+
+ 1
+ 1
+ 1
+
+
+
+ rbxasset://textures/bombtex.png
+
+
+ 1
+ 1
+ 1
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Models/Trowel.rbxmx b/Tools/Models/Trowel.rbxmx
new file mode 100644
index 0000000..4a24a36
--- /dev/null
+++ b/Tools/Models/Trowel.rbxmx
@@ -0,0 +1,163 @@
+
+ -
+
+
+ true
+ true
+
+ 0
+ -1.3
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ false
+ Trowel
+ true
+
+
+ rbxasset://Textures/Wall.png
+
+
+ true
+
+
-
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 452.9303
+ 2.522889
+ 429.0599
+ 1
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ -1
+ 0
+
+ true
+ true
+ 0
+ 4292330906
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Handle
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 2
+ 1
+
+ 1
+ 4.4
+ 1
+
+ true
+
+
-
+
+
+ 10
+ false
+ BuildSound
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds//bass.wav
+
+
+ 0
+ 1
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 2
+ 2
+
+ rbxasset://fonts/trowel.mesh
+
+ 5
+ Mesh
+
+ 0
+ 0
+ 0
+
+
+ 1
+ 1
+ 1
+
+
+
+ rbxasset://textures/TrowelTexture.png
+
+
+ 1
+ 1
+ 1
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Scripts/GravityHammer/GravityHammer.rbxmx b/Tools/Scripts/GravityHammer/GravityHammer.rbxmx
new file mode 100644
index 0000000..7de8632
--- /dev/null
+++ b/Tools/Scripts/GravityHammer/GravityHammer.rbxmx
@@ -0,0 +1,163 @@
+
+ -
+
+
+ false
+ true
+
+ 0
+ 0
+ -3.25
+ 0
+ 0
+ 1
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+
+ false
+ GravityHammer
+ true
+
+
+ http://www.roblox.com/asset/?id=1256305
+
+
+ true
+
+
-
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 433.6435
+ 11.72512
+ 424.1967
+ -0.9932989
+ 0.114088
+ 0.01847253
+ 0.01858202
+ -0.0001038264
+ 0.9998273
+ 0.1140701
+ 0.9934707
+ -0.002016852
+
+ true
+ true
+ 0
+ 4284702562
+
+ false
+
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ false
+ false
+ 256
+ Handle
+ 0.4
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+
+ -0.5
+ 0.5
+ 0
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 2
+ 1
+
+ 1
+ 0.8
+ 10
+
+ true
+
+
-
+
+
+ 10
+ false
+ Sound
+ false
+ 1
+ false
+ 0
+
[null]
+
+ http://www.roblox.com/asset/?id=1255794
+
+
+ 0
+ 1
+ 10000
+ 10
+ true
+
+
+ -
+
+
+ 2
+ 2
+
+ http://www.roblox.com/asset/?id=1256290
+
+ 5
+ Mesh
+
+ 0
+ 0
+ 0
+
+
+ 0.05
+ 0.05
+ 0.05
+
+
+
+ http://www.roblox.com/asset/?id=1256283
+
+
+ 1
+ 1
+ 1
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Scripts/GravityHammer/SwordScript.server.lua b/Tools/Scripts/GravityHammer/SwordScript.server.lua
new file mode 100644
index 0000000..53b34a6
--- /dev/null
+++ b/Tools/Scripts/GravityHammer/SwordScript.server.lua
@@ -0,0 +1,158 @@
+--Made by Luckymaxer
+
+Tool = script.Parent
+Handle = Tool:WaitForChild("Handle")
+
+Sound = Handle:WaitForChild("Sound")
+
+Players = game:GetService("Players")
+Debris = game:GetService("Debris")
+
+Debounce = false
+
+Tool.Enabled = true
+
+local function spawnSound(s)
+ local sound = s:Clone()
+ sound.Parent = s.Parent
+ sound:Play()
+ sound.Ended:Connect(function ()
+ sound:Destroy()
+ end)
+end
+function TagHumanoid(humanoid, player)
+ local Creator_Tag = Instance.new("ObjectValue")
+ Creator_Tag.Name = "creator"
+ Creator_Tag.Value = player
+ Debris:AddItem(Creator_Tag, 2)
+ Creator_Tag.Parent = humanoid
+end
+
+function UntagHumanoid(humanoid)
+ for i, v in pairs(humanoid:GetChildren()) do
+ if v:IsA("ObjectValue") and v.Name == "creator" then
+ v:Destroy()
+ end
+ end
+end
+
+function FindCharacterAncestor(Parent)
+ if Parent and Parent ~= game:GetService("Workspace") then
+ local humanoid = Parent:FindFirstChild("Humanoid")
+ if humanoid then
+ return Parent, humanoid
+ else
+ return FindCharacterAncestor(Parent.Parent)
+ end
+ end
+ return nil
+end
+
+function Blow(Hit)
+ RightGrip = RightGrip:Clone()
+ if Hit and Hit.Parent then
+ local humanoid = Hit.Parent:FindFirstChild("Humanoid")
+ if humanoid == Humanoid then
+ return
+ end
+ if humanoid then
+ Propel(Hit)
+ UntagHumanoid(humanoid)
+ TagHumanoid(humanoid, Player)
+ humanoid.Health = humanoid.Health - 49
+ else
+ Explode(Hit)
+ end
+ end
+end
+
+function Propel(Part)
+ if not Part or not Part.Parent or Part.Anchored then
+ return
+ end
+ local character, humanoid = FindCharacterAncestor(Part)
+ if character == Character then
+ return
+ end
+ local Direction = (Part.Position - Torso.Position).unit
+ Direction = Direction + Vector3.new(0, 1, 0)
+ Direction = Direction * 200
+ Part.Velocity = Part.Velocity + Direction
+end
+
+function Explode(Part)
+ if not Part or not Part.Parent or Debounce then
+ return
+ end
+ Debounce = true
+ local Direction = (Part.Position - Torso.Position).unit
+ local Position = Direction * 12 + Torso.Position
+ local Explosion = Instance.new("Explosion")
+ Explosion.ExplosionType = Enum.ExplosionType.NoCraters
+ Explosion.BlastRadius = 4
+ Explosion.BlastPressure = 1
+ Explosion.Position = Position
+ Explosion.Hit:connect(function(Part, Distance)
+ Propel(Part)
+ end)
+ local owner = Instance.new("ObjectValue")
+ owner.Name = "Owner"
+ owner.Value = Player
+ owner.Parent = Explosion
+ Explosion.Parent = game:GetService("Workspace")
+ wait(0.1)
+ local GripClone = RightGrip:Clone()
+ GripClone.Parent = RightArm
+ Debounce = false
+end
+
+function Attack()
+ spawnSound(Sound)
+ local Anim = Instance.new("StringValue")
+ Anim.Name = "toolanim"
+ Anim.Value = "Slash"
+ Debris:AddItem(Anim, 2)
+ Anim.Parent = Tool
+end
+
+function Lunge()
+ Attack()
+ local Force = Instance.new("BodyPosition")
+ Force.maxForce = Vector3.new(1e+005, 1e+004, 1e+005)
+ local Direction = Humanoid.targetPoint
+ if ((Direction - Handle.Position).magnitude > 15) then
+ return
+ end
+ Force.position = Direction
+ Debris:AddItem(Force, 0.25)
+ Force.Parent = Handle
+end
+
+function Activated()
+ if not Tool.Enabled or not Humanoid.Parent or Humanoid.Health == 0 or not Torso.Parent or not RightArm.Parent or not RightGrip then
+ return
+ end
+ Tool.Enabled = false
+ connection = Handle.Touched:connect(Blow)
+ Lunge()
+ wait(0.4)
+ connection:disconnect()
+ Tool.Enabled = true
+end
+
+function Equipped()
+ Character = Tool.Parent
+ Player = Players:GetPlayerFromCharacter(Character)
+ Humanoid = Character:FindFirstChild("Humanoid")
+ Torso = Character:FindFirstChild("Torso")
+ RightArm = Character:FindFirstChild("Right Arm")
+ if RightArm then
+ RightGrip = RightArm:WaitForChild("RightGrip",1)
+ end
+ if not Player or not Humanoid or Humanoid.Health == 0 or not Torso or not RightArm or not RightGrip then
+ return
+ end
+end
+
+Tool.Activated:connect(Activated)
+Tool.Equipped:connect(Equipped)
\ No newline at end of file
diff --git a/Tools/Scripts/JetBoots/Jetboots.server.lua b/Tools/Scripts/JetBoots/Jetboots.server.lua
new file mode 100644
index 0000000..2de58f6
--- /dev/null
+++ b/Tools/Scripts/JetBoots/Jetboots.server.lua
@@ -0,0 +1,76 @@
+print("Jet Boots loaded")
+
+bin = script.Parent
+
+walking = false
+reloadtime = 0
+
+local thrust = Instance.new("BodyVelocity")
+local velocity = 0
+local max_velocity = 30
+local flight_time = 6
+local localPlayer
+
+
+
+function onStart()
+ local char = localPlayer.Character
+ local head = char:WaitForChild("Head")
+
+ print("start walking")
+ walking = true
+ reloadtime = 8.0
+
+ thrust.Parent = head
+
+ thrust.velocity = Vector3.new(0,velocity,0)
+ thrust.maxForce = Vector3.new(0,4e+003,0)
+
+ local sound = head:findFirstChild("JetbootSound")
+ if sound == nil then
+ sound = Instance.new("Sound")
+ sound.Name = "JetbootSound"
+ sound.SoundId = "rbxasset://sounds\\Rocket whoosh 01.wav"
+ sound.Looped = true
+ sound.Parent = head
+ end
+ sound:play()
+
+end
+
+function onDeactivated()
+ print("stop walking")
+ local char = localPlayer.Character
+ local head = char:WaitForChild("Head")
+ walking = false
+ thrust.Parent = nil
+ local sound = head:findFirstChild("JetbootSound")
+ if sound ~= nil then sound:stop() end
+ bin.Enabled = false
+ wait(reloadtime)
+ bin.Enabled = true
+ reloadtime = 0
+end
+
+function onActivated()
+ local char = bin.Parent
+ localPlayer = game.Players:GetPlayerFromCharacter(char)
+ if not localPlayer then return end
+ if reloadtime > 0 then return end
+ if walking then return end
+
+ onStart()
+
+ local time = 0
+ while walking do
+ wait(.2)
+ time = time + .2
+ velocity = (max_velocity * (time / flight_time)) + 3
+ thrust.velocity = Vector3.new(0,velocity,0)
+
+ if time > flight_time then onDeactivated() end
+ end
+end
+
+bin.Activated:Connect(onActivated)
+bin.Deactivated:Connect(onDeactivated)
\ No newline at end of file
diff --git a/Tools/Scripts/Multirocket/RocketScript.server.lua b/Tools/Scripts/Multirocket/RocketScript.server.lua
new file mode 100644
index 0000000..b2678fa
--- /dev/null
+++ b/Tools/Scripts/Multirocket/RocketScript.server.lua
@@ -0,0 +1,71 @@
+r = game:service("RunService")
+
+shaft = script.Parent
+position = Vector3.new(0,0,0)
+debris = game:GetService("Debris")
+
+function tagHumanoid(humanoid)
+ -- todo: make tag expire
+ local tag = shaft:findFirstChild("creator")
+ if tag ~= nil then
+ -- kill all other tags
+ while(humanoid:findFirstChild("creator") ~= nil) do
+ humanoid:findFirstChild("creator").Parent = nil
+ end
+
+ local new_tag = tag:clone()
+ new_tag.Parent = humanoid
+ debris:AddItem(new_tag, 1)
+ end
+end
+
+local function onExplosionHit(hit)
+ local char = hit.Parent
+ if char then
+ local humanoid = char:FindFirstChild("Humanoid")
+ if humanoid then
+ tagHumanoid(humanoid)
+ end
+ end
+end
+
+function fly()
+ direction = shaft.CFrame.lookVector
+ position = position + direction
+ error = position - shaft.Position
+ shaft.Velocity = 7*error
+end
+
+function blow()
+ swoosh:Stop()
+ explosion = Instance.new("Explosion")
+ explosion.Position = shaft.Position
+ explosion.Parent = game.Workspace
+ explosion.Hit:Connect(onExplosionHit)
+ connection:disconnect()
+ shaft.Explosion:Play()
+ shaft.Anchored = true
+ shaft.CanCollide = false
+ shaft.Transparency = 1
+
+ shaft.Explosion:Play()
+ shaft.Explosion.Ended:Wait()
+ shaft:Destroy()
+end
+
+t, s = r.Stepped:wait()
+
+swoosh = script.Parent.Swoosh
+swoosh:Play()
+
+position = shaft.Position
+d = t + 10.0 - s
+connection = shaft.Touched:connect(blow)
+
+while t < d do
+ fly()
+ t = r.Stepped:wait()
+end
+
+swoosh:Stop()
+shaft:remove()
diff --git a/Tools/Scripts/Multirocket/ServerLauncher.server.lua b/Tools/Scripts/Multirocket/ServerLauncher.server.lua
new file mode 100644
index 0000000..d01eb1c
--- /dev/null
+++ b/Tools/Scripts/Multirocket/ServerLauncher.server.lua
@@ -0,0 +1,93 @@
+local Rocket = Instance.new("Part")
+local Tool = script.Parent
+
+Rocket.Locked = true
+Rocket.BackSurface = 3
+Rocket.BottomSurface = 3
+Rocket.FrontSurface = 3
+Rocket.LeftSurface = 3
+Rocket.RightSurface = 3
+Rocket.TopSurface = 3
+Rocket.Size = Vector3.new(1,1,4)
+Rocket.BrickColor = BrickColor.Red()
+
+Tool.RocketScript:clone().Parent = Rocket
+Tool.Explosion:clone().Parent = Rocket
+Tool.Swoosh:clone().Parent = Rocket
+
+
+function fire(vTarget)
+
+ local vCharacter = Tool.Parent;
+
+ local vHandle = vCharacter:findFirstChild("Head")
+ if vHandle == nil then
+ print("Handle not found")
+ return
+ end
+
+ local dir = vTarget - vHandle.Position
+
+ dir = computeDirection(dir)
+
+ local missile = Rocket:clone()
+
+ local pos = vHandle.Position + (dir * 6)
+
+ --missile.Position = pos
+ missile.CFrame = CFrame.new(pos, pos + dir)
+
+ local creator_tag = Instance.new("ObjectValue")
+
+ local vPlayer = game.Players:playerFromCharacter(vCharacter)
+
+ if vPlayer == nil then
+ print("Player not found")
+ else
+ if (vPlayer.Neutral == false) then -- nice touch
+ missile.BrickColor = vPlayer.TeamColor
+ end
+ end
+
+ creator_tag.Value =vPlayer
+ creator_tag.Name = "creator"
+ creator_tag.Parent = missile
+
+ missile.RocketScript.Disabled = false
+
+ missile.Parent = game.Workspace
+end
+
+function computeDirection(vec)
+ local lenSquared = vec.magnitude * vec.magnitude
+ local invSqrt = 1 / math.sqrt(lenSquared)
+ return Vector3.new(vec.x * invSqrt, vec.y * invSqrt, vec.z * invSqrt)
+end
+
+Tool.Enabled = true
+function onActivated()
+ if not Tool.Enabled then
+ return
+ end
+
+ Tool.Enabled = false
+
+ local character = Tool.Parent;
+ local humanoid = character.Humanoid
+ if humanoid == nil then
+ print("Humanoid not found")
+ return
+ end
+
+ local targetPos = humanoid.TargetPoint
+
+ fire(targetPos)
+
+ wait(1)
+
+ Tool.Enabled = true
+end
+
+
+script.Parent.Activated:connect(onActivated)
+
diff --git a/Tools/Scripts/PaintballGun/BrickCleanup.server.lua b/Tools/Scripts/PaintballGun/BrickCleanup.server.lua
new file mode 100644
index 0000000..5bec096
--- /dev/null
+++ b/Tools/Scripts/PaintballGun/BrickCleanup.server.lua
@@ -0,0 +1,4 @@
+-- this script removes its parent from the workspace after 120 seconds
+
+wait(120)
+script.Parent.Parent = nil
diff --git a/Tools/Scripts/PaintballGun/Paintball.server.lua b/Tools/Scripts/PaintballGun/Paintball.server.lua
new file mode 100644
index 0000000..e03adf0
--- /dev/null
+++ b/Tools/Scripts/PaintballGun/Paintball.server.lua
@@ -0,0 +1,83 @@
+ball = script.Parent
+damage = 20
+
+function onTouched(hit)
+ if not (hit.CanCollide and hit.Parent) then
+ return
+ end
+
+ local humanoid = hit.Parent:findFirstChild("Humanoid")
+
+ if hit:getMass() < 1.2 * 200 then
+ hit.BrickColor = ball.BrickColor
+ end
+
+ -- make a splat
+ for i = 1, 3 do
+ local s = Instance.new("Part")
+ s.Shape = 1 -- block
+ s.formFactor = 2 -- plate
+ s.Size = Vector3.new(1,.4,1)
+ s.BrickColor = ball.BrickColor
+
+ local v = Vector3.new(math.random(-1,1), math.random(0,1), math.random(-1,1))
+ s.Velocity = 15 * v
+ s.CFrame = CFrame.new(ball.Position + v, v)
+
+ ball.BrickCleanup:clone().Parent = s
+
+ s.BrickCleanup.Disabled = false
+ s.Parent = game.Workspace
+ end
+
+
+ if humanoid ~= nil then
+ local canDamage = true
+ local tag = ball:FindFirstChild("creator")
+ local char = humanoid:FindFirstAncestorWhichIsA("Model")
+
+ if tag and char then
+ local creator = tag.Value
+ local player = game.Players:GetPlayerFromCharacter(char)
+ if creator and player then
+ if creator.Team and player.Team and creator.Team == player.Team then
+ canDamage = false
+ end
+ end
+ end
+
+ if canDamage then
+ tagHumanoid(humanoid)
+ humanoid:TakeDamage(damage)
+ wait(2)
+ untagHumanoid(humanoid)
+ end
+ end
+
+ connection:disconnect()
+ ball.Parent = nil
+end
+
+function tagHumanoid(humanoid)
+ -- todo: make tag expire
+ local tag = ball:findFirstChild("creator")
+ if tag ~= nil then
+ local new_tag = tag:clone()
+ new_tag.Parent = humanoid
+ end
+end
+
+
+function untagHumanoid(humanoid)
+ if humanoid ~= nil then
+ local tag = humanoid:findFirstChild("creator")
+ if tag ~= nil then
+ tag.Parent = nil
+ end
+ end
+end
+
+connection = ball.Touched:connect(onTouched)
+
+wait(8)
+ball.Parent = nil
diff --git a/Tools/Scripts/PaintballGun/PaintballShooter.server.lua b/Tools/Scripts/PaintballGun/PaintballShooter.server.lua
new file mode 100644
index 0000000..4f6a199
--- /dev/null
+++ b/Tools/Scripts/PaintballGun/PaintballShooter.server.lua
@@ -0,0 +1,85 @@
+Tool = script.Parent
+
+colors = {45, 119, 21, 24, 23, 105, 104}
+
+function fire(v)
+
+ Tool.Handle.Fire:play()
+
+
+ local vCharacter = Tool.Parent
+ local vPlayer = game.Players:playerFromCharacter(vCharacter)
+
+ local missile = Instance.new("Part")
+
+
+
+ local spawnPos = vCharacter.PrimaryPart.Position
+
+
+
+ spawnPos = spawnPos + (v * 8)
+
+ missile.Position = spawnPos
+ missile.Size = Vector3.new(1,1,1)
+ missile.Velocity = v * 100
+ missile.BrickColor = BrickColor.new(colors[math.random(1, #colors)])
+ missile.Shape = 0
+ missile.BottomSurface = 0
+ missile.TopSurface = 0
+ missile.Name = "Paintball"
+ missile.Elasticity = 0
+ missile.Reflectance = 0
+ missile.Friction = .9
+
+ local force = Instance.new("BodyForce")
+ force.force = Vector3.new(0,45,0)
+ force.Parent = missile
+
+ Tool.BrickCleanup:clone().Parent = missile
+
+ local new_script = script.Parent.Paintball:clone()
+ new_script.Disabled = false
+ new_script.Parent = missile
+
+ local creator_tag = Instance.new("ObjectValue")
+ creator_tag.Value = vPlayer
+ creator_tag.Name = "creator"
+ creator_tag.Parent = missile
+
+
+
+ missile.Parent = game.Workspace
+ missile:SetNetworkOwner(vPlayer)
+end
+
+
+
+Tool.Enabled = true
+function onActivated()
+
+ if not Tool.Enabled then
+ return
+ end
+
+ Tool.Enabled = false
+
+ local character = Tool.Parent;
+ local humanoid = character.Humanoid
+ if humanoid == nil then
+ print("Humanoid not found")
+ return
+ end
+
+ local targetPos = humanoid.TargetPoint
+ local lookAt = (targetPos - character.Head.Position).unit
+
+ fire(lookAt)
+
+ wait(.5)
+
+ Tool.Enabled = true
+end
+
+
+script.Parent.Activated:connect(onActivated)
diff --git a/Tools/Scripts/Plane/ControlScheme.lua b/Tools/Scripts/Plane/ControlScheme.lua
new file mode 100644
index 0000000..327c155
--- /dev/null
+++ b/Tools/Scripts/Plane/ControlScheme.lua
@@ -0,0 +1,32 @@
+return
+{
+ ActivateContext = "Steer Plane";
+ Buttons =
+ {
+ ["Y"] =
+ {
+ Label = "Start Engine";
+ XboxButton = "ButtonY";
+ };
+ ["X"] =
+ {
+ Label = "Stop Engine";
+ XboxButton = "ButtonB";
+ };
+ ["F"] =
+ {
+ Label = "Fire Weapon";
+ XboxButton = "ButtonL2";
+ };
+ ["G"] =
+ {
+ Label = "Barrel Roll";
+ XboxButton = "ButtonL1";
+ };
+ ["T"] =
+ {
+ Label = "Stunt Roll";
+ XboxButton = "ButtonR1";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tools/Scripts/Plane/IconOverride.txt b/Tools/Scripts/Plane/IconOverride.txt
new file mode 100644
index 0000000..8de79c4
--- /dev/null
+++ b/Tools/Scripts/Plane/IconOverride.txt
@@ -0,0 +1 @@
+rbxassetid://1067894989
\ No newline at end of file
diff --git a/Tools/Scripts/Plane/PlaneTool/FlyScript.server.lua b/Tools/Scripts/Plane/PlaneTool/FlyScript.server.lua
new file mode 100644
index 0000000..fcc310d
--- /dev/null
+++ b/Tools/Scripts/Plane/PlaneTool/FlyScript.server.lua
@@ -0,0 +1,19 @@
+local engine = script.Parent.Parent.Engine
+local speed = 90
+if engine:FindFirstChild("EngineSpeed") then
+ speed = engine.EngineSpeed.Value
+end
+
+local bv = engine:FindFirstChild("EngineForce")
+if not bv then
+ bv = Instance.new("BodyVelocity")
+ bv.Name = "EngineForce"
+ bv.MaxForce = Vector3.new(10e7,10e7,10e7)
+ bv.Velocity = Vector3.new()
+ bv.Parent = engine
+end
+
+while true do
+ wait(.1)
+ bv.Velocity = engine.CFrame.lookVector * speed
+end
diff --git a/Tools/Scripts/Plane/PlaneTool/init.server.lua b/Tools/Scripts/Plane/PlaneTool/init.server.lua
new file mode 100644
index 0000000..9b9b97c
--- /dev/null
+++ b/Tools/Scripts/Plane/PlaneTool/init.server.lua
@@ -0,0 +1,311 @@
+local Players = game:GetService("Players")
+
+local tool = script.Parent
+local plane = nil
+local hold = false
+local debounce = false
+local planedebounce = false
+local stuntdebounce = false
+local controlling = false
+local player
+
+local rocket = tool:WaitForChild("Rocket")
+rocket.Parent = nil
+
+local function fireRocket(pln,spn)
+ local missile = rocket:Clone()
+ missile.CFrame = spn.CFrame * CFrame.new(0, 0, -35)
+ missile.Anchored = false
+
+ missile.RocketScript.Disabled = false
+ missile.Parent = workspace
+
+ local creator_tag = Instance.new("ObjectValue")
+ creator_tag.Value = player
+ creator_tag.Name = "creator"
+ creator_tag.Parent = missile
+
+ missile.Owner.Value = pln
+end
+
+local function fireDeathLaser(engine)
+ local dir = engine.CFrame.lookVector
+ for i = 1, 50 do
+ local ex = Instance.new("Explosion")
+ ex.BlastRadius = 6
+ ex.BlastPressure = 8000000
+ ex.Position = engine.Position + (dir * 50) + (dir * i * 12)
+ ex.Parent = workspace
+ end
+ if engine:FindFirstChild("DeathLaser") then
+ engine.DeathLaser:Play()
+ end
+end
+
+local function computeDirection(vec)
+ local lenSquared = vec.magnitude * vec.magnitude
+ local invSqrt = 1 / math.sqrt(lenSquared)
+ return Vector3.new(vec.x * invSqrt, vec.y * invSqrt, vec.z * invSqrt)
+end
+
+local function move(target, engine)
+ local bg = engine:findFirstChild("BodyGyro")
+ if bg then
+ local origincframe = bg.cframe
+ local dir = (target - engine.Position).unit
+ local spawnPos = engine.Position
+
+ local pos = spawnPos + (dir * 1)
+
+ bg.maxTorque = Vector3.new(900000, 900000, 900000)
+ bg.cframe = CFrame.new(pos, pos + dir)
+ wait(0.1)
+ bg.maxTorque = Vector3.new(0, 0, 0)
+ bg.cframe = origincframe
+ end
+end
+
+function findPlane(char)
+ local player = Players:GetPlayerFromCharacter(char)
+ local humanoid = char:FindFirstChildWhichIsA("Humanoid")
+ local plane = char:FindFirstChildWhichIsA("Model")
+ if plane and plane.Name == "Plane" then
+ local color_tag = plane:FindFirstChild("PlaneColor")
+ if color_tag then
+ color_tag.Value = player.TeamColor
+ end
+ local seat = plane:FindFirstChildWhichIsA("Seat",true)
+ if seat then
+ local occupant = seat.Occupant
+ if humanoid == occupant then
+ return plane
+ end
+ end
+ end
+end
+
+local function onActivated()
+ local char = tool.Parent
+ local humanoid = char:FindFirstChildWhichIsA("Humanoid")
+ if humanoid then
+ local vehicle = findPlane(char)
+ if vehicle ~= nil and debounce == false and planedebounce == false and stuntdebounce == false then
+ debounce = true
+ player = Players:GetPlayerFromCharacter(char)
+ controlling = true
+
+ local engine = vehicle.Parts.Engine
+
+ while wait() do
+ local target = humanoid.TargetPoint
+ if engine:FindFirstChild("FlyScript") ~= nil then
+ move(target, engine)
+ end
+ if planedebounce or not controlling then
+ break
+ end
+ end
+
+ wait(.1)
+ debounce = false
+ end
+ end
+end
+
+local function onDeactivated()
+ controlling = false
+end
+
+local function onKeyDown(key)
+ if (key~=nil) then
+ key = key:lower()
+ local char = tool.Parent
+ local player = game.Players:GetPlayerFromCharacter(char)
+ if player==nil then return end
+ local vehicle = findPlane(char)
+ if (vehicle==nil) then return end
+ plane = vehicle.Parts
+ local engine = vehicle.Parts.Engine
+ if (key=="f") and tool.Enabled then
+ local engine = plane.Engine
+ if engine:FindFirstChild("DeathLaserMode") and engine.DeathLaserMode.Value then
+ fireDeathLaser(engine)
+ else
+ fireRocket(vehicle,plane.Gun1)
+ fireRocket(vehicle,plane.Gun2)
+ end
+ tool.Enabled = false
+ wait(1)
+ tool.Enabled = true
+ end
+ if (key=="x") and not planedebounce then
+ local power = plane.Engine:FindFirstChild("FlyScript")
+ if (power ~= nil) then
+ power:Destroy()
+ tool.Enabled = false
+ for _,v in pairs(vehicle:GetDescendants()) do
+ if v:IsA("ParticleEmitter") and v.Name == "EngineSparkles" then
+ v.Enabled = false
+ elseif v:IsA("BodyVelocity") and v.Name == "EngineForce" then
+ v:Destroy()
+ end
+ end
+ end
+ end
+ if (key=="y") then
+ local power = plane.Engine:FindFirstChild("FlyScript")
+ if not power then
+ local fly = script.FlyScript:Clone()
+ fly.Disabled = false
+ fly.Parent = plane.Engine
+ tool.Enabled = true
+ for _,v in pairs(vehicle:GetDescendants()) do
+ if v:IsA("ParticleEmitter") and v.Name == "EngineSparkles" then
+ v.Enabled = true
+ elseif v:IsA("BasePart") and v:CanSetNetworkOwnership() then
+ v:SetNetworkOwner(player)
+ end
+ end
+ end
+ end
+ if (key=="k") and not planedebounce then
+ planedebounce = true
+ for i = 1,4 do
+ wait()
+ engine.RotVelocity = engine.RotVelocity + Vector3.new(0, -0.7, 0)
+ end
+ planedebounce = false
+ end
+ if (key=="h") and not planedebounce then
+ planedebounce = true
+ for i = 1,4 do
+ wait()
+ engine.RotVelocity = engine.RotVelocity + Vector3.new(0, 0.7, 0)
+ end
+ end
+ if (key=="j") and not planedebounce then
+ local body = plane.Engine.BodyGyro
+ body.maxTorque = Vector3.new(9000, 9000, 9000)
+
+ local position = engine.CFrame * Vector3.new(0, 0.5, -4)
+ local dir = position - engine.Position
+
+ dir = computeDirection(dir)
+
+ local spawnPos = engine.Position
+
+ local pos = spawnPos + (dir * 8)
+
+ body.cframe = CFrame.new(pos, pos + dir)
+ wait(.2)
+ body.maxTorque = Vector3.new(0, 0, 0)
+ end
+ if (key=="l") and planedebounce == false then
+ local body = plane.Engine.BodyGyro
+ body.maxTorque = Vector3.new(9000, 0, 0)
+ local frame = plane:FindFirstChild("OriginCFrame")
+ if frame ~= nil then
+ body.cframe = frame.Value
+ end
+ wait(0.1)
+ body.maxTorque = Vector3.new(0, 0, 0)
+ end
+ if (key=="u") and planedebounce == false then
+ local body = plane.Engine.BodyGyro
+ body.maxTorque = Vector3.new(9000, 9000, 9000)
+
+ local position = engine.CFrame * Vector3.new(0, -0.5, -4)
+ local dir = position - engine.Position
+
+ dir = computeDirection(dir)
+
+ local spawnPos = engine.Position
+
+ local pos = spawnPos + (dir * 8)
+
+ body.cframe = CFrame.new(pos, pos + dir)
+ wait(.2)
+ body.maxTorque = Vector3.new(0, 0, 0)
+ end
+ if (key=="g") and planedebounce == false and stuntdebounce == false then
+ planedebounce = true
+ stuntdebounce = true
+ plane.Parent.Stunt.Value = 1
+ local body = plane.Engine.BodyGyro
+ body.maxTorque = Vector3.new(9000, 9000, 9000)
+
+ local currentframe = plane.Engine.CFrame
+
+ for i = 1,6 do
+ body.cframe = plane.Engine.CFrame * CFrame.fromEulerAnglesXYZ(0, 0, 30)
+ wait(.2)
+ end
+
+ body.cframe = currentframe
+ wait(.6)
+
+ body.maxTorque = Vector3.new(0, 0, 0)
+ planedebounce = false
+ plane.Parent.Stunt.Value = 0
+ wait(3)
+ stuntdebounce = false
+ end
+ if (key=="t") and planedebounce == false and stuntdebounce == false then
+ planedebounce = true
+ stuntdebounce = true
+ plane.Parent.Stunt.Value = 1
+ local body = plane.Engine.BodyGyro
+ body.maxTorque = Vector3.new(9000, 9000, 9000)
+
+ local currentframe = plane.Engine.CFrame
+ local valy = 30
+ local valz = 30
+
+ for i = 1,8 do
+ body.cframe = currentframe * CFrame.fromEulerAnglesXYZ(0, valy, valz)
+ valy = valy +50
+ valz = valz +100
+ wait(.1)
+ end
+
+ body.cframe = currentframe * CFrame.fromEulerAnglesXYZ(0, 600, 0)
+
+ wait(.5)
+
+ body.maxTorque = Vector3.new(0, 0, 0)
+ planedebounce = false
+ plane.Parent.Stunt.Value = 0
+ wait(4)
+ stuntdebounce = false
+ end
+ end
+end
+
+spawn(function ()
+ local iconOverride = tool:WaitForChild("IconOverride")
+ while wait(.25) do
+ local isToolInactive = true
+ local char = tool.Parent
+ if char and char:IsA("Model") then
+ local plane = findPlane(char)
+ if plane then
+ if plane.Parts.Engine:FindFirstChild("FlyScript") then
+ isToolInactive = false
+ end
+ end
+ end
+ iconOverride.Parent = isToolInactive and tool or nil
+ end
+end)
+
+local keyEvent = tool:WaitForChild("KeyEvent",99999)
+
+local function onKeyEvent(key,down)
+ if down then
+ onKeyDown(key)
+ end
+end
+
+tool.Activated:Connect(onActivated)
+tool.Deactivated:Connect(onDeactivated)
+keyEvent.Event:Connect(onKeyEvent)
\ No newline at end of file
diff --git a/Tools/Scripts/Plane/Rocket/RocketScript.server.lua b/Tools/Scripts/Plane/Rocket/RocketScript.server.lua
new file mode 100644
index 0000000..965b584
--- /dev/null
+++ b/Tools/Scripts/Plane/Rocket/RocketScript.server.lua
@@ -0,0 +1,95 @@
+r = game:service("RunService")
+
+shaft = script.Parent
+position = shaft.Position
+
+function fly()
+ direction = shaft.CFrame.lookVector
+ position = position + 35*direction
+ error = position - shaft.Position
+ shaft.Velocity = 5*error
+end
+
+function blow()
+ swoosh:stop()
+ explosion = Instance.new("Explosion")
+ explosion.Position = shaft.Position
+ explosion.BlastRadius = 10
+
+ -- find instigator tag
+ local creator = script.Parent:findFirstChild("creator")
+ if creator ~= nil then
+ explosion.Hit:connect(function(part, distance) onPlayerBlownUp(part, distance, creator) end)
+ end
+
+ explosion.Parent = game.Workspace
+ connection:disconnect()
+ wait(.1)
+ shaft:remove()
+end
+
+function onTouch(hit)
+ if hit.Name == "Building" or
+ hit.Name == "Safe" then
+ swoosh:stop()
+ shaft:remove()
+ return end
+
+ local parent = hit.Parent.Parent
+ local owner = shaft.Owner
+ if owner ~= nil then
+ if parent ~= nil and owner.Value ~= nil then
+ if parent ~= owner.Value then
+ local stunt = parent:FindFirstChild("Stunt")
+ if stunt ~= nil then
+ if stunt.Value ~= 1 then
+ blow()
+ end
+ else
+ blow()
+ end
+ end
+ end
+ end
+end
+
+function onPlayerBlownUp(part, distance, creator)
+ if part.Name == "Head" then
+ local humanoid = part.Parent:findFirstChild("Humanoid")
+ tagHumanoid(humanoid, creator)
+ end
+end
+
+function tagHumanoid(humanoid, creator)
+ if creator ~= nil then
+ local new_tag = creator:clone()
+ new_tag.Parent = humanoid
+ end
+end
+
+function untagHumanoid(humanoid)
+ if humanoid ~= nil then
+ local tag = humanoid:findFirstChild("creator")
+ if tag ~= nil then
+ tag.Parent = nil
+ end
+ end
+end
+
+t, s = r.Stepped:wait()
+
+swoosh = script.Parent.Swoosh
+swoosh:play()
+
+d = t + 4.0 - s
+connection = shaft.Touched:connect(onTouch)
+
+while t < d do
+ fly()
+ t = r.Stepped:wait()
+end
+
+-- at max range
+script.Parent.Explosion.PlayOnRemove = false
+swoosh:stop()
+shaft:remove()
diff --git a/Tools/Scripts/Reset/IconOverride.txt b/Tools/Scripts/Reset/IconOverride.txt
new file mode 100644
index 0000000..60cec04
--- /dev/null
+++ b/Tools/Scripts/Reset/IconOverride.txt
@@ -0,0 +1 @@
+rbxassetid://1000000
\ No newline at end of file
diff --git a/Tools/Scripts/Reset/Reset.server.lua b/Tools/Scripts/Reset/Reset.server.lua
new file mode 100644
index 0000000..56b07f3
--- /dev/null
+++ b/Tools/Scripts/Reset/Reset.server.lua
@@ -0,0 +1,13 @@
+local tool = script.Parent
+
+local function onActivated()
+ local char = tool.Parent
+ if char then
+ local humanoid = char:FindFirstChild("Humanoid")
+ if humanoid then
+ humanoid.Health = 0
+ end
+ end
+end
+
+tool.Activated:Connect(onActivated)
\ No newline at end of file
diff --git a/Tools/Scripts/RocketLauncher/RocketScript.server.lua b/Tools/Scripts/RocketLauncher/RocketScript.server.lua
new file mode 100644
index 0000000..16f685a
--- /dev/null
+++ b/Tools/Scripts/RocketLauncher/RocketScript.server.lua
@@ -0,0 +1,91 @@
+r = game:service("RunService")
+
+shaft = script.Parent
+position = Vector3.new(0,0,0)
+debris = game:GetService("Debris")
+
+function tagHumanoid(humanoid)
+ -- todo: make tag expire
+ local tag = shaft:findFirstChild("creator")
+ if tag ~= nil then
+ -- kill all other tags
+ while(humanoid:findFirstChild("creator") ~= nil) do
+ humanoid:findFirstChild("creator").Parent = nil
+ end
+
+ local new_tag = tag:clone()
+ new_tag.Parent = humanoid
+ debris:AddItem(new_tag, 1)
+ end
+end
+
+local function onExplosionHit(hit)
+ local char = hit:FindFirstAncestorWhichIsA("Model")
+ if char then
+ local humanoid = char:FindFirstChild("Humanoid")
+ if humanoid then
+ tagHumanoid(humanoid)
+ end
+ end
+end
+
+function fly()
+ local direction = shaft.CFrame.lookVector
+ position = position + direction
+ shaft.Velocity = position - shaft.Position
+end
+
+function blow(hit)
+ local canExplode = true
+ local char = hit:FindFirstAncestorWhichIsA("Model")
+
+ if char then
+ local humanoid = char:FindFirstChild("Humanoid")
+ if humanoid then
+ local tag = shaft:FindFirstChild("creator")
+ local player = game.Players:GetPlayerFromCharacter(char)
+ if tag and player and tag.Value == player then
+ canExplode = false
+ end
+ end
+ end
+
+ if canExplode then
+ local tag = shaft:FindFirstChild("creator")
+ swoosh:Stop()
+
+ if tag then
+ local explosion = Instance.new("Explosion")
+ explosion.Position = shaft.Position
+ tag:Clone().Parent = explosion
+ explosion.Parent = workspace
+ explosion.Hit:Connect(onExplosionHit)
+ connection:disconnect()
+ shaft.Explosion:Play()
+ shaft.Anchored = true
+ shaft.CanCollide = false
+ shaft.Transparency = 1
+
+ shaft.Explosion:Play()
+ shaft.Explosion.Ended:Wait()
+ shaft:Destroy()
+ end
+ end
+end
+
+t, s = r.Stepped:wait()
+
+swoosh = script.Parent.Swoosh
+swoosh:Play()
+
+position = shaft.Position
+d = t + 10.0 - s
+connection = shaft.Touched:connect(blow)
+
+while t < d do
+ fly()
+ t = r.Stepped:wait()
+end
+
+swoosh:Stop()
+shaft:remove()
diff --git a/Tools/Scripts/RocketLauncher/ServerLauncher.server.lua b/Tools/Scripts/RocketLauncher/ServerLauncher.server.lua
new file mode 100644
index 0000000..9999d8a
--- /dev/null
+++ b/Tools/Scripts/RocketLauncher/ServerLauncher.server.lua
@@ -0,0 +1,93 @@
+local Rocket = Instance.new("Part")
+local Tool = script.Parent
+
+Rocket.Locked = true
+Rocket.BackSurface = 3
+Rocket.BottomSurface = 3
+Rocket.FrontSurface = 3
+Rocket.LeftSurface = 3
+Rocket.RightSurface = 3
+Rocket.TopSurface = 3
+Rocket.Size = Vector3.new(1,1,4)
+Rocket.BrickColor = BrickColor.new(23)
+
+Tool.RocketScript:clone().Parent = Rocket
+Tool.Explosion:clone().Parent = Rocket
+Tool.Swoosh:clone().Parent = Rocket
+
+
+function fire(vTarget)
+
+ local vCharacter = Tool.Parent;
+
+ local vHandle = Tool:findFirstChild("Handle")
+ if vHandle == nil then
+ print("Handle not found")
+ return
+ end
+
+ local dir = vTarget - vHandle.Position
+
+ dir = computeDirection(dir)
+
+ local missile = Rocket:clone()
+
+ local pos = vHandle.Position + (dir * 8)
+
+ --missile.Position = pos
+ missile.CFrame = CFrame.new(pos, pos + dir)
+
+ local creator_tag = Instance.new("ObjectValue")
+
+ local vPlayer = game.Players:playerFromCharacter(vCharacter)
+
+ if vPlayer == nil then
+ print("Player not found")
+ else
+ if (vPlayer.Neutral == false) then -- nice touch
+ missile.BrickColor = vPlayer.TeamColor
+ end
+ end
+
+ creator_tag.Value =vPlayer
+ creator_tag.Name = "creator"
+ creator_tag.Parent = missile
+
+ missile.RocketScript.Disabled = false
+
+ missile.Parent = game.Workspace
+end
+
+function computeDirection(vec)
+ local lenSquared = vec.magnitude * vec.magnitude
+ local invSqrt = 1 / math.sqrt(lenSquared)
+ return Vector3.new(vec.x * invSqrt, vec.y * invSqrt, vec.z * invSqrt)
+end
+
+Tool.Enabled = true
+function onActivated()
+ if not Tool.Enabled then
+ return
+ end
+
+ Tool.Enabled = false
+
+ local character = Tool.Parent;
+ local humanoid = character.Humanoid
+ if humanoid == nil then
+ print("Humanoid not found")
+ return
+ end
+
+ local targetPos = humanoid.TargetPoint
+
+ fire(targetPos)
+
+ wait(3)
+
+ Tool.Enabled = true
+end
+
+
+script.Parent.Activated:connect(onActivated)
+
diff --git a/Tools/Scripts/Slingshot/PelletScript.server.lua b/Tools/Scripts/Slingshot/PelletScript.server.lua
new file mode 100644
index 0000000..0cc2030
--- /dev/null
+++ b/Tools/Scripts/Slingshot/PelletScript.server.lua
@@ -0,0 +1,63 @@
+local debris = game:service("Debris")
+pellet = script.Parent
+damage = 8
+
+local allowTeamDamage = false
+local ServerStorage = game:GetService("ServerStorage")
+if ServerStorage:FindFirstChild("TeamDamage") then
+ allowTeamDamage = ServerStorage.TeamDamage.Value
+end
+
+function tagHumanoid(humanoid)
+ -- todo: make tag expire
+ local tag = pellet:findFirstChild("creator")
+ if tag ~= nil then
+ -- kill all other tags
+ while(humanoid:findFirstChild("creator") ~= nil) do
+ humanoid:findFirstChild("creator").Parent = nil
+ end
+
+ local new_tag = tag:clone()
+ new_tag.Parent = humanoid
+ debris:AddItem(new_tag, 1)
+ end
+end
+
+function onTouched(hit)
+ local hitChar = hit:FindFirstAncestorWhichIsA("Model")
+ local humanoid = hitChar:FindFirstChild("Humanoid")
+ if humanoid ~= nil then
+ local canDamage = true
+ local tag = pellet:FindFirstChild("creator")
+ if tag then
+ local creator = tag.Value
+ local player = game.Players:GetPlayerFromCharacter(hitChar)
+ if creator and player then
+ if creator.Team and player.Team and creator.Team == player.Team then
+ canDamage = allowTeamDamage
+ end
+ end
+ end
+ if canDamage then
+ tagHumanoid(humanoid)
+ humanoid:TakeDamage(damage)
+ end
+ else
+ damage = damage / 2
+ if damage < 1 then
+ connection:disconnect()
+ pellet.Parent = nil
+ end
+ end
+end
+
+connection = pellet.Touched:connect(onTouched)
+
+r = game:service("RunService")
+t, s = r.Stepped:wait()
+d = t + 2.0 - s
+while t < d do
+ t = r.Stepped:wait()
+end
+
+pellet.Parent = nil
diff --git a/Tools/Scripts/Slingshot/Slingshot.server.lua b/Tools/Scripts/Slingshot/Slingshot.server.lua
new file mode 100644
index 0000000..d649ad7
--- /dev/null
+++ b/Tools/Scripts/Slingshot/Slingshot.server.lua
@@ -0,0 +1,138 @@
+Tool = script.Parent
+VELOCITY = 85 -- constant
+
+local Pellet = Instance.new("Part")
+Pellet.Locked = true
+Pellet.BottomSurface = 0
+Pellet.TopSurface = 0
+Pellet.Shape = 0
+Pellet.Size = Vector3.new(1,1,1)
+Pellet.BrickColor = BrickColor.new(2)
+
+Tool.PelletScript:clone().Parent = Pellet
+
+function spawnSound(sound)
+ local s = sound:clone()
+ s.Parent = sound.Parent
+ s:Play()
+ s.Ended:connect(function ()
+ s:Destroy()
+ end)
+end
+
+function fire(mouse_pos)
+
+ spawnSound(Tool.Handle.SlingshotSound)
+
+-- find player's head pos
+
+ local vCharacter = Tool.Parent
+ local vPlayer = game.Players:playerFromCharacter(vCharacter)
+
+ local head = vCharacter:findFirstChild("Head")
+ if head == nil then return end
+
+ local dir = mouse_pos - head.Position
+ dir = computeDirection(dir)
+
+ local launch = head.Position + 5 * dir
+
+ local delta = mouse_pos - launch
+
+ local dy = delta.y
+
+ local new_delta = Vector3.new(delta.x, 0, delta.z)
+ delta = new_delta
+
+ local dx = delta.magnitude
+ local unit_delta = delta.unit
+
+ -- acceleration due to gravity in RBX units
+ local g = (-9.81 * 20)
+
+ local theta = computeLaunchAngle( dx, dy, g)
+
+ local vy = math.sin(theta)
+ local xz = math.cos(theta)
+ local vx = unit_delta.x * xz
+ local vz = unit_delta.z * xz
+
+
+ local missile = Pellet:clone()
+
+
+
+
+ missile.Position = launch
+ missile.Velocity = Vector3.new(vx,vy,vz) * VELOCITY
+
+ missile.PelletScript.Disabled = false
+
+ local creator_tag = Instance.new("ObjectValue")
+ creator_tag.Value = vPlayer
+ creator_tag.Name = "creator"
+ creator_tag.Parent = missile
+
+ missile.Parent = game.Workspace
+ missile:SetNetworkOwner(vPlayer)
+end
+
+
+function computeLaunchAngle(dx,dy,grav)
+ -- arcane
+ -- http://en.wikipedia.org/wiki/Trajectory_of_a_projectile
+
+ local g = math.abs(grav)
+ local inRoot = (VELOCITY*VELOCITY*VELOCITY*VELOCITY) - (g * ((g*dx*dx) + (2*dy*VELOCITY*VELOCITY)))
+ if inRoot <= 0 then
+ return .25 * math.pi
+ end
+ local root = math.sqrt(inRoot)
+ local inATan1 = ((VELOCITY*VELOCITY) + root) / (g*dx)
+
+ local inATan2 = ((VELOCITY*VELOCITY) - root) / (g*dx)
+ local answer1 = math.atan(inATan1)
+ local answer2 = math.atan(inATan2)
+ if answer1 < answer2 then return answer1 end
+ return answer2
+end
+
+function computeDirection(vec)
+ local lenSquared = vec.magnitude * vec.magnitude
+ local invSqrt = 1 / math.sqrt(lenSquared)
+ return Vector3.new(vec.x * invSqrt, vec.y * invSqrt, vec.z * invSqrt)
+end
+
+
+
+
+Tool.Enabled = true
+function onActivated()
+
+ if not Tool.Enabled then
+ return
+ end
+
+ Tool.Enabled = false
+
+ local character = Tool.Parent;
+ local humanoid = character.Humanoid
+ if humanoid == nil then
+ print("Humanoid not found")
+ return
+ end
+
+ if humanoid.Health <= 0 then
+ return
+ end
+
+ local targetPos = humanoid.TargetPoint
+
+ fire(targetPos)
+
+ wait(.2)
+
+ Tool.Enabled = true
+end
+
+script.Parent.Activated:connect(onActivated)
diff --git a/Tools/Scripts/Superball/CannonBall.server.lua b/Tools/Scripts/Superball/CannonBall.server.lua
new file mode 100644
index 0000000..af2d435
--- /dev/null
+++ b/Tools/Scripts/Superball/CannonBall.server.lua
@@ -0,0 +1,79 @@
+local Ball = script.Parent
+local damage = 35
+
+local r = game:service("RunService")
+local debris = game:GetService("Debris")
+
+local last_sound_time = r.Stepped:wait()
+
+local allowTeamDamage = false
+local ServerStorage = game:GetService("ServerStorage")
+if ServerStorage:FindFirstChild("TeamDamage") then
+ allowTeamDamage = ServerStorage.TeamDamage.Value
+end
+
+function onTouched(hit)
+ local hitChar = hit:FindFirstAncestorWhichIsA("Model")
+ local humanoid = hitChar:FindFirstChild("Humanoid")
+ if humanoid ~=nil then
+ local canDamage = true
+ local tag = Ball:FindFirstChild("creator")
+ if tag then
+ local creator = tag.Value
+ local player = game.Players:GetPlayerFromCharacter(hitChar)
+ if creator and player then
+ if creator.Team and player.Team and creator.Team == player.Team then
+ canDamage = allowTeamDamage
+ end
+ if creator == player then
+ canDamage = true
+ end
+ end
+ end
+
+ if canDamage then
+ if connection then
+ connection:disconnect()
+ end
+ Ball.Boing:play()
+ tagHumanoid(humanoid)
+ humanoid:TakeDamage(damage)
+ if humanoid.RootPart then
+ local apply = (Ball.Position - humanoid.RootPart.Position).unit * (Ball.Velocity/4)
+ humanoid.RootPart.Velocity = humanoid.RootPart.Velocity + apply
+ humanoid.RootPart.RotVelocity = humanoid.RootPart.RotVelocity + apply
+ end
+ humanoid.PlatformStand = true
+ wait(.1)
+ humanoid.PlatformStand = false
+ end
+ else
+ local now = tick()
+ if (now - last_sound_time > .1) then
+ Ball.Boing:play()
+ last_sound_time = now
+ damage = damage / 2
+ if damage < 2 then
+ if connection then
+ connection:disconnect()
+ end
+ end
+ end
+ end
+end
+
+function tagHumanoid(humanoid)
+ local tag = Ball:findFirstChild("creator")
+ if tag ~= nil then
+ local new_tag = tag:clone()
+ new_tag.Parent = humanoid
+ debris:AddItem(new_tag, 4)
+ end
+end
+
+
+connection = Ball.Touched:connect(onTouched)
+
+wait(5)
+
+Ball:Destroy()
\ No newline at end of file
diff --git a/Tools/Scripts/Superball/CannonScript.server.lua b/Tools/Scripts/Superball/CannonScript.server.lua
new file mode 100644
index 0000000..fe44f0a
--- /dev/null
+++ b/Tools/Scripts/Superball/CannonScript.server.lua
@@ -0,0 +1,65 @@
+local Tool = script.Parent
+local Ball = Tool.Handle
+
+function fire(direction)
+
+ Tool.Handle.Boing:Play()
+
+ local vCharacter = Tool.Parent
+ local vPlayer = game.Players:playerFromCharacter(vCharacter)
+
+ local missile = Instance.new("Part")
+
+ local spawnPos = vCharacter.PrimaryPart.Position
+
+ spawnPos = spawnPos + (direction * 5)
+
+ missile.Position = spawnPos
+ missile.Size = Vector3.new(2,2,2)
+ missile.Velocity = direction * 200
+ missile.BrickColor = BrickColor.random()
+ missile.Shape = 0
+ missile.BottomSurface = 0
+ missile.TopSurface = 0
+ missile.Name = "Cannon Shot"
+ missile.Reflectance = .2
+ missile.CustomPhysicalProperties = PhysicalProperties.new(1,0,1)
+ Tool.Handle.Boing:clone().Parent = missile
+
+ local new_script = script.Parent.CannonBall:clone()
+ new_script.Disabled = false
+ new_script.Parent = missile
+
+ local creator_tag = Instance.new("ObjectValue")
+ creator_tag.Value = vPlayer
+ creator_tag.Name = "creator"
+ creator_tag.Parent = missile
+
+ missile.Parent = game.Workspace
+ missile:SetNetworkOwner(nil)
+end
+
+
+
+Tool.Enabled = true
+function onActivated()
+ if not Tool.Enabled then
+ return
+ end
+ Tool.Enabled = false
+ local character = Tool.Parent;
+ local humanoid = character.Humanoid
+ if humanoid == nil then
+ print("Humanoid not found")
+ return
+ end
+ local targetPos = humanoid.TargetPoint
+ local lookAt = (targetPos - character.Head.Position).unit
+ fire(lookAt)
+ wait(2)
+ Tool.Enabled = true
+end
+
+
+Tool.Activated:connect(onActivated)
+
diff --git a/Tools/Scripts/Sword/SwordScript.server.lua b/Tools/Scripts/Sword/SwordScript.server.lua
new file mode 100644
index 0000000..d14b07f
--- /dev/null
+++ b/Tools/Scripts/Sword/SwordScript.server.lua
@@ -0,0 +1,181 @@
+-------- OMG HAX
+
+r = game:service("RunService")
+
+
+local damage = 5
+
+
+local slash_damage = 10
+local lunge_damage = 30
+
+local Debris = game:GetService("Debris")
+
+sword = script.Parent.Handle
+Tool = script.Parent
+
+
+local SlashSound = Instance.new("Sound")
+SlashSound.SoundId = "rbxasset://sounds\\swordslash.wav"
+SlashSound.Parent = sword
+SlashSound.Volume = .7
+
+local LungeSound = Instance.new("Sound")
+LungeSound.SoundId = "rbxasset://sounds\\swordlunge.wav"
+LungeSound.Parent = sword
+LungeSound.Volume = .6
+
+local UnsheathSound = Instance.new("Sound")
+UnsheathSound.SoundId = "rbxasset://sounds\\unsheath.wav"
+UnsheathSound.Parent = sword
+UnsheathSound.Volume = 1
+
+
+function tagHumanoid(humanoid, player)
+ local creator_tag = Instance.new("ObjectValue")
+ creator_tag.Value = player
+ creator_tag.Name = "creator"
+ creator_tag.Parent = humanoid
+ Debris:AddItem(creator_tag,1)
+end
+
+function spawnSound(sound)
+ local s = sound:clone()
+ s.Parent = sound.Parent
+ s:Play()
+ s.Ended:connect(function ()
+ s:Destroy()
+ end)
+end
+
+function blow(hit)
+ if (hit.Parent == nil) then return end -- happens when bullet hits sword
+
+ local humanoid = hit.Parent:findFirstChild("Humanoid")
+ local vCharacter = Tool.Parent
+ local vPlayer = game.Players:playerFromCharacter(vCharacter)
+ local hum = vCharacter:findFirstChild("Humanoid") -- non-nil if tool held by a character
+ if humanoid~=nil and humanoid ~= hum and hum ~= nil then
+ -- final check, make sure sword is in-hand
+
+ local right_arm = vCharacter:FindFirstChild("Right Arm")
+ if (right_arm ~= nil) then
+ local joint = right_arm:FindFirstChild("RightGrip")
+ if (joint ~= nil and (joint.Part0 == sword or joint.Part1 == sword)) then
+ local canDamage = true
+ local victim = game.Players:GetPlayerFromCharacter(humanoid.Parent)
+ if victim then
+ if vPlayer.Team and victim.Team and vPlayer.Team == victim.Team then
+ canDamage = false
+ end
+ end
+ if canDamage then
+ local damageDone = damage
+ local amplify = vCharacter:FindFirstChild("DamageAmplifier")
+ if amplify then
+ damageDone = damageDone * amplify.Value
+ end
+ tagHumanoid(humanoid, vPlayer)
+ humanoid:TakeDamage(damageDone)
+ end
+ end
+ end
+
+
+ end
+end
+
+function attack()
+ damage = slash_damage
+ spawnSound(SlashSound)
+ local anim = Instance.new("StringValue")
+ anim.Name = "toolanim"
+ anim.Value = "Slash"
+ anim.Parent = Tool
+end
+
+function lunge()
+ damage = lunge_damage
+
+ spawnSound(LungeSound)
+
+ local anim = Instance.new("StringValue")
+ anim.Name = "toolanim"
+ anim.Value = "Lunge"
+ anim.Parent = Tool
+
+
+ local force = Instance.new("BodyVelocity")
+ force.Velocity = Vector3.new(0,3,0) --Tool.Parent.Torso.CFrame.lookVector * 80
+ force.MaxForce = Vector3.new(0,4000,0)
+ force.Parent = Tool.Parent.Torso
+ wait(.25)
+ swordOut()
+ wait(.25)
+ force.Parent = nil
+ wait(.5)
+ swordUp()
+
+ damage = slash_damage
+end
+
+function swordUp()
+ Tool.GripForward = Vector3.new(-1,0,0)
+ Tool.GripRight = Vector3.new(0,1,0)
+ Tool.GripUp = Vector3.new(0,0,1)
+end
+
+function swordOut()
+ Tool.GripForward = Vector3.new(0,0,1)
+ Tool.GripRight = Vector3.new(0,-1,0)
+ Tool.GripUp = Vector3.new(-1,0,0)
+end
+
+function swordAcross()
+ -- parry
+end
+
+
+Tool.Enabled = true
+local last_attack = 0
+function onActivated()
+
+ if not Tool.Enabled then
+ return
+ end
+
+ Tool.Enabled = false
+
+ local character = Tool.Parent;
+ local humanoid = character.Humanoid
+ if humanoid == nil then
+ print("Humanoid not found")
+ return
+ end
+
+ local t = tick()
+
+ if (t - last_attack < .2) then
+ lunge()
+ else
+ attack()
+ end
+
+ last_attack = t
+
+ --wait(.5)
+
+ Tool.Enabled = true
+end
+
+
+function onEquipped()
+ UnsheathSound:play()
+end
+
+
+script.Parent.Activated:connect(onActivated)
+script.Parent.Equipped:connect(onEquipped)
+
+
+connection = sword.Touched:connect(blow)
\ No newline at end of file
diff --git a/Tools/Scripts/Timebomb/Bomb.server.lua b/Tools/Scripts/Timebomb/Bomb.server.lua
new file mode 100644
index 0000000..3589dfb
--- /dev/null
+++ b/Tools/Scripts/Timebomb/Bomb.server.lua
@@ -0,0 +1,83 @@
+local updateInterval = .4
+
+local currentColor = 1
+local colors = {26, 21}
+
+local bomb = script.Parent
+local debris = game:GetService("Debris")
+
+local ticksound = Instance.new("Sound")
+ticksound.SoundId = "rbxasset://sounds\\clickfast.wav"
+ticksound.Parent = bomb
+
+local function update()
+ updateInterval = updateInterval * .9
+ script.Parent.BrickColor = BrickColor.new(colors[currentColor])
+ currentColor = currentColor + 1
+ if (currentColor > 2) then currentColor = 1 end
+end
+
+local function spawnSound(sound)
+ local s = sound:clone()
+ s.Parent = sound.Parent
+ s:Play()
+ s.Ended:connect(function ()
+ s:Destroy()
+ end)
+end
+
+function tagHumanoid(humanoid)
+ -- todo: make tag expire
+ local tag = bomb:findFirstChild("creator")
+ if tag ~= nil then
+ -- kill all other tags
+ while(humanoid:findFirstChild("creator") ~= nil) do
+ humanoid:findFirstChild("creator").Parent = nil
+ end
+
+ local new_tag = tag:clone()
+ new_tag.Parent = humanoid
+ debris:AddItem(new_tag, 1)
+ end
+end
+
+local function onExplosionHit(hit)
+ local char = hit:FindFirstAncestorWhichIsA("Model")
+ if char then
+ local humanoid = char:FindFirstChild("Humanoid")
+ if humanoid then
+ tagHumanoid(humanoid)
+ end
+ end
+end
+
+while updateInterval > .1 do
+ wait(updateInterval)
+ update()
+ spawnSound(ticksound)
+end
+
+local sound = Instance.new("Sound")
+sound.SoundId = "rbxasset://sounds\\Rocket shot.wav"
+sound.Parent = script.Parent
+sound.Volume = 1
+sound:Play()
+
+local explosion = Instance.new("Explosion")
+explosion.BlastRadius = 12
+explosion.BlastPressure = 1000000 -- these are really wussy units
+explosion.Hit:Connect(onExplosionHit)
+
+local creator = bomb:FindFirstChild("creator")
+if creator then
+ creator:Clone().Parent = explosion
+end
+
+explosion.Position = bomb.Position
+explosion.Parent = workspace
+
+bomb.Transparency = 1
+bomb.Anchored = true
+bomb.CanCollide = false
+sound.Ended:wait()
+bomb:Destroy()
\ No newline at end of file
diff --git a/Tools/Scripts/Timebomb/PlantBomb.server.lua b/Tools/Scripts/Timebomb/PlantBomb.server.lua
new file mode 100644
index 0000000..485df6d
--- /dev/null
+++ b/Tools/Scripts/Timebomb/PlantBomb.server.lua
@@ -0,0 +1,69 @@
+local bombScript = script.Parent.Bomb
+local Tool = script.Parent
+local Bomb = Tool.Handle
+
+function plant()
+ local bomb2 = Instance.new("Part")
+
+ local vCharacter = Tool.Parent
+ local vPlayer = game.Players:playerFromCharacter(vCharacter)
+
+ local spawnPos = Bomb.Position
+
+ bomb2.Position = Vector3.new(spawnPos.x, spawnPos.y+3, spawnPos.z)
+ bomb2.Size = Vector3.new(2,2,2)
+
+ bomb2.BrickColor = BrickColor.new(21)
+ bomb2.Shape = 0
+ bomb2.BottomSurface = 0
+ bomb2.TopSurface = 0
+ bomb2.Reflectance = 0.2
+ bomb2.Name = "TimeBomb"
+ bomb2.Locked = true
+
+ local creator_tag = Instance.new("ObjectValue")
+ creator_tag.Value = vPlayer
+ creator_tag.Name = "creator"
+ creator_tag.Parent = bomb2
+
+ bomb2.Parent = game.Workspace
+ bomb2:SetNetworkOwner(vPlayer)
+ local new_script = bombScript:clone()
+ new_script.Disabled = false
+ new_script.Parent = bomb2
+end
+
+
+Tool.Enabled = true
+function onActivated()
+
+ if not Tool.Enabled then
+ return
+ end
+
+ Tool.Enabled = false
+
+ local character = Tool.Parent;
+ local humanoid = character.Humanoid
+ if humanoid == nil then
+ print("Humanoid not found")
+ return
+ end
+
+ local targetPos = humanoid.TargetPoint
+ Bomb.Transparency = 1.0
+
+ plant()
+
+ wait(6)
+ Bomb.Transparency = 0.0
+
+ Tool.Enabled = true
+end
+
+function onUnequipped()
+end
+
+
+Tool.Activated:connect(onActivated)
+Tool.Unequipped:connect(onUnequipped)
\ No newline at end of file
diff --git a/Tools/Scripts/Trowel/BrickCleanup.server.lua b/Tools/Scripts/Trowel/BrickCleanup.server.lua
new file mode 100644
index 0000000..5f2dd3e
--- /dev/null
+++ b/Tools/Scripts/Trowel/BrickCleanup.server.lua
@@ -0,0 +1,4 @@
+-- this script removes its parent from the workspace after 24 seconds
+
+wait(24)
+script.Parent.Parent = nil
diff --git a/Tools/Scripts/Trowel/WallMaker.server.lua b/Tools/Scripts/Trowel/WallMaker.server.lua
new file mode 100644
index 0000000..e6fc5bc
--- /dev/null
+++ b/Tools/Scripts/Trowel/WallMaker.server.lua
@@ -0,0 +1,96 @@
+local wallHeight = 4
+local brickSpeed = 0.04
+local wallWidth = 12
+
+local Tool = script.Parent
+
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local brickColors = require(ReplicatedStorage:WaitForChild("BrickColors"))
+
+
+-- places a brick at pos and returns the position of the brick's opposite corner
+function placeBrick(cf, pos, color)
+ local brick = Instance.new("Part")
+ brick.BrickColor = color
+ brick.CFrame = cf * CFrame.new(pos + brick.Size / 2)
+ script.Parent.BrickCleanup:Clone().Parent = brick -- attach cleanup script to this brick
+ brick.BrickCleanup.Disabled = false
+ brick.Parent = game.Workspace
+ return brick, pos + brick.Size
+end
+
+function buildWall(cf)
+
+ local color = BrickColor.new(brickColors[math.random(1,#brickColors)])
+ local bricks = {}
+
+ assert(wallWidth>0)
+ local y = 0
+ while y < wallHeight do
+ local p
+ local x = -wallWidth/2
+ while x < wallWidth/2 do
+ local brick
+ brick, p = placeBrick(cf, Vector3.new(x, y, 0), color)
+ x = p.x
+ table.insert(bricks, brick)
+ brick:MakeJoints()
+ wait(brickSpeed)
+ end
+ y = p.y
+ end
+
+ --workspace:UnjoinFromOutsiders(bricks)
+ return bricks
+
+end
+
+
+function snap(v)
+ if math.abs(v.x)>math.abs(v.z) then
+ if v.x>0 then
+ return Vector3.new(1,0,0)
+ else
+ return Vector3.new(-1,0,0)
+ end
+ else
+ if v.z>0 then
+ return Vector3.new(0,0,1)
+ else
+ return Vector3.new(0,0,-1)
+ end
+ end
+end
+
+
+Tool.Enabled = true
+function onActivated()
+
+ if not Tool.Enabled then
+ return
+ end
+
+ Tool.Enabled = false
+
+ local character = Tool.Parent;
+ local humanoid = character.Humanoid
+ if humanoid == nil then
+ print("Humanoid not found")
+ return
+ end
+
+ local targetPos = humanoid.TargetPoint
+ local lookAt = snap( (targetPos - character.Head.Position).unit )
+ local cf = CFrame.new(targetPos, targetPos + lookAt)
+
+ Tool.Handle.BuildSound:play()
+
+ buildWall(cf)
+
+ wait(5)
+
+ Tool.Enabled = true
+end
+
+script.Parent.Activated:connect(onActivated)
+
diff --git a/UI/Backpack/SlotTemp.rbxmx b/UI/Backpack/SlotTemp.rbxmx
new file mode 100644
index 0000000..ab23d35
--- /dev/null
+++ b/UI/Backpack/SlotTemp.rbxmx
@@ -0,0 +1,650 @@
+
+ -
+
+ true
+
+ 0
+ 0
+
+
+ false
+ true
+
+ 0.7058824
+ 0.7058824
+ 0.7058824
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 3
+ 0
+ 1
+ false
+ SlotTemp
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ false
+ 14
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ false
+ 2
+ 1
+ false
+ 1
+ true
+
+ -
+
+ true
+
+ 0
+ 1
+
+
+ true
+
+ 0.6666667
+ 0.6666667
+ 0.6666667
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ Index
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.03
+ 2
+ 0.97
+ -2
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.2
+ 0
+ 0.2
+ 0
+
+ 2
+
+ 1
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 1
+ 1
+ 1
+
+ 0.8
+ 0
+ 0
+ true
+ 2
+ 1
+ true
+ 3
+ true
+
+ -
+
+
+ 20
+ 1
+ UITextSizeConstraint
+
+ true
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 1
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ SelectionOutline
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ -1
+ 0
+ -1
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 2
+ 1
+ 2
+
+ 0
+ 0
+
+ false
+ 1
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 1
+ 0
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ Outline
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0.97
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.03
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 1
+ 0
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ Outline
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.97
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.03
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 1
+ 0
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ Outline
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.03
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 1
+ 0
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ Outline
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.03
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+ true
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.7058824
+ 0.7058824
+ 0.7058824
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ TextHover
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ false
+ 3
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+
+
+
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ ToolIcon
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.15
+ 0
+ 0.15
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 0.7
+ 0
+ 0.7
+ 0
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 4
+ true
+
+
+ -
+
+ true
+
+ 0
+ 0.5
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ ToolName
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.1
+ 0
+ 0.475
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 2
+ 0
+ 0.17
+ 0
+
+ 0
+
+ Tool
+
+ 1
+ 1
+ 1
+
+ true
+ 100
+
+ 0.4980392
+ 0.4980392
+ 0.4980392
+
+ 0.5
+ 0.2
+ 0
+ true
+ 0
+ 1
+ true
+ 4
+ true
+
+
+
+
\ No newline at end of file
diff --git a/UI/Backpack/init.client.lua b/UI/Backpack/init.client.lua
new file mode 100644
index 0000000..6be5ed8
--- /dev/null
+++ b/UI/Backpack/init.client.lua
@@ -0,0 +1,300 @@
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- @CloneTrooper1019, 2015
+-- Backpack
+-- Simulates the 2008 backpack from scratch.
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Setup
+
+local ui = script.Parent
+local rootFrame = ui:WaitForChild("RootFrame")
+
+local self = rootFrame:WaitForChild("Backpack")
+local slotTemp = script:WaitForChild("SlotTemp")
+
+local backdrop = self:WaitForChild("Backdrop")
+local slotsBin = self:WaitForChild("Slots")
+
+local player = game.Players.LocalPlayer
+local UserInputService = game:GetService("UserInputService")
+
+local toolIndex = 0
+
+local tools = {}
+local slots = {}
+local tokens =
+{
+ One = 1;
+ Two = 2;
+ Three = 3;
+ Four = 4;
+ Five = 5;
+ Six = 6;
+ Seven = 7;
+ Eight = 8;
+ Nine = 9;
+ Zero = 10; -- shhh not a hack
+}
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Key Hookup
+
+local eNumPress = Instance.new("BindableEvent")
+local numPress = eNumPress.Event
+
+-- Hack to work around the inputs being overridden while the Plane tool is active.
+local function allowGameProcessedBypassHack()
+ local lastInputType = UserInputService:GetLastInputType()
+ if lastInputType.Name == "Gamepad1" then
+ local char = player.Character
+ if char then
+ local tool = char:FindFirstChildWhichIsA("Tool")
+ if tool and not tool.Enabled then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+local function onInputBegan(input,gameProcessed)
+ if not gameProcessed or allowGameProcessedBypassHack() then
+ local name = input.UserInputType.Name
+ local keyCode = input.KeyCode.Name
+ if name == "Keyboard" then
+ local toIndex = tokens[keyCode]
+ if toIndex then
+ eNumPress:Fire(toIndex)
+ end
+ elseif name == "Gamepad1" then
+ if keyCode == "ButtonL1" or keyCode == "ButtonR1" then
+ local nextIndex = toolIndex
+ if keyCode == "ButtonL1" then
+ nextIndex = nextIndex - 1
+ elseif keyCode == "ButtonR1" then
+ nextIndex = nextIndex + 1
+ end
+ print(nextIndex,#tools)
+ if nextIndex > 0 and nextIndex <= #tools then
+ eNumPress:Fire(nextIndex)
+ else
+ eNumPress:Fire(toolIndex)
+ end
+ end
+ end
+ end
+end
+
+UserInputService.InputBegan:connect(onInputBegan)
+
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local function resortSlots()
+ for index,tool in ipairs(tools) do
+ local slot = slots[tool]
+ slot.Index.Text = index
+ slot.LayoutOrder = index
+ slot.Visible = true
+ end
+ backdrop.Size = UDim2.new(#tools,0,1,0)
+end
+
+local function createSlot(tool)
+ if not slots[tool] then
+ local index = #tools+1
+ tools[index] = tool
+
+ local slot = slotTemp:clone()
+ slot.Name = tool.Name
+ slot.Parent = slotsBin
+
+ local textHover = slot:WaitForChild("TextHover")
+ local selectionOutline = slot:WaitForChild("SelectionOutline")
+ local toolIcon = slot:WaitForChild("ToolIcon")
+ local indexLbl = slot:WaitForChild("Index")
+ local toolName = slot:WaitForChild("ToolName")
+
+ local isHovering = false
+ local isDown = false
+
+ local backpack = player:WaitForChild("Backpack")
+ local char = player.Character or player.CharacterAdded:Wait()
+
+ local humanoid = char:WaitForChild("Humanoid")
+ local conReg = {}
+
+ local function killTool()
+ local currentIndex = tonumber(indexLbl.Text)
+ table.remove(tools, currentIndex)
+
+ for _,con in pairs(conReg) do
+ con:disconnect()
+ end
+
+ slots[tool] = nil
+ slot:Destroy()
+
+ resortSlots()
+ end
+
+ local function checkParent()
+ if tool.Parent == char then
+ selectionOutline.Visible = true
+ elseif tool.Parent == backpack then
+ selectionOutline.Visible = false
+ else
+ killTool()
+ end
+ end
+
+ local function toggleTool()
+ if tool.Parent == char then
+ humanoid:UnequipTools()
+ else
+ toolIndex = tonumber(indexLbl.Text)
+ humanoid:EquipTool(tool)
+ end
+ end
+
+ local function renderUpdate()
+ if tool.TextureId ~= "" then
+ toolName.Visible = false
+ toolIcon.Visible = true
+ toolIcon.Image = tool.TextureId
+ else
+ toolIcon.Visible = false
+ toolName.Visible = true
+ toolName.Text = tool.Name
+ end
+ if tool.TextureId ~= "" then
+ textHover.Visible = false
+ if isHovering then
+ toolIcon.BackgroundTransparency = 0
+ if isDown then
+ toolIcon.BackgroundColor3 = Color3.new(0,0,1)
+ else
+ toolIcon.BackgroundColor3 = Color3.new(1,1,0)
+ end
+ else
+ toolIcon.BackgroundTransparency = 1
+ end
+ else
+ textHover.Visible = true
+ if isHovering then
+ textHover.BackgroundTransparency = 0
+ if isDown then
+ textHover.BackgroundColor3 = Color3.new(1,1,0)
+ else
+ textHover.BackgroundColor3 = Color3.new(0.706,0.706,0.706)
+ end
+ else
+ textHover.BackgroundTransparency = 1
+ end
+ end
+ end
+
+ local function onInputBegan(input)
+ if input.UserInputType.Name == "MouseButton1" then
+ isDown = true
+ elseif input.UserInputType.Name == "MouseMovement" or input.UserInputType.Name == "Touch" then
+ isHovering = true
+ end
+ renderUpdate()
+ end
+
+ local function onInputEnded(input)
+ if input.UserInputType.Name == "MouseButton1" then
+ isDown = false
+ if isHovering then
+ toggleTool()
+ end
+ elseif input.UserInputType.Name == "MouseMovement" then
+ isHovering = false
+ elseif input.UserInputType.Name == "Touch" then
+ isHovering = false
+ if humanoid.MoveDirection == Vector3.new() then
+ toggleTool()
+ end
+ end
+
+ renderUpdate()
+ end
+
+ local function onNumDown(num)
+ local currentIndex = tonumber(indexLbl.Text)
+
+ if num == currentIndex then
+ toggleTool()
+ end
+ end
+
+ local function onToolChanged(property)
+ if property == "TextureId" or property == "Name" then
+ renderUpdate()
+ elseif property == "Parent" then
+ checkParent()
+ end
+ end
+
+ local eventMounts =
+ {
+ [numPress] = onNumDown;
+ [tool.Changed] = onToolChanged;
+ [slot.InputBegan] = onInputBegan;
+ [slot.InputEnded] = onInputEnded;
+ [humanoid.Died] = killTool;
+ }
+
+ renderUpdate()
+ checkParent()
+
+ for event, func in pairs(eventMounts) do
+ local connection = event:Connect(func)
+ table.insert(conReg, connection)
+ end
+
+ slots[tool] = slot
+ resortSlots()
+ end
+end
+
+local currentChar
+
+local function onCharacterAdded(char)
+ if currentChar ~= char then
+ currentChar = char
+
+ for _,v in pairs(slots) do
+ v:Destroy()
+ end
+
+ slots = {}
+ tools = {}
+
+ local function onChildAdded(child)
+ if child:IsA("Tool") then
+ createSlot(child)
+ end
+ end
+
+ local backpack = player:WaitForChild("Backpack")
+
+ for _,v in pairs(backpack:GetChildren()) do
+ onChildAdded(v)
+ end
+
+ for _,v in pairs(char:GetChildren()) do
+ onChildAdded(v)
+ end
+
+ char.ChildAdded:connect(onChildAdded)
+ backpack.ChildAdded:connect(onChildAdded)
+ end
+end
+
+if player.Character then
+ onCharacterAdded(player.Character)
+end
+
+player.CharacterAdded:connect(onCharacterAdded)
+
+game.StarterGui.ResetPlayerGuiOnSpawn = false
\ No newline at end of file
diff --git a/UI/Chat/GetCoreGateway.lua b/UI/Chat/GetCoreGateway.lua
new file mode 100644
index 0000000..96af2a6
--- /dev/null
+++ b/UI/Chat/GetCoreGateway.lua
@@ -0,0 +1,96 @@
+local ChatConnections = {}
+
+local function AddObjects(bindableClass,targetName,...)
+ local target = ChatConnections[targetName]
+ if not target then
+ target = {}
+ ChatConnections[targetName] = target
+ end
+ local names = {...}
+ for _,name in pairs(names) do
+ local signal = Instance.new(bindableClass)
+ signal.Name = targetName .. "_" .. name
+ signal.Parent = script
+ target[name] = signal
+ end
+end
+
+AddObjects("BindableEvent","ChatWindow",
+ ---------------------------
+ -- Fired from the CoreGui
+ ---------------------------
+ "ToggleVisibility", -- Fired when the CoreGui chat button is pressed.
+ "SetVisible", -- Fired when the CoreGui wants to directly change the visiblity state of the chat window.
+ "FocusChatBar", -- Fired when the CoreGui wants to capture the Chatbar's Focus.
+ "TopbarEnabledChanged", -- Fired when the visibility of the Topbar is changed.
+ "SpecialKeyPressed", -- Fired when the reserved ChatHotkey is pressed.
+ "CoreGuiEnabled", -- Fired when a user changes the SetCoreGuiEnabled state of the Chat Gui.
+
+ ---------------------------
+ -- Fired to the CoreGui
+ ---------------------------
+ "ChatBarFocusChanged",
+ -- ^ Fire this with 'true' when you want to assure the CoreGui that the ChatBar is being focused on.
+
+ "VisibilityStateChanged",
+ -- ^ Fire this with 'true' when the user shows or hides the chat.
+
+ "MessagesChanged",
+ -- ^ Fire this with a number to change the number of messages that have been recorded by the chat window.
+ -- If the CoreGui thinks the chat window isn't visible, it will display the recorded difference between
+ -- the number of messages that was displayed when it was visible, and the number you supply.
+
+ "MessagePosted"
+ -- ^ Fire this to make the player directly chat under ROBLOX's C++ API.
+ -- This will fire the LocalPlayer's Chatted event.
+ -- Please only fire this on the player's behalf. If you attempt to spoof a player's chat
+ -- to get them in trouble, you could face serious moderation action.
+)
+
+AddObjects("BindableFunction","ChatWindow",
+ "IsFocused" -- This will be invoked by the CoreGui when it wants to check if the chat window is active.
+)
+
+-- The following events are fired if the user calls StarterGui:SetCore(string name, Variant data)
+-- Note that you can only hook onto these ones specifically.
+AddObjects("BindableEvent","SetCore",
+ "ChatMakeSystemMessage",
+ "ChatWindowPosition",
+ "ChatWindowSize",
+ "ChatBarDisabled"
+)
+
+-- The following functions are invoked if the user calls StarterGui:GetCore(string name)
+-- Note that you can only hook onto these ones specifically.
+AddObjects("BindableFunction","GetCore",
+ "ChatWindowPosition", -- Should return a UDim2 representing the position of the chat window.
+ "ChatWindowSize", -- Should return a UDim2 representing the size of the chat window.
+ "ChatBarDisabled" -- Should return true if the chat bar is currently disabled.
+)
+
+-- Connect ChatConnections to the CoreGui.
+local StarterGui = game:GetService("StarterGui")
+local GuiService = game:GetService("GuiService")
+
+if not GuiService:IsTenFootInterface() then
+ local tries = 0
+ local maxAttempts = 30
+
+ while (tries < maxAttempts) do
+ local success,result = pcall(function ()
+ StarterGui:SetCore("CoreGuiChatConnections", ChatConnections)
+ end)
+ if success then
+ break
+ else
+ tries = tries + 1
+ if tries == maxAttempts then
+ error("Error calling SetCore CoreGuiChatConnections: " .. result)
+ else
+ wait()
+ end
+ end
+ end
+end
+
+return ChatConnections
\ No newline at end of file
diff --git a/UI/Chat/LinkedList.lua b/UI/Chat/LinkedList.lua
new file mode 100644
index 0000000..26faa79
--- /dev/null
+++ b/UI/Chat/LinkedList.lua
@@ -0,0 +1,84 @@
+local LinkedList = {}
+LinkedList.__index = LinkedList
+
+function LinkedList:Add(data)
+ local node = {}
+ node.data = data
+ node.id = tostring(node):sub(8)
+
+ local back = self.back
+ if back then
+ back.next = node
+ node.prev = back
+ end
+
+ if not self.front then
+ self.front = node
+ end
+
+ self.back = node
+ self.size = self.size + 1
+
+ self.nodes[node.id] = node
+ self.lookup[data] = node
+
+ return node.id
+end
+
+function LinkedList:Get(id)
+ local node = self.nodes[id]
+ if node then
+ return node.data
+ end
+end
+
+function LinkedList:Remove(id)
+ local node = self.nodes[id]
+
+ if node then
+ if node.prev then
+ node.prev.next = node.next
+ end
+
+ if node.next then
+ node.next.prev = node.prev
+ end
+
+ if node == self.front then
+ self.front = node.next
+ end
+
+ if node == self.back then
+ self.back = node.prev
+ end
+
+ if node.data then
+ node.data = nil
+ end
+
+ self.size = self.size - 1
+ end
+end
+
+function LinkedList:GetEnumerator()
+ return coroutine.wrap(function ()
+ local node = self.front
+ while node ~= nil do
+ coroutine.yield(node.id, node.data)
+ node = node.next
+ end
+ end)
+end
+
+function LinkedList.new()
+ local list =
+ {
+ nodes = {};
+ lookup = {};
+ size = 0;
+ }
+
+ return setmetatable(list, LinkedList)
+end
+
+return LinkedList
\ No newline at end of file
diff --git a/UI/Chat/MessageTemplate.rbxmx b/UI/Chat/MessageTemplate.rbxmx
new file mode 100644
index 0000000..92651ad
--- /dev/null
+++ b/UI/Chat/MessageTemplate.rbxmx
@@ -0,0 +1,220 @@
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.6
+ 0.6
+ 0.6
+
+ 0.6
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ MessageTemplate
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 0
+ 16
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 1
+ 1
+ Message
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ 0
+
+
+
+ 1
+ 1
+ 1
+
+ false
+ 16
+
+ 1
+ 1
+ 1
+
+ 0.9
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 1
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ PlayerName
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+ false
+ 16
+
+ 0
+ 0
+ 0
+
+ 0.9
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 1
+ true
+
+
+ -
+
+
+ 0
+ 1
+ AutoLayout
+
+
0
+ 0
+
+ 2
+
+ 1
+ true
+
+
+
+
\ No newline at end of file
diff --git a/UI/Chat/init.client.lua b/UI/Chat/init.client.lua
new file mode 100644
index 0000000..422089b
--- /dev/null
+++ b/UI/Chat/init.client.lua
@@ -0,0 +1,181 @@
+--------------------------------------------------------------------------------------------
+-- Constants
+--------------------------------------------------------------------------------------------
+
+local Debris = game:GetService("Debris")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local RunService = game:GetService("RunService")
+local TextService = game:GetService("TextService")
+local UserInputService = game:GetService("UserInputService")
+
+local LinkedList = require(script:WaitForChild("LinkedList"))
+
+local ui = script.Parent
+local rootFrame = ui:WaitForChild("RootFrame")
+
+local chat = rootFrame:WaitForChild("Chat")
+local chatBar = chat:WaitForChild("ChatBar")
+local chatOutput = chat:WaitForChild("ChatOutput")
+local chatRemote = ReplicatedStorage:WaitForChild("ChatRemote")
+
+local focusBackdrop = chatBar:WaitForChild("FocusBackdrop")
+local mainBackdrop = chat:WaitForChild("MainBackdrop")
+local messageTemplate = script:WaitForChild("MessageTemplate")
+
+local hasCoreGateway, coreGateway = pcall(function ()
+ local getCoreGateway = script:WaitForChild("GetCoreGateway")
+ return require(getCoreGateway)
+end)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Player Colors
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local PLAYER_COLORS =
+{
+ [0] = Color3.fromRGB(173, 35, 35); -- red
+ [1] = Color3.fromRGB( 42, 75, 215); -- blue
+ [2] = Color3.fromRGB( 29, 105, 20); -- green
+ [3] = Color3.fromRGB(129, 38, 192); -- purple
+ [4] = Color3.fromRGB(255, 146, 51); -- orange
+ [5] = Color3.fromRGB(255, 238, 51); -- yellow
+ [6] = Color3.fromRGB(255, 205, 243); -- pink
+ [7] = Color3.fromRGB(233, 222, 187); -- tan
+}
+
+local function computePlayerColor(player)
+ if player.Team then
+ return player.TeamColor.Color
+ else
+ local pName = player.Name
+ local length = #pName
+
+ local oddShift = (1 - (length % 2))
+ local value = 0
+
+ for i = 1,length do
+ local char = pName:sub(i, i):byte()
+ local rev = (length - i) + oddShift
+
+ if (rev % 4) >= 2 then
+ value = value - char
+ else
+ value = value + char
+ end
+ end
+
+ return PLAYER_COLORS[value % 8]
+ end
+end
+
+--------------------------------------------------------------------------------------------
+-- Chat Input
+--------------------------------------------------------------------------------------------
+
+local function beginChatting()
+ focusBackdrop.Visible = true
+
+ if not chatBar:IsFocused() then
+ chatBar.TextTransparency = 1
+ chatBar:CaptureFocus()
+ wait()
+ chatBar.Text = ""
+ chatBar.TextTransparency = 0
+ end
+end
+
+local function onInputBegan(input, processed)
+ if not processed and input.UserInputType == Enum.UserInputType.Keyboard then
+ if input.KeyCode == Enum.KeyCode.Slash then
+ beginChatting()
+ end
+ end
+end
+
+local function onChatFocusLost(enterPressed)
+ local msg = chatBar.Text
+
+ if enterPressed and #msg > 0 then
+ if #msg > 128 then
+ msg = msg:sub(1, 125) .. "..."
+ end
+
+ chatRemote:FireServer(msg)
+
+ if hasCoreGateway then
+ coreGateway.ChatWindow.MessagePosted:Fire(msg)
+ end
+ end
+
+ chatBar.Text = ""
+ focusBackdrop.Visible = false
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
+
+chatBar.Focused:Connect(beginChatting)
+chatBar.FocusLost:Connect(onChatFocusLost)
+
+--------------------------------------------------------------------------------------------
+-- Chat Output
+--------------------------------------------------------------------------------------------
+
+local messageId = 0
+local blank_v2 = Vector2.new()
+local chatQueue = LinkedList.new()
+
+local function computeTextBounds(label)
+ local bounds = TextService:GetTextSize(label.Text, label.TextSize, label.Font, blank_v2)
+ return UDim2.new(0, bounds.X, 0, bounds.Y)
+end
+
+local function getMessageId()
+ messageId = messageId + 1
+ return messageId
+end
+
+local function onReceiveChat(player, message, wasFiltered)
+ -- Process the message
+ if message:sub(1, 1) == "%" then
+ message = "(TEAM) " .. message:sub(2)
+ end
+
+ if wasFiltered then
+ message = message:gsub("#[# ]+#", "[ Content Deleted ]")
+ end
+
+ -- Create the message
+ local msg = messageTemplate:Clone()
+
+ local playerLbl = msg:WaitForChild("PlayerName")
+ playerLbl.TextColor3 = computePlayerColor(player)
+ playerLbl.TextStrokeColor3 = playerLbl.TextColor3
+ playerLbl.Text = player.Name .. "; "
+ playerLbl.Size = computeTextBounds(playerLbl)
+
+ local msgLbl = msg:WaitForChild("Message")
+ msgLbl.Text = message
+ msgLbl.Size = computeTextBounds(msgLbl)
+
+ local width = playerLbl.AbsoluteSize.X + msgLbl.AbsoluteSize.X
+
+ msg.Size = msg.Size + UDim2.new(0, width, 0, 0)
+ msg.LayoutOrder = getMessageId()
+
+ msg.Name = "Message" .. msg.LayoutOrder
+ msg.Parent = chatOutput
+
+ if chatQueue.size == 6 then
+ local front = chatQueue.front
+ front.data:Destroy()
+
+ chatQueue:Remove(front.id)
+ end
+
+ chatQueue:Add(msg)
+ Debris:AddItem(msg, 60)
+end
+
+chatRemote.OnClientEvent:Connect(onReceiveChat)
+
+--------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/UI/Health.client.lua b/UI/Health.client.lua
new file mode 100644
index 0000000..bca252c
--- /dev/null
+++ b/UI/Health.client.lua
@@ -0,0 +1,32 @@
+local StarterGui = game:GetService("StarterGui")
+StarterGui:SetCoreGuiEnabled("All",false)
+
+local health = script.Parent
+local redBar = health:WaitForChild("RedBar")
+local greenBar = redBar:WaitForChild("GreenBar")
+
+local player = game.Players.LocalPlayer
+local c = workspace.CurrentCamera
+
+if c.ViewportSize.Y < 600 then
+ local scale = Instance.new("UIScale")
+ scale.Scale = 0.6
+ scale.Parent = health
+end
+
+local function onCharacterAdded(char)
+ local humanoid = char:WaitForChild("Humanoid")
+
+ local function updateHealth(health)
+ greenBar.Size = UDim2.new(1, 0, health / humanoid.MaxHealth, 0)
+ end
+
+ updateHealth(humanoid.MaxHealth)
+ humanoid.HealthChanged:Connect(updateHealth)
+end
+
+if player.Character then
+ onCharacterAdded(player.Character)
+end
+
+player.CharacterAdded:Connect(onCharacterAdded)
\ No newline at end of file
diff --git a/UI/Messages/Hint.rbxmx b/UI/Messages/Hint.rbxmx
new file mode 100644
index 0000000..0df6cbc
--- /dev/null
+++ b/UI/Messages/Hint.rbxmx
@@ -0,0 +1,76 @@
+
+ -
+
+ false
+
+ 0
+ 1
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ Hint
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0
+ 20
+
+ 0
+
+
+
+ 0.882353
+ 0.882353
+ 0.882353
+
+ false
+ 16
+
+ 0.7843138
+ 0.7843138
+ 0.7843138
+
+ 0.9
+ 0
+ 0
+ false
+ 2
+ 2
+ true
+ 1
+ true
+
+
+
\ No newline at end of file
diff --git a/UI/Messages/Message.rbxmx b/UI/Messages/Message.rbxmx
new file mode 100644
index 0000000..0b5c0b5
--- /dev/null
+++ b/UI/Messages/Message.rbxmx
@@ -0,0 +1,76 @@
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.4980392
+ 0.4980392
+ 0.4980392
+
+ 0.5
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ Message
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ -36
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 36
+
+ 0
+
+
+
+ 1
+ 1
+ 1
+
+ false
+ 18
+
+ 0
+ 0
+ 0
+
+ 0.1
+ 0.1
+ 0
+ true
+ 2
+ 1
+ false
+ 1
+ true
+
+
+
\ No newline at end of file
diff --git a/UI/Messages/Player.rbxmx b/UI/Messages/Player.rbxmx
new file mode 100644
index 0000000..55f856e
--- /dev/null
+++ b/UI/Messages/Player.rbxmx
@@ -0,0 +1,76 @@
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.5882353
+ 0.5882353
+ 0.5882353
+
+ 0.5
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 9
+ 0
+ 1
+ Player
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 32
+ 0
+ 5
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.25
+ 0
+ 0.25
+ 0
+
+ 0
+
+ Test
+
+ 1
+ 1
+ 1
+
+ false
+ 18
+
+ 0
+ 0
+ 0
+
+ 0.1
+ 0.1
+ 0
+ true
+ 2
+ 1
+ true
+ 10
+ true
+
+
+
\ No newline at end of file
diff --git a/UI/Messages/init.client.lua b/UI/Messages/init.client.lua
new file mode 100644
index 0000000..e2371f2
--- /dev/null
+++ b/UI/Messages/init.client.lua
@@ -0,0 +1,92 @@
+local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+
+local gui = script.Parent
+local player = Players.LocalPlayer
+
+local hintBin = Instance.new("Folder")
+local msgNameFmt = "MsgLbl_%s [%s]"
+
+local function addMessage(sourceMsg,msgType)
+ local isInPlayer = (sourceMsg.Parent == player)
+ local msgType = sourceMsg.ClassName
+
+ if msgType == "Message" and isInPlayer then
+ msgType = "Player"
+ end
+
+ local msgTemp = script:WaitForChild(msgType)
+
+ local msg = msgTemp:Clone()
+ msg.Name = msgNameFmt:format(msgType, sourceMsg:GetFullName())
+
+ local textUpdater = sourceMsg:GetPropertyChangedSignal("Text")
+ local isUpdating = false
+
+ local function updateText()
+ if not isUpdating then
+ isUpdating = true
+
+ msg.Text = sourceMsg.Text
+ sourceMsg.Text = ""
+
+ if msgType ~= "Hint" then
+ msg.Visible = (#msg.Text > 0)
+ end
+
+ isUpdating = false
+ end
+ end
+
+ local function onAncestryChanged()
+ local desiredAncestor
+
+ if msgType == "Hint" then
+ desiredAncestor = hintBin
+ elseif isInPlayer then
+ desiredAncestor = player
+ else
+ desiredAncestor = workspace
+ end
+
+ if not sourceMsg:IsDescendantOf(desiredAncestor) then
+ msg:Destroy()
+ end
+ end
+
+ --[[
+ I have to parent the Hint somewhere where it won't render since it
+ draws even if the Hint has no text. The server will remove the object
+ by it's reference address even if I change the parent, so this isn't a
+ problem online. But I can't rely on this in a non-network scenario so
+ regular Hints will still be visible offline if they're in the Workspace :(
+ --]]
+
+ if msgType == "Hint" then
+ wait()
+ sourceMsg.Parent = hintBin
+ end
+
+ updateText()
+ textUpdater:Connect(updateText)
+ sourceMsg.AncestryChanged:Connect(onAncestryChanged)
+
+ msg.Parent = gui
+end
+
+local function registerMessage(obj)
+ if obj:IsA("Message") then
+ addMessage(obj)
+ end
+end
+
+for _,v in pairs(workspace:GetDescendants()) do
+ registerMessage(v)
+end
+
+for _,v in pairs(player:GetChildren()) do
+ registerMessage(v)
+end
+
+player.ChildAdded:Connect(registerMessage)
+workspace.DescendantAdded:Connect(registerMessage)
\ No newline at end of file
diff --git a/UI/PlayerList/BaseGroup.rbxmx b/UI/PlayerList/BaseGroup.rbxmx
new file mode 100644
index 0000000..df3cd29
--- /dev/null
+++ b/UI/PlayerList/BaseGroup.rbxmx
@@ -0,0 +1,362 @@
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ -9999
+ BaseGroup
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 1
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 999999999
+ Footer
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.165
+ 0
+
+ 0
+ 0
+
+ false
+ 1
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ -9999999
+ Header
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.333
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+ -
+
+ false
+
+ 1
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ Title
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+
+ Player List
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ true
+ 0
+ 1
+ true
+ 2
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 0
+ Stats
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.75
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ TeamUnderline
+
[null]
+ [null]
+ [null]
+ [null]
+
+ -1
+ 2
+ 0.875
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ -4
+ 0
+ 1
+
+ 0
+ 0
+
+ false
+ 2
+ true
+
+
+
+ -
+
+
+ 1
+ 1
+ ListLayout
+
+
0
+ 0
+
+ 2
+
+ 1
+ true
+
+
+
+
\ No newline at end of file
diff --git a/UI/PlayerList/BasePlayerLbl.rbxmx b/UI/PlayerList/BasePlayerLbl.rbxmx
new file mode 100644
index 0000000..43e1a1d
--- /dev/null
+++ b/UI/PlayerList/BasePlayerLbl.rbxmx
@@ -0,0 +1,184 @@
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 1
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 0
+ BasePlayerLbl
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0.3333
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+ -
+
+ false
+
+ 1
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ PlayerName
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+
+ OnlyTwentyCharacters
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ true
+ 0
+ 1
+ true
+ 2
+ true
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 0
+ Stats
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.75
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+
+
\ No newline at end of file
diff --git a/UI/PlayerList/BaseStat.rbxmx b/UI/PlayerList/BaseStat.rbxmx
new file mode 100644
index 0000000..bcbd2ef
--- /dev/null
+++ b/UI/PlayerList/BaseStat.rbxmx
@@ -0,0 +1,76 @@
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ BaseStat
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+
+ 0
+
+ 1
+ 1
+ 1
+
+ true
+ 14
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ true
+ 0
+ 1
+ true
+ 2
+ true
+
+
+
\ No newline at end of file
diff --git a/UI/PlayerList/init.client.lua b/UI/PlayerList/init.client.lua
new file mode 100644
index 0000000..e3685ce
--- /dev/null
+++ b/UI/PlayerList/init.client.lua
@@ -0,0 +1,632 @@
+local Players = game:GetService("Players")
+local RunService = game:GetService("RunService")
+local Teams = game:GetService("Teams")
+
+spawn(function ()
+ local StarterGui = game:GetService("StarterGui")
+ StarterGui:SetCoreGuiEnabled("PlayerList", false)
+
+ local player = Players.LocalPlayer
+
+ local playerGui = player:WaitForChild("PlayerGui")
+ playerGui:SetTopbarTransparency(1)
+end)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Setup
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local playerStates = {}
+local teamGroups = {}
+
+local statLookup = {}
+local statNames = {}
+
+local inTeamMode = false
+
+local basePlayerLbl = script:WaitForChild("BasePlayerLbl")
+local baseGroup = script:WaitForChild("BaseGroup")
+local baseStat = script:WaitForChild("BaseStat")
+
+local playerList = script.Parent
+local backdrop = playerList:WaitForChild("Backdrop")
+local container = playerList:WaitForChild("Container")
+
+local coreGroup = baseGroup:Clone()
+coreGroup.Name = "Default"
+coreGroup.Parent = container
+
+local coreFooter = coreGroup.Footer
+local coreHeader = coreGroup.Header
+
+local eUpdateStatLayout = Instance.new("BindableEvent")
+local updateStatLayout = eUpdateStatLayout.Event
+
+local eUpdateTeamTotal = Instance.new("BindableEvent")
+local updateTeamTotal = eUpdateTeamTotal.Event
+
+local ePlayerTeamChanged = Instance.new("BindableEvent")
+local playerTeamChanged = ePlayerTeamChanged.Event
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Player Colors
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local PLAYER_COLORS =
+{
+ [0] = Color3.fromRGB(173, 35, 35); -- red
+ [1] = Color3.fromRGB( 42, 75, 215); -- blue
+ [2] = Color3.fromRGB( 29, 105, 20); -- green
+ [3] = Color3.fromRGB(129, 38, 192); -- purple
+ [4] = Color3.fromRGB(255, 146, 51); -- orange
+ [5] = Color3.fromRGB(255, 238, 51); -- yellow
+ [6] = Color3.fromRGB(255, 205, 243); -- pink
+ [7] = Color3.fromRGB(233, 222, 187); -- tan
+}
+
+local function computePlayerColor(player)
+ local pName = player.Name
+ local length = #pName
+ local oddShift = (1 - (length % 2))
+ local value = 0
+
+ for i = 1,length do
+ local char = pName:sub(i,i):byte()
+ local rev = (length - i) + oddShift
+ if (rev % 4) >= 2 then
+ value = value - char
+ else
+ value = value + char
+ end
+ end
+
+ return PLAYER_COLORS[value % 8]
+end
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Backdrop Handler
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local isTeamMode = false
+local hasStats = false
+
+local size1x1 = Vector2.new(1,1)
+local rawGroups = {}
+
+local function onContainerChildAdded(child)
+ if child:IsA("Frame") then
+ local listLayout = child:WaitForChild("ListLayout",2)
+ if listLayout then
+ rawGroups[child] = listLayout
+ end
+ end
+end
+
+local function onContainerChildRemoved(child)
+ if rawGroups[child] then
+ rawGroups[child] = nil
+ end
+end
+
+local function sortGroups(a,b)
+ if a == coreGroup then
+ return true
+ elseif b == coreGroup then
+ return false
+ else
+ local orderA,orderB = a.LayoutOrder,b.LayoutOrder
+ if orderA == orderB then
+ return a.Name < b.Name
+ else
+ return orderA < orderB
+ end
+ end
+end
+
+local function updateBackdrop()
+ local groups = {}
+ local at = 1
+
+ for group in pairs(rawGroups) do
+ if group.Visible then
+ groups[at] = group
+ at = at + 1
+ end
+ end
+
+ local height = 0
+ table.sort(groups,sortGroups)
+
+ for i = 1,#groups do
+ local group = groups[i]
+ local layout = rawGroups[group]
+ group.Position = UDim2.new(0,0,0,height)
+ height = height + layout.AbsoluteContentSize.Y
+ end
+
+ if #statNames > 0 and not hasStats then
+ hasStats = true
+ container.AnchorPoint = Vector2.new(1,0)
+ for _,group in pairs(groups) do
+ group.Header.TeamUnderline.Size = UDim2.new(2,-4,0,1)
+ end
+ eUpdateStatLayout:Fire()
+ elseif #statNames == 0 and hasStats then
+ hasStats = false
+ container.AnchorPoint = Vector2.new(0,0)
+ for _,group in pairs(groups) do
+ group.Header.TeamUnderline.Size = UDim2.new(1,-4,0,1)
+ end
+ eUpdateStatLayout:Fire()
+ end
+
+ if isTeamMode then
+ height = height + coreHeader.AbsoluteSize.Y
+ end
+
+ local width = container.AbsoluteSize.X * (container.AnchorPoint.X+1)
+ backdrop.Size = UDim2.new(0,width,0,height)
+end
+
+for _,child in pairs(container:GetChildren()) do
+ onContainerChildAdded(child)
+end
+
+container.ChildAdded:Connect(onContainerChildAdded)
+container.ChildRemoved:Connect(onContainerChildRemoved)
+RunService.Heartbeat:Connect(updateBackdrop)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Header Size Stuff
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local function switchHeaderMode(isTeamMode)
+ if isTeamMode then
+ coreHeader.Size = UDim2.new(1,0,1/3,0)
+ coreHeader.Stats.Size = UDim2.new(0.75,0,1,0)
+ coreHeader.Title.Size = UDim2.new(1,0,1,0)
+ else
+ coreHeader.Size = UDim2.new(1,0,0.4,0)
+ coreHeader.Stats.Size = UDim2.new(0.75,0,0.85,0)
+ coreHeader.Title.Size = UDim2.new(1,0,0.85,0)
+ end
+end
+
+switchHeaderMode(false)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Player Stats
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local function incrementStat(statName)
+ if not statLookup[statName] then
+ statLookup[statName] = 1
+ table.insert(statNames,statName)
+ else
+ statLookup[statName] = statLookup[statName] + 1
+ end
+ eUpdateStatLayout:Fire()
+end
+
+local function decrementStat(statName)
+ if statLookup[statName] then
+ statLookup[statName] = statLookup[statName] - 1
+
+ if statLookup[statName] == 0 then
+ statLookup[statName] = nil
+ for i,name in ipairs(statNames) do
+ if name == statName then
+ table.remove(statNames,i)
+ break
+ end
+ end
+ end
+
+ eUpdateStatLayout:Fire()
+ end
+end
+
+local function getPlayerStateFromStat(stat)
+ local leaderstats = stat.Parent
+ if leaderstats then
+ local player = leaderstats.Parent
+ if player then
+ return playerStates[player]
+ end
+ end
+end
+
+local function refreshTeamStats()
+ for _,team in pairs(Teams:GetTeams()) do
+ eUpdateTeamTotal:Fire(team)
+ end
+end
+
+local function onStatRemoved(stat,statName)
+ if stat.ClassName == "IntValue" then
+ local statName = statName or stat.Name
+ local playerState = getPlayerStateFromStat(stat)
+ if playerState and playerState.Stats[statName] then
+ playerState.Stats[statName]:Destroy()
+ playerState.Stats[statName] = nil
+ end
+ decrementStat(statName)
+ refreshTeamStats()
+ end
+end
+
+local function onStatAdded(stat)
+ if stat.ClassName == "IntValue" then
+ local statName = stat.Name
+ local playerState = getPlayerStateFromStat(stat)
+ if playerState then
+ local changeSignal
+
+ if not playerState.Stats[statName] then
+ local statLbl = baseStat:Clone()
+ statLbl.Name = statName
+
+ local function updateStat()
+ statLbl.Text = stat.Value
+ if isTeamMode then
+ local team = playerState.Player.Team
+ if team then
+ eUpdateTeamTotal:Fire(team)
+ end
+ end
+ end
+
+ updateStat()
+ changeSignal = stat.Changed:Connect(updateStat)
+
+ statLbl.Parent = playerState.Label.Stats
+ playerState.Stats[statName] = statLbl
+ end
+
+ local nameSignal do
+ local function onNameChanged()
+ if changeSignal then
+ changeSignal:Disconnect()
+ changeSignal = nil
+ end
+ nameSignal:Disconnect()
+ nameSignal = nil
+
+ -- Rebuild the stat
+ onStatRemoved(stat,statName)
+ onStatAdded(stat)
+ end
+
+ nameSignal = stat:GetPropertyChangedSignal("Name"):Connect(onNameChanged)
+ end
+ end
+
+ incrementStat(statName)
+ refreshTeamStats()
+ end
+end
+
+local function onPlayerChildAdded(leaderstats)
+ if leaderstats.Name == "leaderstats" then
+ local player = leaderstats.Parent
+ local playerState = playerStates[player]
+ if playerState and not playerState.leaderstats then
+ playerState.leaderstats = leaderstats
+ for _,stat in pairs(leaderstats:GetChildren()) do
+ onStatAdded(stat)
+ end
+ leaderstats.ChildAdded:Connect(onStatAdded)
+ leaderstats.ChildRemoved:Connect(onStatRemoved)
+ end
+ end
+end
+
+local function onPlayerChildRemoved(child)
+ if child.Name == "leaderstats" then
+ for _,stat in pairs(child:GetChildren()) do
+ onStatRemoved(stat)
+ end
+ for player,playerState in pairs(playerStates) do
+ if playerState.leaderstats == child then
+ playerState.leaderstats = nil
+ break
+ end
+ end
+ end
+end
+
+local function updateStatLbl(statLbl,index)
+ statLbl.Size = UDim2.new(1/#statNames,0,1,0)
+ statLbl.Position = UDim2.new((index-1)/#statNames)
+end
+
+local function onUpdateStatLayout()
+ local statBin = coreHeader.Stats
+
+ for _,statLbl in pairs(statBin:GetChildren()) do
+ if statLbl:IsA("TextLabel") and not statLookup[statLbl.Name] then
+ statLbl:Destroy()
+ end
+ end
+
+ for i,statName in pairs(statNames) do
+ local statLbl = statBin:FindFirstChild(statName)
+ if not statLbl then
+ statLbl = baseStat:Clone()
+ statLbl.Name = statName
+ statLbl.Text = statName
+ statLbl.Parent = statBin
+ end
+ updateStatLbl(statLbl,i)
+ end
+
+ for player,playerState in pairs(playerStates) do
+ for statName,statLbl in pairs(playerState.Stats) do
+ if not statLookup[statName] then
+ statLbl:Destroy()
+ playerState.Stats[statName] = nil
+ end
+ end
+
+ for i,statName in pairs(statNames) do
+ local statLbl = playerState.Stats[statName]
+ if statLbl then
+ if player.Team then
+ statLbl.TextColor = player.Team.TeamColor
+ else
+ statLbl.TextColor3 = Color3.new(1,1,1)
+ end
+ updateStatLbl(statLbl,i)
+ end
+ end
+ end
+
+ isTeamMode = (#Teams:GetTeams() > 0)
+
+ if isTeamMode then
+ coreHeader.Visible = hasStats
+ coreHeader.Title.Text = "Team"
+ else
+ coreHeader.Visible = true
+ if hasStats then
+ coreHeader.Title.Text = "Players"
+ else
+ coreHeader.Title.Text = "Player List"
+ end
+ end
+
+ switchHeaderMode(isTeamMode)
+end
+
+updateStatLayout:Connect(onUpdateStatLayout)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Player States
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local function onPlayerAdded(player)
+ local playerState = {}
+ local name = player.Name
+
+ local lbl = basePlayerLbl:Clone()
+ lbl.Name = name
+ lbl.PlayerName.Text = name
+ lbl.PlayerName.TextColor3 = computePlayerColor(player)
+ lbl.Parent = coreGroup
+
+ playerState.Player = player
+ playerState.Label = lbl
+ playerState.Stats = {}
+ playerStates[player] = playerState
+
+ for _,child in pairs(player:GetChildren()) do
+ onPlayerChildAdded(child)
+ end
+
+ player.ChildAdded:Connect(onPlayerChildAdded)
+ player.ChildRemoved:Connect(onPlayerChildRemoved)
+
+ player.Changed:Connect(function (property)
+ if property == "Team" then
+ ePlayerTeamChanged:Fire(player)
+ end
+ end)
+
+ ePlayerTeamChanged:Fire(player)
+end
+
+local function onPlayerRemoved(player)
+ local state = playerStates[player]
+ playerStates[player] = nil
+
+ if state and state.Label then
+ state.Label:Destroy()
+ end
+end
+
+for _,player in pairs(Players:GetPlayers()) do
+ onPlayerAdded(player)
+end
+
+Players.PlayerAdded:Connect(onPlayerAdded)
+Players.PlayerRemoving:Connect(onPlayerRemoved)
+
+--------------------------------------------------------------------------------------------------------------------------------------
+-- Teams
+--------------------------------------------------------------------------------------------------------------------------------------
+
+local function neutralizePlayer(player)
+ local playerState = playerStates[player]
+ if playerState then
+ local playerLbl = playerState.Label
+ playerLbl.PlayerName.Text = player.Name
+ playerLbl.PlayerName.TextColor3 = computePlayerColor(player)
+ playerLbl.PlayerName.Position = UDim2.new(0,0,0,0)
+ for stat,statLbl in pairs(playerState.Stats) do
+ statLbl.TextColor3 = Color3.new(1,1,1)
+ end
+ playerLbl.Visible = (not isTeamMode)
+ playerLbl.Parent = coreGroup
+ end
+end
+
+local function onPlayerAddedToTeam(player)
+ local team = player.Team
+ local group = teamGroups[team]
+ if group then
+ local playerState = playerStates[player]
+ if playerState then
+ local playerLbl = playerState.Label
+ playerLbl.PlayerName.TextColor = team.TeamColor
+ playerLbl.PlayerName.Position = UDim2.new(0,4,0,0)
+ for stat,statLbl in pairs(playerState.Stats) do
+ statLbl.TextColor = team.TeamColor
+ end
+ playerLbl.Parent = group
+ playerLbl.Visible = true
+ eUpdateStatLayout:Fire()
+ refreshTeamStats()
+ end
+ end
+end
+
+local function onPlayerRemovedFromTeam(player)
+ if not player.Team then
+ neutralizePlayer(player)
+ refreshTeamStats()
+ end
+end
+
+local function onUpdateTeamTotal(team)
+ local teamGroup = teamGroups[team]
+ if teamGroup then
+ local teamStats = teamGroup.Header.Stats
+ local totals = {}
+
+ for i,statName in ipairs(statNames) do
+ local total = totals[i]
+ if not total then
+ total = { Name = statName, Value = 0 }
+ totals[i] = total
+ end
+ for _,player in pairs(team:GetPlayers()) do
+ local playerState = playerStates[player]
+ if playerState then
+ local leaderstats = playerState.leaderstats
+ if leaderstats then
+ local stat = leaderstats:FindFirstChild(statName)
+ if stat then
+ total.Value = total.Value + stat.Value
+ end
+ end
+ end
+ end
+ end
+
+ local numStats = #statNames
+
+ for i,statRecord in ipairs(totals) do
+ local statName = statRecord.Name
+ local statLbl = teamStats:FindFirstChild(statName)
+ if not statLbl then
+ statLbl = baseStat:Clone()
+ statLbl.Name = statName
+ statLbl.TextColor = team.TeamColor
+ statLbl.TextStrokeTransparency = 0.5
+ statLbl.Parent = teamStats
+ end
+ statLbl.Text = statRecord.Value
+ updateStatLbl(statLbl,i)
+ end
+
+ for _,statLbl in pairs(teamStats:GetChildren()) do
+ if not statLookup[statLbl.Name] then
+ statLbl:Destroy()
+ end
+ end
+ end
+end
+
+local function onTeamAdded(team)
+ if team.ClassName == "Team" then
+ local teamGroup = baseGroup:Clone()
+ teamGroup.Name = team.Name
+ teamGroup.Footer.Visible = true
+
+ local teamHeader = teamGroup.Header
+
+ local teamUnderline = teamHeader.TeamUnderline
+ teamUnderline.Visible = true
+ teamUnderline.BackgroundColor = team.TeamColor
+
+ if hasStats then
+ teamUnderline.Size = teamUnderline.Size + UDim2.new(1,0,0,0)
+ end
+
+ local teamTitle = teamHeader.Title
+ teamTitle.Text = team.Name
+ teamTitle.TextColor = team.TeamColor
+ teamTitle.TextStrokeTransparency = 0.5
+
+ teamGroup.Parent = container
+ teamGroups[team] = teamGroup
+
+ for _,player in pairs(team:GetPlayers()) do
+ onPlayerAddedToTeam(player)
+ end
+
+ eUpdateTeamTotal:Fire(team)
+ eUpdateStatLayout:Fire()
+ end
+ if #Teams:GetTeams() > 0 and not isTeamMode then
+ isTeamMode = true
+ for _,player in pairs(Players:GetPlayers()) do
+ if not player.Team then
+ neutralizePlayer(player)
+ end
+ end
+ end
+end
+
+local function onTeamRemoved(team)
+ if teamGroups[team] then
+ for _,player in pairs(Players:GetPlayers()) do
+ if player.TeamColor == team.TeamColor then
+ neutralizePlayer(player)
+ end
+ end
+ teamGroups[team]:Destroy()
+ teamGroups[team] = nil
+ eUpdateStatLayout:Fire()
+ end
+ if #Teams:GetTeams() == 0 then
+ isTeamMode = false
+ for _,player in pairs(Players:GetPlayers()) do
+ neutralizePlayer(player)
+ end
+ end
+end
+
+local function onPlayerTeamChange(player)
+ local team = player.Team
+ if team then
+ onPlayerAddedToTeam(player)
+ else
+ onPlayerRemovedFromTeam(player)
+ end
+end
+
+for _,team in pairs(Teams:GetTeams()) do
+ onTeamAdded(team)
+end
+
+for _,player in pairs(Players:GetPlayers()) do
+ onPlayerTeamChange(player)
+end
+
+Teams.ChildAdded:Connect(onTeamAdded)
+Teams.ChildRemoved:Connect(onTeamRemoved)
+updateTeamTotal:Connect(onUpdateTeamTotal)
+playerTeamChanged:Connect(onPlayerTeamChange)
+
+--------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/UI/RootFrame.rbxmx b/UI/RootFrame.rbxmx
new file mode 100644
index 0000000..56eebe1
--- /dev/null
+++ b/UI/RootFrame.rbxmx
@@ -0,0 +1,1790 @@
+
+ true
+ null
+ nil
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ false
+ false
+ 0
+ RootFrame
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+
+ ChatPadding
+
+
0
+ 20
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+ false
+
+ 0.5
+ 0.5
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ false
+ false
+ rbxassetid://334630296
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ ClassicMouse
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0.5
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 0
+ 80
+ 0
+ 80
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 10
+
+
+ -
+
+ true
+
+ 0
+ 1
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://991182833
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+ SafeChat
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 22
+ 0.75
+ 0
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 32
+ 0
+ 30
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+ -
+
+ false
+
+ 0.5
+ 0.75
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ false
+ false
+ rbxasset://textures/ui/Settings/Help/XButtonDark@2x.png
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ GamepadHint
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 5
+ 0
+ 0
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 0.75
+ 0
+ 0.75
+ 0
+
+ 2
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ false
+ 1
+
+
+
+ -
+
+ false
+
+ 1
+ 1
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ false
+ false
+ 0
+ ZoomControls
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 70
+ 0
+ 70
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ true
+
+ 0
+ 0
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://598662248
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+ RotateUp
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+
+ -
+
+ true
+
+ 0
+ 0
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://598662248
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+ RotateDown
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 35
+
+
+ [null]
+ -180
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+
+ -
+
+ true
+
+ 0
+ 0
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://598663795
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+ ZoomIn
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 35
+ 0
+ 0
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0.5
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+ 0
+ Lock
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+ 0
+
+ false
+ 3
+
+
+
+ -
+
+ true
+
+ 0
+ 0
+
+
+ true
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+
+ rbxassetid://598665130
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ false
+ ZoomOut
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+
+ [null]
+ 0
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 35
+ 0
+ 35
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+ 0
+
+
+ 1
+ 0
+ 1
+ 0
+
+ true
+ 2
+
+
+ -
+
+ false
+
+ 1
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ false
+ false
+ rbxassetid://598702035
+
+ 1
+ 1
+ 1
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+ 0
+ 0
+ FirstPersonIndicator
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 0
+ 0
+ -97
+
+ [null]
+ 0
+ 0
+ false
+ [null]
+
+ 0
+ 168
+ 0
+ 42
+
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 1
+
+
+ 1
+ 0
+ 1
+ 0
+
+ false
+ 2
+
+
+
+ -
+
+ false
+
+ 1
+ 0.5
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+ 0
+ Health
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ -31
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 66
+ 0
+ 137
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ false
+
+ 0.5
+ 0
+
+
+ true
+
+ 1
+ 0
+ 0
+
+ 0
+
+ 1
+ 0
+ 0
+
+ 0
+ 0
+ false
+ false
+ 0
+ RedBar
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 12
+ 0
+ 112
+
+ 0
+ 0
+
+ true
+ 2
+
+ -
+
+ false
+
+ 0.5
+ 1
+
+
+ true
+
+ 0.505882382
+ 0.772549093
+ 0.0862745121
+
+ 0
+
+ 0.505882382
+ 0.772549093
+ 0.0862745121
+
+ 0
+ 0
+ false
+ false
+ 0
+ GreenBar
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0.5
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 3
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ HealthLbl
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 1
+ -24
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0
+ 24
+
+ 0
+
+ Health
+
+ 0
+ 0
+ 1
+
+ true
+ 18
+
+ 0
+ 0
+ 1
+
+ 0.899999976
+ 0
+ 0
+ true
+ 2
+ 1
+ true
+ 2
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ false
+ false
+ 0
+ Chat
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ true
+
+ 1
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.168627501
+ 0.168627501
+ 0.168627501
+
+ 0
+ 0
+ true
+ false
+ false
+ 4
+ 0
+ 1
+ false
+ ChatBar
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 1
+ 0.784313798
+
+ To chat click here or press the "/" key
+
+ 1
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ true
+ [null]
+ true
+
+ 1
+ -3
+ 0
+ 15
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+ true
+ false
+ 15
+
+ 0
+ 0
+ 0
+
+ 1
+ 0
+ 0
+ false
+ 0
+ 1
+ true
+ 3
+
+ -
+
+ false
+
+ 1
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+ 0
+ FocusBackdrop
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 3
+ 1
+ 0
+
+ 0
+ 0
+
+ false
+ 2
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.250980407
+ 0.250980407
+ 0.250980407
+
+ 0
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+ 0
+ MainBackdrop
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0
+ 20
+
+ 0
+ 0
+
+ true
+ 1
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ true
+ false
+ 0
+ ChatOutput
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 23
+ 0
+ 29
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 0
+ 96
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+
+ 1
+ 1
+ Stream
+
+
0
+ 0
+
+ 2
+
+ 2
+
+
+
+
+ -
+
+ true
+
+ 0
+ 1
+
+
+ true
+
+ 0.498039186
+ 0.498039186
+ 0.498039186
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+ 0
+ Backpack
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 2
+ 0
+
+ true
+ 1
+
+ -
+
+
+ UIScale
+ 0.133000001
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0.70588237
+ 0.70588237
+ 0.70588237
+
+ 0.5
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+ 0
+ Backdrop
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 2
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ false
+ false
+ 0
+ Slots
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+
+ 0
+ 1
+ UIListLayout
+
+
0
+ 0
+
+ 2
+
+ 1
+
+
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 1
+ false
+ false
+ 0
+ PlayerList
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ -36
+
+ [null]
+ 0
+ false
+ [null]
+
+ 1
+ 0
+ 1
+ 36
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ false
+
+ 1
+ 0
+
+
+ true
+
+ 0.600000024
+ 0.600000024
+ 0.600000024
+
+ 0.400000006
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+ 0
+ Backdrop
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ -10
+ 0
+ 10
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 10
+ 0
+ 10
+
+ 0
+ 0
+
+ true
+ 1
+
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 0
+ 0
+ 0
+
+ 1
+
+ 0.105882399
+ 0.164705902
+ 0.207843199
+
+ 0
+ 0
+ false
+ false
+ 0
+ Container
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ -10
+ 0
+ 10
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0.165000007
+ 0
+ 1
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+
+ -
+
+ 3
+ 0
+
+ 0
+ AspectRatio
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/UI/SafeChat/Click.rbxmx b/UI/SafeChat/Click.rbxmx
new file mode 100644
index 0000000..66db075
--- /dev/null
+++ b/UI/SafeChat/Click.rbxmx
@@ -0,0 +1,24 @@
+
+ -
+
+
+ 10
+ false
+ Click
+ false
+ 1
+ false
+ 0
+
[null]
+
+ rbxasset://sounds/switch.mp3
+
+
+ 0
+ 0.5
+ 10000
+ 10
+ true
+
+
+
\ No newline at end of file
diff --git a/UI/SafeChat/NullSelectionImageObject.rbxmx b/UI/SafeChat/NullSelectionImageObject.rbxmx
new file mode 100644
index 0000000..6373a95
--- /dev/null
+++ b/UI/SafeChat/NullSelectionImageObject.rbxmx
@@ -0,0 +1,56 @@
+
+ -
+
+ false
+
+ 0
+ 0
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 0
+ false
+ false
+ 0
+ NullSelectionImageObject
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ 0
+ 0
+
+ true
+ 1
+ true
+
+
+
\ No newline at end of file
diff --git a/UI/SafeChat/TempBranch.rbxmx b/UI/SafeChat/TempBranch.rbxmx
new file mode 100644
index 0000000..d353cc0
--- /dev/null
+++ b/UI/SafeChat/TempBranch.rbxmx
@@ -0,0 +1,72 @@
+
+ -
+
+ false
+
+ 0
+ 0.5
+
+
+ true
+
+ 1
+ 1
+ 1
+
+ 1
+
+ 0.1058824
+ 0.1647059
+ 0.2078432
+
+ 0
+ 1
+ false
+ false
+ 0
+ TempBranch
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 1
+ 5
+ 0.5
+ 0
+
+ [null]
+ 0
+ false
+ [null]
+
+ 0
+ 140
+ 0
+ 24
+
+ 0
+ 0
+
+ false
+ 2
+ true
+
+ -
+
+
+ 1
+ 1
+ UIListLayout
+
+
0
+ 5
+
+ 2
+
+ 0
+ true
+
+
+
+
\ No newline at end of file
diff --git a/UI/SafeChat/TempButton.rbxmx b/UI/SafeChat/TempButton.rbxmx
new file mode 100644
index 0000000..5346add
--- /dev/null
+++ b/UI/SafeChat/TempButton.rbxmx
@@ -0,0 +1,80 @@
+
+ -
+
+ true
+
+ 0
+ 0
+
+
+ false
+ true
+
+ 1
+ 1
+ 1
+
+ 0
+
+ 0.2470588
+ 0.2470588
+ 0.2470588
+
+ 0
+ 1
+ false
+ false
+ 9
+ 0
+ 1
+ false
+ TempButton
+
[null]
+ [null]
+ [null]
+ [null]
+
+ 0
+ 0
+ 0
+ 0
+
+ [null]
+ 0
+ true
+ false
+ [null]
+
+ 0
+ 140
+ 0
+ 24
+
+ 0
+ 0
+
+ ROOT
+
+ 0
+ 0
+ 0
+
+ false
+ 12
+
+ 0
+ 0
+ 0
+
+ 0.95
+ 0.25
+ 0
+ false
+ 2
+ 1
+ true
+ 2
+ true
+
+
+
\ No newline at end of file
diff --git a/UI/SafeChat/init.client.lua b/UI/SafeChat/init.client.lua
new file mode 100644
index 0000000..08aac35
--- /dev/null
+++ b/UI/SafeChat/init.client.lua
@@ -0,0 +1,204 @@
+local UserInputService = game:GetService("UserInputService")
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local GuiService = game:GetService("GuiService")
+
+local c = workspace.CurrentCamera
+local resUpdate = c:GetPropertyChangedSignal("ViewportSize")
+local safeChat = script.Parent
+local click = script:WaitForChild("Click")
+
+local IMG_CHAT = "rbxassetid://991182833"
+local IMG_CHAT_DN = "rbxassetid://991182832"
+local IMG_CHAT_OVR = "rbxassetid://991182834"
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Fetch Tree Data
+
+local mSafeChatTree = ReplicatedStorage:WaitForChild("SafeChatTree")
+local safeChatTree = require(mSafeChatTree)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Button Positioning
+
+local IS_PHONE = c.ViewportSize.Y < 600
+
+local function onResolutionUpdate()
+ local viewPort = c.ViewportSize
+ local chatX = math.min(25,viewPort.Y/40)
+ local chatY = (viewPort.X/viewPort.Y) * (viewPort.Y * 0.225)
+ safeChat.Position = UDim2.new(0,chatX,1,-chatY)
+end
+
+onResolutionUpdate()
+resUpdate:Connect(onResolutionUpdate)
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Safe Chat Tree
+
+local chatRemote = ReplicatedStorage:WaitForChild("ChatRemote")
+local tempBranch = script:WaitForChild("TempBranch")
+local tempButton = script:WaitForChild("TempButton")
+local isActivated = false
+local rootTree
+
+local function recursivelyDeactivateTree(obj)
+ if obj:IsA("Frame") then
+ obj.Visible = false
+ elseif obj:IsA("TextButton") then
+ obj.BackgroundColor3 = Color3.new(1,1,1)
+ end
+ for _,v in pairs(obj:GetChildren()) do
+ recursivelyDeactivateTree(v)
+ end
+end
+
+local function deactivateRootTree()
+ isActivated = false
+ safeChat.Image = IMG_CHAT
+ recursivelyDeactivateTree(rootTree)
+
+ if GuiService.SelectedObject then
+ GuiService.SelectedObject = nil
+ GuiService:RemoveSelectionGroup("SafechatNav")
+ end
+end
+
+local function activateRootTree()
+ isActivated = true
+ rootTree.Visible = true
+ safeChat.Image = IMG_CHAT_DN
+
+ if UserInputService:GetLastInputType() == Enum.UserInputType.Gamepad1 then
+ GuiService:AddSelectionParent("SafechatNav",safeChat)
+ GuiService.SelectedObject = safeChat
+ end
+end
+
+local function assembleTree(tree)
+ local treeFrame = tempBranch:Clone()
+ treeFrame.Name = "Branches"
+
+ local currentBranch
+
+ for i,branch in ipairs(tree.Branches) do
+ local label = branch.Label
+ local branches = branch.Branches
+ local button = tempButton:Clone()
+ button.Name = label
+ button.Text = label
+ button.LayoutOrder = i
+ local branchFrame = assembleTree(branch)
+ branchFrame.Parent = button
+ button.Parent = treeFrame
+
+ local function onEnter()
+ if currentBranch then
+ recursivelyDeactivateTree(currentBranch)
+ end
+ currentBranch = button
+ button.BackgroundColor3 = Color3.new(0.7,0.7,0.7)
+ branchFrame.Visible = true
+ end
+
+ local function onActivate()
+ local submit = true
+ if UserInputService.TouchEnabled then
+ if not branchFrame.Visible and #branchFrame:GetChildren() > 1 then
+ branchFrame.Visible = true
+ submit = false
+ end
+ end
+ if submit then
+ deactivateRootTree()
+ chatRemote:FireServer(label)
+ click:Play()
+ end
+ end
+
+ button.MouseEnter:Connect(onEnter)
+ button.SelectionGained:Connect(onEnter)
+ button.MouseButton1Down:Connect(onActivate)
+ end
+
+ return treeFrame
+end
+
+rootTree = assembleTree(safeChatTree)
+rootTree.Parent = safeChat
+
+if IS_PHONE then
+ local uiScale = Instance.new("UIScale")
+ uiScale.Parent = rootTree
+ uiScale.Scale = 0.7
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Button State
+
+local isActivated = false
+local isHovering = false
+
+do
+ local function onMouseEnter()
+ if not isActivated then
+ safeChat.Image = IMG_CHAT_OVR
+ end
+ isHovering = true
+ end
+
+ local function onMouseLeave()
+ if not isActivated then
+ safeChat.Image = IMG_CHAT
+ end
+ isHovering = false
+ end
+
+ local function onMouseDown()
+ safeChat.Image = IMG_CHAT_DN
+ end
+
+ local function onMouseUp()
+ if isHovering then
+ activateRootTree()
+ end
+ end
+
+ local function onInputBegan(input,gameProcessed)
+ if input.UserInputType == Enum.UserInputType.MouseButton1 and not gameProcessed and isActivated then
+ deactivateRootTree()
+ end
+ end
+
+ safeChat.MouseEnter:Connect(onMouseEnter)
+ safeChat.MouseLeave:Connect(onMouseLeave)
+ safeChat.MouseButton1Down:Connect(onMouseDown)
+ safeChat.MouseButton1Up:Connect(onMouseUp)
+
+ UserInputService.InputBegan:Connect(onInputBegan)
+end
+
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Gamepad Stuff
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local gamepadHint = safeChat:WaitForChild("GamepadHint")
+
+if GuiService:IsTenFootInterface() then
+ gamepadHint.Visible = true
+else
+ local function onLastInputTypeChanged(inputType)
+ gamepadHint.Visible = (inputType.Name == "Gamepad1")
+ end
+
+ onLastInputTypeChanged(UserInputService:GetLastInputType())
+ UserInputService.LastInputTypeChanged:Connect(onLastInputTypeChanged)
+end
+
+
+local function onInputBegan(input)
+ if input.KeyCode == Enum.KeyCode.ButtonX then
+ activateRootTree()
+ end
+end
+
+UserInputService.InputBegan:Connect(onInputBegan)
\ No newline at end of file
diff --git a/UI/ZoomControls.client.lua b/UI/ZoomControls.client.lua
new file mode 100644
index 0000000..36fcd84
--- /dev/null
+++ b/UI/ZoomControls.client.lua
@@ -0,0 +1,112 @@
+local UserInputService = game:GetService("UserInputService")
+local GuiService = game:GetService("GuiService")
+
+if GuiService:IsTenFootInterface() then
+ return
+end
+
+local IS_TOUCH = UserInputService.TouchEnabled
+
+local player = game.Players.LocalPlayer
+local playerScripts = player:WaitForChild("PlayerScripts")
+local passCameraEvent = playerScripts:WaitForChild("PassCameraEvent")
+
+local self = script.Parent
+local zoomIn = self:WaitForChild("ZoomIn")
+local zoomLock = zoomIn:WaitForChild("Lock")
+local firstPersonIndicator = self:WaitForChild("FirstPersonIndicator")
+
+local yellow = Color3.new(1,1,0)
+local cyan = Color3.new(0,1,1)
+local white = Color3.new(1,1,1)
+
+local c = workspace.CurrentCamera
+local currentlyDown
+
+local function updateCameraStatus()
+ local dist = (c.Focus.p - c.CFrame.p).magnitude
+ firstPersonIndicator.Visible = (dist <= 1.5)
+ zoomLock.Visible = (dist <= 1)
+end
+
+local function setupButton(btn)
+ local isDown = false
+ local inBounds = false
+ local lock = btn:FindFirstChild("Lock")
+ local mouse = player:GetMouse()
+ btn.MouseEnter:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ if (currentlyDown == nil or currentlyDown == btn) then
+ inBounds = true
+ if isDown then
+ btn.ImageColor3 = yellow
+ else
+ btn.ImageColor3 = cyan
+ end
+ end
+ end
+ end)
+ btn.MouseLeave:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ inBounds = false
+ if isDown then
+ btn.ImageColor3 = cyan
+ else
+ btn.ImageColor3 = white
+ end
+ end
+ end)
+ btn.MouseButton1Down:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ isDown = true
+ currentlyDown = btn
+ btn.ImageColor3 = yellow
+ end
+ end)
+ btn.MouseButton1Click:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ isDown = false
+ currentlyDown = nil
+ inBounds = false
+ passCameraEvent:Fire(btn.Name)
+ if IS_TOUCH then
+ btn.ImageColor3 = white
+ end
+ end
+ end)
+ mouse.Button1Up:connect(function ()
+ if (lock == nil or not lock.Visible) then
+ if isDown then
+ isDown = false
+ currentlyDown = nil
+ if inBounds then
+ inBounds = false
+ passCameraEvent:Fire(btn.Name)
+ end
+ end
+ end
+ btn.ImageColor3 = white
+ end)
+ if lock then
+ lock.Changed:connect(function ()
+ if lock.Visible then
+ btn.ImageColor3 = white
+ isDown = false
+ if currentlyDown == btn then
+ currentlyDown = nil
+ end
+ end
+ end)
+ end
+ if IS_TOUCH then
+ btn.Modal = true
+ end
+end
+
+for _,v in pairs(script.Parent:GetChildren()) do
+ if v:IsA("ImageButton") then
+ setupButton(v)
+ end
+end
+
+c.Changed:connect(updateCameraStatus)
\ No newline at end of file
diff --git a/default.project.json b/default.project.json
new file mode 100644
index 0000000..b4629f7
--- /dev/null
+++ b/default.project.json
@@ -0,0 +1,54 @@
+{
+ "name": "MainModule",
+
+ "tree":
+ {
+ "$path": "main.lua",
+
+ "ReplicatedFirst":
+ {
+ "$className": "Folder",
+
+ "JoinScript":
+ {
+ "$path": "Resources/GameJoin"
+ }
+ },
+
+ "ReplicatedStorage":
+ {
+ "$className": "Folder",
+
+ "SafeChatTree":
+ {
+ "$path": "Resources/SafeChat"
+ }
+ },
+
+ "StarterGui":
+ {
+ "$className": "Folder",
+
+ "UI":
+ {
+ "$className": "ScreenGui",
+ "$path": "UI"
+ }
+ },
+
+ "ServerScriptService":
+ {
+ "$path": "Server"
+ },
+
+ "StarterPlayerScripts":
+ {
+ "$path": "Client"
+ },
+
+ "StarterCharacterScripts":
+ {
+ "$path": "Character"
+ }
+ }
+}
\ No newline at end of file
diff --git a/main.lua b/main.lua
new file mode 100644
index 0000000..5d44be5
--- /dev/null
+++ b/main.lua
@@ -0,0 +1,174 @@
+local gameInst = game.JobId
+local isOnline = (gameInst ~= "")
+
+if isOnline and game.GameId ~= 123949867 then
+ script:Destroy()
+ return
+end
+
+local dataModel = script:WaitForChild("DataModel")
+local framework = script:WaitForChild("Framework")
+
+local Chat = game:GetService("Chat")
+local InsertService = game:GetService("InsertService")
+local Lighting = game:GetService("Lighting")
+local Players = game:GetService("Players")
+local ServerStorage = game:GetService("ServerStorage")
+local StarterGui = game:GetService("StarterGui")
+local StarterPlayer = game:GetService("StarterPlayer")
+local TeleportService = game:GetService("TeleportService")
+
+local initMsg = Instance.new("Message")
+initMsg.Text = "INITIALIZING..."
+initMsg.Parent = workspace
+
+if not workspace.FilteringEnabled then
+ initMsg.Text = "FATAL: Workspace.FilteringEnabled MUST be set to true!!!"
+ return 0
+end
+
+local override = ServerStorage:FindFirstChild("LocalGameImport")
+
+if override then
+ if isOnline then
+ warn("WARNING: Dev framework is present in a networked game, and it shouldn't be!!!")
+ override:Destroy()
+ elseif override ~= script then
+ initMsg:Destroy()
+ require(override)
+ return 1
+ end
+end
+
+-- Standard Forced Settings
+Lighting.Outlines = false
+Players.CharacterAutoLoads = false
+StarterGui.ShowDevelopmentGui = false
+
+local sky = Lighting:FindFirstChildOfClass("Sky")
+
+if not sky then
+ local skyProps = {"Bk", "Dn", "Ft", "Lf", "Rt", "Up"}
+ local skyId = "rbxasset://Sky/null_plainsky512_%s.jpg"
+
+ sky = Instance.new("Sky")
+
+ for _,prop in pairs(skyProps) do
+ sky["Skybox"..prop] = skyId:format(prop:lower())
+ end
+
+ sky.Parent = Lighting
+end
+
+sky.SunAngularSize = 14
+sky.MoonAngularSize = 6
+
+local devProps =
+{
+ DevComputerMovementMode = "KeyboardMouse";
+ DevComputerCameraMovementMode = "Classic";
+ DevTouchMovementMode = "UserChoice";
+ DevTouchCameraMovementMode = "Classic";
+}
+
+StarterPlayer.LoadCharacterAppearance = false
+StarterPlayer.EnableMouseLockOption = false
+
+for prop,value in pairs(devProps) do
+ StarterPlayer[prop] = value
+end
+
+for _,player in pairs(Players:GetPlayers()) do
+ local char = player.Character
+
+ if char and char:IsDescendantOf(workspace) then
+ char:Destroy()
+ player.Character = nil
+ end
+
+ for prop,value in pairs(devProps) do
+ StarterPlayer[prop] = value
+ end
+end
+
+-- Import the shared universe assets (scripts and stuff shared between both the main menu and the actual places)
+require(ServerStorage:FindFirstChild("LocalSharedImport") or 1027421176)
+
+-- Load Scripts
+for _,desc in pairs(dataModel:GetDescendants()) do
+ if desc:IsA("StringValue") and desc.Name:sub(1,9) == "ScriptRef" then
+ local scriptName = desc.Name:gsub("ScriptRef%[(.+)%]","%1")
+ local scriptPath = desc.Value
+ local scriptRef = framework
+
+ local gotScript = true
+
+ for path in scriptPath:gmatch("[^/]+") do
+ scriptRef = scriptRef:WaitForChild(path, 1)
+
+ if not scriptRef then
+ gotScript = false
+
+ warn("WARNING: Failed to load ScriptRef for", desc:GetFullName())
+ warn(" got stuck at:", path)
+
+ break
+ end
+ end
+
+ if gotScript then
+ local newScript = scriptRef:Clone()
+ newScript.Name = scriptName
+
+ if newScript:IsA("BaseScript") then
+ newScript.Disabled = false
+ end
+
+ for _,child in pairs(desc:GetChildren()) do
+ child.Parent = newScript
+ end
+
+ newScript.Parent = desc.Parent
+ end
+
+ desc:Destroy()
+ end
+end
+
+-- Load DataModel
+
+for _,rep in pairs(dataModel:GetChildren()) do
+ local real = game:FindFirstChildWhichIsA(rep.Name, true)
+
+ if not real then -- Hopefully a service that doesn't exist yet?
+ real = game:GetService(rep.Name)
+ end
+
+ for _,child in pairs(rep:GetChildren()) do
+ local existing = real:FindFirstChild(child.Name)
+
+ if existing then
+ existing:Destroy()
+ end
+
+ child.Parent = real
+ end
+end
+
+-- Reconnect any players that may have joined during initialization.
+-- (or restart the PlayerScripts manually if I'm offline testing)
+
+local StarterPlayerScripts = StarterPlayer:WaitForChild("StarterPlayerScripts")
+
+if isOnline then
+ for _,player in pairs(Players:GetPlayers()) do
+ TeleportService:TeleportToPlaceInstance(game.PlaceId, gameInst, player)
+ end
+end
+
+if Chat.LoadDefaultChat then
+ warn("Chat.LoadDefaultChat should be set to false!")
+end
+
+initMsg:Destroy()
+return 0
\ No newline at end of file
diff --git a/place.project.json b/place.project.json
new file mode 100644
index 0000000..aaab7bb
--- /dev/null
+++ b/place.project.json
@@ -0,0 +1,38 @@
+{
+ "name": "Roblox-PNG-Library",
+
+ "tree":
+ {
+ "$className": "DataModel",
+
+ "ReplicatedStorage":
+ {
+ "$className": "ReplicatedStorage",
+
+ "PNG":
+ {
+ "$path": "init.lua",
+
+ "Chunks":
+ {
+ "$path": "Chunks"
+ },
+
+ "BinaryReader":
+ {
+ "$path": "Modules/BinaryReader.lua"
+ },
+
+ "Deflate":
+ {
+ "$path": "Modules/Deflate.lua"
+ },
+
+ "Unfilter":
+ {
+ "$path": "Modules/Unfilter.lua"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/rojo-build.bat b/rojo-build.bat
new file mode 100644
index 0000000..dcab747
--- /dev/null
+++ b/rojo-build.bat
@@ -0,0 +1,4 @@
+@echo off
+rojo build -o Test.rbxmx
+
+pause
\ No newline at end of file
diff --git a/rojo-build.sh b/rojo-build.sh
new file mode 100644
index 0000000..16c7e99
--- /dev/null
+++ b/rojo-build.sh
@@ -0,0 +1 @@
+rojo build -o PNG.rbxm
diff --git a/rojo-serve.bat b/rojo-serve.bat
new file mode 100644
index 0000000..4cb5052
--- /dev/null
+++ b/rojo-serve.bat
@@ -0,0 +1,4 @@
+@echo off
+rojo serve place.project.json
+
+pause
\ No newline at end of file
diff --git a/rojo-serve.sh b/rojo-serve.sh
new file mode 100644
index 0000000..75bc9c9
--- /dev/null
+++ b/rojo-serve.sh
@@ -0,0 +1 @@
+rojo serve place.project.json