Hey! This is my SIC System— an ongoing project I’ve been working on that adds a bit of a unique feel of immersion to your experience!
Allow me to introduce you to the second version my Sunray Intensity Compensation System!
This system tweaks a SunRaysEffect instance to a specific value every time the camera moves. If the camera is looking closer towards the sun than the previous check, then sunray intensity will be decreased.
The closer you look towards the sun, the less intense sunrays are. This eliminates the issues with high intensity values, while maintaining 99% of control over the sun, sky, clouds, and anything else.
As of June 6th, 2024, I have updated the system version `2.9.0`This system will inform you of new updates in your Studio Output window upon test playing!This system will update automatically upon server start. All default settings are now handled in the root server script's attributes section of the properties panel. Further client settings are handled using the systems included API functions.
Check the changelog thread here for new changes; I will continue to keep this updated.
If there are any better ways to code sections of this script or the entirety of it, do feel free to post a comment. I will gladly adjust the script to add the necessary changes and give credit where credit is due.
This also applies to bugs and any instances of lag being caused by this script. Please let me know if you encounter either of these!
Instructions
•Drop in ServerScriptService
•Edit any necessary settings that you’d like in the SunrayIntensityController.Settings
module and the SunrayIntensityController.Presets
module
•Edit any necessary default settings in the “SunrayIntensitySystem” server script’s attributes section.
-For custom skyboxes, replace the skies in the CustomSkies
folder in the Settings
module.
-Daytime skies MUST be named “DaySky” and nighttime “NightSky” in order for the system to successfully locate and differentiate between each sky. (See attached photo)
The above is no longer necessary. As the system now checks for existing skyboxes and uses the Roblox default night skybox when transitioning to night-mode. (Editable within code)
Notes
•In the beginning of March (2023), I created version 1.0. This version will always be available. However, due to the massive changes this version made to the system, I decided to keep version 1.0 around but I won’t be updating it. With the exception of bug fixes.
-The DevForum post for version 1.0 can be found here:
[Lighting] Sunray Intensity Compensation
Test Experience, free to test!
Comparison
Late this summer I caught myself up in Dying Light 2. I saw what they did with their sun rays, and it serves as a great example to what my system achieves.
↑ Dying Light 2
↓ Jeebus’ SICS
Showcase Video
Excuse the UI designs I used, I was testing out UI stuff I don’t typically play with… I’m also not a UI designer.
As of writing this (10/10/2023) I’ve noticed this video is of a very early build. I will have new things to showcase relatively soon. However, the full experience can still be tested in the above test place.
Get SICS here: https://www.roblox.com/library/13400931193/V2-Sunray-Intensity-Compensation-System
Get the plugin: https://www.roblox.com/library/14103372000/Sunray-Intensity-Compensation-Plugin
This plugin has since been taken off sale due to being outdated as well as the recent change from Robux to USD sales for plugins.
API
Use :GetSetting() to easily grab an updated attribute from the settings module.
-- Get the value for any setting that's part of the root script's attribute list
local exampleSetting = SettingsModule:GetSetting("SettingName" : string) : Any
:GetSky() is just that, a function that returns the sky. As unnecessary as this function is, it’s mainly just used to ensure we’re looking at the correct sun in the sky (in the case there are multiple).
-- This will return the current Sky being used
local sky = SettingsModule:GetSky()
Use :UpdateNightStatus(boolean) to switch between night-mode and normal day rays. This function tweens the time to a point at which the sun is no longer visible and the sky is Roblox’s night sky. It is then switched with a nighttime skybox and the time is set back up to daytime, but with a moon as the sun’s image ID. This allows us to have the moon give off light rays “during the night”.
--[[
This function enables/disables Night Mode. When activated,
The time of day spans quickly through a single night into the next morning. However, halfway through the change,
the custom Sky that is in the Settings module is replaced with the current CustomDaySky in game.Lighting.
-The CustomDaySky's Moon is disabled to not show its moon during the transition.
-After the "fake night" sky has been applied, the sun texture is replaced with a moon, and it is still daytime. Just with a night-time skybox
and a moon texture for the sun. This gives the impression of 'moon rays'.
]]
require(SettingsModule):UpdateNightStatus(isNight : boolean)
Source Code (May be outdated)
There are four total sections to this system. The root server script, this is where the default values will be set. You can set these values via the root script’s attributes section in the properties window.
(Note: These attributes are copied from this server script to every player when they join. Each player can then update their own individual client-side values via SettingsModule:SetSetting(attributeName : string, newValue : any).
Server Script Source
local Players = game:GetService("Players")
local InsertService = game:GetService("InsertService")
local MainController = script:FindFirstChild("BetaSunrayIntensityController") or script:WaitForChild("SunrayIntensityController",5)
local SICSCoreId = 15024209529
local NativeUIAssetID = 15038123726
local potentialNewCore
local potentialNewNativeUI
local SICS_Server_Version = "2.4"
local autoUpdateDevSettings = {
skipUpdate = false;
currentRetry = 0;
currentNativeUIRetry = 0;
maxRetries = 4;
}
function addCoreAttributes(target)
for attribute, value in pairs(script:GetAttributes()) do
target:SetAttribute(attribute, value)
end
MainController:SetAttribute("SICS_Server_Version", SICS_Server_Version)
end
function addCoreTags(target)
for i, tagName in pairs(script:GetTags()) do
target:AddTag(tagName)
end
end
function addNewCoreClone(plr)
local clonedSystem = MainController:Clone()
clonedSystem.Parent = (plr.Character or plr.CharacterAdded:Wait())
clonedSystem.Enabled = true
clonedSystem:SetAttribute("SICS_Server_Version", SICS_Server_Version)
--// Updates from client-side system
clonedSystem.DataStore.UpdateData.OnServerEvent:Connect(function(plr, dataName, dataValue)
if not plr or not plr.SICS or not plr.SICS:FindFirstChild(dataName) then return warn("Data location not found") end
plr.SICS[dataName].Value = dataValue
end)
end
function getUISavedData(plr)
local DataStore2 = require(1936396537)
local Settings = require(MainController.DataStore)
for dataName,valueTable in pairs(Settings) do
local datastore = DataStore2(dataName, plr)
local where = valueTable.Where
if valueTable.Where ~= "Player" then
if plr:findFirstChild(valueTable.Where) then
where = plr[valueTable.Where]
else
local folder = Instance.new("Folder", plr)
folder.Name = valueTable.Where
where = folder
end
end
if valueTable.Where == "Player" then
where = plr
end
--// Creates the Value
local val = Instance.new(valueTable.What,where)
val.Name = dataName
val.Value = valueTable.Value
--// Loading
if datastore:Get() ~= nil then -- If datastore already exists
val.Value = datastore:Get() -- loads in player's data
require(MainController.Settings):SetSetting(dataName, datastore:Get()) -- Update attribute value
end
--// Saving
val.Changed:connect(function() -- if Value changes
datastore:Set(val.Value) -- Sets Datastore value to changed Value
end)
end
end
function insertBackupUI(plr)
potentialNewNativeUI = script:WaitForChild("SICSPanel",10)
potentialNewNativeUI.Enabled = false
potentialNewNativeUI:FindFirstChildOfClass("Frame").Size = UDim2.new(0,0,1,0)
potentialNewNativeUI.Parent = plr.PlayerGui
end
function addNativeUI(plr)
if autoUpdateDevSettings.currentNativeUIRetry > autoUpdateDevSettings.maxRetries then insertBackupUI(plr) return warn("SICS max retries exceeded! Relying on last still existing system.") end
local plrGui = plr:WaitForChild("PlayerGui",10)
local Success, result = pcall(InsertService.LoadAsset, InsertService, NativeUIAssetID)
if Success and result then
potentialNewNativeUI = result:WaitForChild("SICSPanel",10)
potentialNewNativeUI.Enabled = false
potentialNewNativeUI:FindFirstChildOfClass("Frame").Size = UDim2.new(0,0,1,0)
potentialNewNativeUI.Parent = plrGui
result:Destroy()
warn("Initilized the latest native SICS UI")
return getUISavedData(plr)
else
autoUpdateDevSettings.currentNativeUIRetry += 1
warn("Failed to fetch the SICS UI! Retrying...")
task.wait(1)
end
task.wait()
return checkForUpdate()
end
function addToExistingPlayers(playerList)
for i,plr in pairs(playerList) do
if plr then
addNewCoreClone(plr)
addNativeUI(plr)
end
end
end
function addKeybinds(targetModule)
-- Setup custom keybinds
if script:GetAttribute("AllowNativeUI") then
local KeybindModule = require(MainController.Keybinds)
for attribute,value in pairs(script:GetAttributes()) do
if string.find(attribute, "Bind") then
MainController.Keybinds:SetAttribute(attribute, value)
end
end
end
end
function checkForTestExperience()
-- Test Experience SICSNativeUI ID - 15038856671
if game.GameId == 4664865401 or game.PlaceId == 13400902755 then
NativeUIAssetID = 15038856671
end
end
function checkForUpdate()
if autoUpdateDevSettings.currentRetry > autoUpdateDevSettings.maxRetries then return warn("SICS max retries exceeded! Relying on last still existing system.") end
local Success, result = pcall(InsertService.LoadAsset, InsertService, SICSCoreId)
if Success and result then
potentialNewCore = result:WaitForChild("SunrayIntensityController",10)
MainController = potentialNewCore
script.SunrayIntensityController:Destroy()
potentialNewCore.Parent = script
result:Destroy()
-- Update main core values to new defaults
addCoreAttributes(MainController.Settings)
addCoreTags(MainController.Settings)
addKeybinds(MainController.Keybinds)
-- Fallback to add new core to existing players (as the following PlayerAdded listener sometimes isn't reached in time for the update process to finish)
addToExistingPlayers(Players:GetPlayers())
warn("Initilized the latest version of SICS")
return
else
autoUpdateDevSettings.currentRetry += 1
warn("Failed to fetch the newest version of SICS! Retrying...")
task.wait(1)
end
task.wait()
return checkForUpdate()
end
-- Set native UI ID regardless
checkForTestExperience()
if script:GetAttribute("_AutoUpdate_") and not autoUpdateDevSettings.skipUpdate then
checkForUpdate()
else
-- Update main core values to new defaults
addCoreAttributes(MainController.Settings)
addCoreTags(MainController.Settings)
addKeybinds(MainController.Keybinds)
end
Players.PlayerAdded:Connect(function(plr)
plr.CharacterAdded:Connect(function(char)
addNewCoreClone(plr)
if script:GetAttribute("AllowNativeUI") then
addNativeUI(plr)
end
end)
end)
local SystemInformationModule = require(15031763998) -- 13383585291 - SICS Client Version
local updateStatus,checkedVersion,latestRelease = SystemInformationModule.GetSystemVersion(MainController.Name, SICS_Server_Version)
local UpdateMessage = SystemInformationModule.GetVersionMessage(updateStatus)
if updateStatus == "Outdated" then
warn(UpdateMessage, "Your version: "..checkedVersion, "|| Latest release: "..latestRelease)
--[[
else
print(UpdateMessage, "Your version: "..checkedVersion, "|| Latest release: "..latestRelease)
]]
end
Client Core
local Lighting = game:GetService("Lighting")
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local RepStorage = game:GetService("ReplicatedStorage")
local UIS = game:GetService("UserInputService")
local CollectionService = game:GetService("CollectionService")
local StarterGui = game:GetService("StarterGui")
local plr = Players.LocalPlayer
local CurrentCamera = workspace.CurrentCamera
local ConfigModule = script:WaitForChild("Settings",10)
local PresetsModule = script:WaitForChild("Presets",10)
local InputFunctionsModule = script:WaitForChild("InputFunctions",10)
local SunrayConfig = require(ConfigModule)
local SunrayPresets = require(PresetsModule)
local InputFunctions = require(InputFunctionsModule)
-------------------------------------------------------------------
--Fake sun stuff to track objects obscuring the player's view of the sun
local distFromCam = 500;
local SunPositionPart
--Essential Sky Assets and Values
local NearSunrays
local FarSunrays
local CurrentAngle
local Sky
local Forgiveness
local ForgivenessRadius
--Device Info
local isKeyboard
local isMobile
local isGamepad
--Debug Options
local enableDebugMessages = false
local function printDebug(msgString,optional1,optional2,optional3)
if enableDebugMessages then
if optional1 then
print(msgString,optional1)
elseif optional2 then
print(msgString,optional1,optional2)
elseif optional3 then
print(msgString,optional1,optional2,optional3)
else
print(msgString)
end
end
end
if UIS.KeyboardEnabled and UIS.GamepadEnabled then
isKeyboard = true
isMobile = false
isGamepad = true
elseif UIS.KeyboardEnabled and not UIS.GamepadEnabled then
isKeyboard = true
isMobile = false
isGamepad = false
elseif UIS.TouchEnabled and not UIS.GamepadEnabled then
isKeyboard = false
isMobile = true
isGamepad = false
elseif UIS.TouchEnabled and UIS.GamepadEnabled then
isKeyboard = false
isMobile = true
isGamepad = true
end
--Create new fake sun part (located far away in the direction of the sun)
local function NewSunPart()
if game.Workspace.CurrentCamera:FindFirstChild("SunPositionBrick") then
game.Workspace.CurrentCamera.SunPositionBrick:Destroy() --Create a new fake sun in case the old one bugged out for any reason
end
local part = Instance.new("Part")
part.Name = "SunPositionBrick"
part.Anchored = true
part.Material = Enum.Material.ForceField
part.Reflectance = 1000
part.Color = Color3.fromRGB(255,255,255)
part.Size = Vector3.new() * SunrayConfig:GetSky().SunAngularSize
part.Shape = Enum.PartType.Ball
part.CastShadow = false
part.Parent = workspace.CurrentCamera
part.CanCollide = false
SunPositionPart = part
end
NewSunPart()
------------------------------------------------------------------
repeat task.wait() Sky = SunrayConfig:GetSky() until SunrayConfig:GetSky() ~= nil
script.Parent = plr.PlayerScripts
local function getIgnoredParts()
local ignoreList = {plr.Character,SunPositionPart}
for i,tag in pairs(ConfigModule:GetTags()) do
for i,taggedInstance in pairs(CollectionService:GetTagged(tag)) do
table.insert(ignoreList,taggedInstance)
end
end
return ignoreList
end
local function SunToCamera(Character) --Is sun on screen and unobstructed?
local _, OnScreen = workspace.CurrentCamera:WorldToScreenPoint(SunPositionPart.Position)
local ignoreList = getIgnoredParts()
local PlayerRoot = Character:WaitForChild("HumanoidRootPart",3)
local rayStartPart = CurrentCamera
local rayfinishPart = SunPositionPart
local rayStartPosition = rayStartPart.CFrame.Position
local rayDestination = rayfinishPart.Position
local rayDirection = rayDestination - rayStartPosition
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = {ignoreList}
raycastParams.IgnoreWater = true
if SunrayConfig:GetSetting("IgnoreTerrain") then
raycastParams:AddToFilter(workspace.Terrain)
end
local NumOfObstructions = #workspace.CurrentCamera:GetPartsObscuringTarget({workspace.CurrentCamera.CFrame.Position,SunPositionPart.Position},ignoreList)
local result = workspace:Raycast(rayStartPosition, rayDirection * (rayStartPosition - rayDestination).magnitude,raycastParams)
if not SunrayConfig:GetSetting("IgnoreTerrain") then --Terrain Obstruction
if result and result.Material ~= nil then
return "OSO"
end
end
if OnScreen then
if NumOfObstructions == 0 then
return "OSU" -- On-screen & unobstructed
else
return "OSO" -- On-screen & obstructed
end
else
return "OFS" --Sun is off-screen
end
end
local function RoundNumber(NumberToRound,DecimalPlaces)
if NumberToRound == nil then
return
end
local Multiplier = 10^(DecimalPlaces or 2)
local Number = math.floor(NumberToRound*Multiplier)/Multiplier
return Number
end
local function getForgivenessRadius(currentForgiveness)
local radius = RoundNumber(math.cos(math.rad(currentForgiveness)))
return radius
end
local HorizonRadius = RoundNumber(math.cos(math.rad(90)))
local function SetSunPosition()
SunPositionPart.Size = Vector3.new() * SunrayConfig:GetSky().SunAngularSize
SunPositionPart.Position = workspace.CurrentCamera.CFrame.Position + Lighting:GetSunDirection() * distFromCam;
end
--Get Preset TweenInfo
local function GetPresetInfo()
local ChosenPreset
if SunrayConfig:GetSetting("ActivePreset") == nil or
SunrayConfig:GetSetting("ActivePreset") == "" or
string.lower(SunrayConfig:GetSetting("ActivePreset")) == "default" then
ChosenPreset = SunrayPresets.default
return ChosenPreset
elseif string.lower(SunrayConfig:GetSetting("ActivePreset")) == "custom" then
ChosenPreset = SunrayPresets.custom
return ChosenPreset
else
ChosenPreset = SunrayPresets[string.lower(SunrayConfig:GetSetting("ActivePreset"))]
return ChosenPreset
end
end
function CreateSunrayEffect()
for i,v in pairs(Lighting:GetChildren()) do
if v:IsA("SunRaysEffect") then
v:Destroy()
end
end
--Add a new SunRaysEffect if one doesn't already exist
local NewNearSunrays = Instance.new("SunRaysEffect",Lighting)
NewNearSunrays.Name = "NearSunRays"
NewNearSunrays.Intensity = 0.085
NewNearSunrays.Spread = 0.1
NearSunrays = NewNearSunrays
local NewFarSunrays = Instance.new("SunRaysEffect",Lighting)
NewFarSunrays.Name = "FarSunRays"
NewFarSunrays.Intensity = SunrayConfig:GetSetting("NearSunRaysIntensity")
NewFarSunrays.Spread = SunrayConfig:GetSetting("DefaultSpread")
FarSunrays = NewFarSunrays
end
CreateSunrayEffect()
SunrayConfig:SetSetting("Forgiveness", Sky.SunAngularSize)
--Get the sun angle relative to the camera and return the result
local function RoundAngle(Value,decimalPlaces)
local CurrentAngle = Value
local Result = CurrentAngle
local mult = 10^(decimalPlaces or 2)
Result = math.floor(Result*mult) / mult
return Result
end
--This function will do the actual updating of the sunray intensity. Prior calculations are done by a function located below
local function updateIntensity(newIntensityValue,isObstructed)
if SunrayConfig.isNightActive then
FarSunrays.Intensity = 0.02
NearSunrays.Intensity = 0.025
return
else
if NearSunrays.Intensity ~= SunrayConfig:GetSetting("NearSunRaysIntensity") then
NearSunrays.Intensity = SunrayConfig:GetSetting("NearSunRaysIntensity")
end
end
if string.lower(SunrayConfig:GetSetting("ActivePreset")) == "instant" then
FarSunrays.Intensity = newIntensityValue - (newIntensityValue * SunrayConfig:GetSetting("Dampening"))
else
if isObstructed then
local TweenIntensity = TweenService:Create(FarSunrays,SunrayConfig.ObstructedTweenInfo,{Intensity = newIntensityValue - (newIntensityValue * SunrayConfig:GetSetting("Dampening"))})
TweenIntensity:Play()
else
local TweenIntensity = TweenService:Create(FarSunrays,GetPresetInfo(),{Intensity = newIntensityValue - (newIntensityValue * SunrayConfig:GetSetting("Dampening"))})
TweenIntensity:Play()
end
end
end
--Double check wait for sunray creation
if FarSunrays == nil or NearSunrays == nil then
repeat
CreateSunrayEffect()
task.wait(1)
until Lighting[FarSunrays] ~= nil and Lighting[NearSunrays] ~= nil
end
--[[
Every heartbeat this script checks the camera direction relative
to the current position of the sun on the client
]]
local function tweakSunray()
-- Make sure SunPositionPart (Fake sun, a physical object) exists
if SunPositionPart == nil then
repeat NewSunPart() task.wait(0.5) until SunPositionPart ~= nil
end
SetSunPosition() --This should always update first after the fake sun is confirmed to exist
Forgiveness = SunrayConfig:GetSetting("Forgiveness")
ForgivenessRadius = RoundNumber(math.cos(math.rad(Forgiveness)))
local dirSun = Lighting:GetSunDirection()
local dirCamera = CurrentCamera.CFrame.LookVector
CurrentAngle = RoundAngle(dirSun:Dot(dirCamera))
local CurrentAngleInversed = RoundNumber((CurrentAngle * -1))
local RoundedDirectIntensity = RoundNumber(CurrentAngleInversed+(1-ForgivenessRadius)+ForgivenessRadius) * 2
local chr = plr.Character or plr.CharacterAdded:Wait()
local sunObstructed = SunToCamera(chr)
FarSunrays.Spread = SunrayConfig:GetSetting("DefaultSpread")
--Handle SunRaysEffect Intensity
if Lighting.ClockTime >= 6 and Lighting.ClockTime < 18 then --Must be daytime for this to take effect
if CurrentAngle >= HorizonRadius then -- (If within the horizon radius)
if sunObstructed == "OSO" then -- OSU = On-screen & unobstructed || OSO = On-screen & obstructed || OFS = Sun is off-screen
updateIntensity(SunrayConfig:GetSetting("IntensityObstructed"),true)
elseif CurrentAngle >= ForgivenessRadius and sunObstructed == "OSU" then --Within forgiveness radius
if Forgiveness >= 30 then
updateIntensity((RoundNumber((-1 * (CurrentAngleInversed+(1-ForgivenessRadius)) - 1) * -1,2)),false)
elseif Forgiveness <= 30 and Forgiveness >= 22 then
updateIntensity((RoundNumber((-1 * (CurrentAngleInversed+(1-ForgivenessRadius)) - 1) * -1,2)),false)
elseif Forgiveness < 22 then
updateIntensity(1-ForgivenessRadius,false)
end
elseif CurrentAngle < ForgivenessRadius and CurrentAngle >= HorizonRadius then --Within horizon radius, but sun is off-screen
if Forgiveness >= 30 then
updateIntensity((RoundNumber((-1 * (CurrentAngleInversed+(1-ForgivenessRadius)) - 1) * -1,2)),false)
elseif Forgiveness <= 30 and Forgiveness >= 22 then
updateIntensity(RoundedDirectIntensity-(ForgivenessRadius/20),false)
elseif Forgiveness < 22 then
updateIntensity(RoundedDirectIntensity-(ForgivenessRadius/20),false)
end
end
else --Outside the horizon radius (sun should be totally off-screen)
if CurrentAngle < HorizonRadius then
updateIntensity(RoundedDirectIntensity,false)
end
end
else --Night time
if FarSunrays.Intensity ~= SunrayConfig:GetSetting("IntensityOffScreen") then
updateIntensity(SunrayConfig:GetSetting("IntensityOffScreen"),false)
end
end
end
RunService.Heartbeat:Connect(tweakSunray)
------------------------------------------------------------------
-- Update forgiveness if sun size changes
Lighting:FindFirstChildOfClass("Sky"):GetPropertyChangedSignal("SunAngularSize"):Connect(function()
SunrayConfig:SetSetting("Forgiveness", Sky.SunAngularSize)
end)
------------------------------------------------------------------
------------------------------------------------------------------
------------------------------------------------------------------
------------------------------------------------------------------
--Native UI Handler
if ConfigModule:GetAttribute("AllowNativeUI") == true then
repeat task.wait() until plr.PlayerGui:FindFirstChild("SICSPanel")
local KeybindsModule = script:WaitForChild("Keybinds",10)
local Keybinds = require(KeybindsModule)
local OpenPhaseSize = UDim2.new(1,0,1,0)
local ClosePhaseSize = UDim2.new(0,0,1,0)
local plrGui = plr:WaitForChild("PlayerGui")
local TransitionTime = 0.5
local TopBar = require(script:WaitForChild("TopBar",10))
local MarketplaceService = game:GetService("MarketplaceService")
local SICSPanelToggler
local SICSPanelUI = plrGui.SICSPanel
local BackgroundFrame = SICSPanelUI:WaitForChild("Background",10)
local LowerFrame = BackgroundFrame:WaitForChild("LowerFrame",10)
local HousingFrame = LowerFrame:WaitForChild("Housing",10)
local Credit = HousingFrame:WaitForChild("Credit",10)
local ButtonFrame = HousingFrame:WaitForChild("ButtonFrame",10)
local CloseMenuButton = ButtonFrame:WaitForChild("CloseMenu",10)
local GetSICSButton = ButtonFrame:WaitForChild("GetSystem",10)
local function setKeybindTip(keyList)
local tip = "SICS".." ["
for i=1,#keyList do
tip = tip..keyList[i].Name
if i == #keyList then tip = tip.."]" else tip = tip.." + " end
end
SliderToggler:setTip(tip)
end
if script:GetAttribute("SICS_Server_Version") and tonumber(script:GetAttribute("SICS_Server_Version")) >= 2.3 then
local SICSGlobalData = plr:WaitForChild("SICS",10)
for i,dataName in pairs(SICSGlobalData:GetChildren()) do
SunrayConfig:SetSetting(dataName.Name, dataName.Value)
dataName:GetPropertyChangedSignal("Value"):Once(function()
SunrayConfig:SetSetting(dataName.Name, dataName.Value)
end)
end
SICSGlobalData.ChildAdded:Connect(function(dataName)
SunrayConfig:SetSetting(dataName.Name, dataName.Value)
dataName:GetPropertyChangedSignal("Value"):Once(function()
SunrayConfig:SetSetting(dataName.Name, dataName.Value)
end)
end)
Keybinds:InitializeBinds() -- Setup Keybinds
-------
end
if SliderToggler == nil then
SliderToggler = TopBar.new()
:autoDeselect(false)
:setName("SliderToggler")
:setImage(1068088395)
if string.find(InputFunctions.getInputType(),"Keyboard") or InputFunctions.getInputType == "Gamepad" then
local inputType = InputFunctions.getInputType()
inputType = (string.gsub(inputType,"InputType_",""))
setKeybindTip(Keybinds.NativeMenuKeybind[inputType])
end
end
-- Phase Functions
local function OpenPhase()
BackgroundFrame:TweenSize(OpenPhaseSize,Enum.EasingDirection.InOut,Enum.EasingStyle.Quart,TransitionTime,true)
end
local function ClosePhase()
BackgroundFrame:TweenSize(ClosePhaseSize,Enum.EasingDirection.InOut,Enum.EasingStyle.Quart,TransitionTime,true)
end
local enabledCoreGui = {}
-- Event Connections
local CoreGuiEnums = {
Enum.CoreGuiType.Backpack,
Enum.CoreGuiType.Health,
Enum.CoreGuiType.PlayerList,
Enum.CoreGuiType.Chat,
Enum.CoreGuiType.EmotesMenu,
}
local function setCoreUIEnabled(isGuiEnabled)
if isGuiEnabled and #enabledCoreGui > 0 then
for i,v in pairs(enabledCoreGui) do
StarterGui:SetCoreGuiEnabled(v,isGuiEnabled)
end
else
for i,v in pairs(CoreGuiEnums) do
StarterGui:SetCoreGuiEnabled(v,isGuiEnabled)
end
end
end
local function pressingRequiredKeys(requiredKeys)
for i,key in pairs(requiredKeys) do
if not table.find(InputFunctions.getKeysPressed(),key) then return false end
end
return true
end
ConfigModule.AttributeChanged:Connect(function(attribute)
--print(attribute,"changed values to", ConfigModule:GetAttribute(attribute))
script.DataStore.UpdateData:FireServer(attribute, ConfigModule:GetAttribute(attribute))
end)
UIS.InputBegan:Connect(function(input, isTyping)
local inputType = InputFunctions.getInputType()
inputType = (string.gsub(inputType,"InputType_",""))
if pressingRequiredKeys(Keybinds.NativeMenuKeybind[inputType]) and (not UIS:GetFocusedTextBox() and not isTyping) then
local selectedOrDeselectedString = SliderToggler:getToggleState()
if selectedOrDeselectedString == "selected" and not SliderToggler.locked then
SliderToggler:deselect()
SliderToggler:debounce(TransitionTime)
elseif selectedOrDeselectedString == "deselected" and not SliderToggler.locked then
SliderToggler:select()
SliderToggler:debounce(TransitionTime)
end
end
--if (InputFunctions.getKeysPressed() == Keybinds.NativeMenuKeybind.Keyboard or InputFunctions.getKeysPressed() == Keybinds.NativeMenuKeybind.Gamepad) and (not UIS:GetFocusedTextBox() and not isTyping) then
--end
end)
CloseMenuButton.Activated:Connect(function(inputObject)
SliderToggler:deselect()
SliderToggler:debounce(TransitionTime)
end)
GetSICSButton.Activated:Connect(function(inputObject)
MarketplaceService:PromptPurchase(plr, 13400931193, false)
end)
SliderToggler.selected:Connect(function()
SICSPanelUI.Enabled = true
table.clear(enabledCoreGui)
for i,coreUIEnum in pairs(CoreGuiEnums) do
if StarterGui:GetCoreGuiEnabled(coreUIEnum) then table.insert(enabledCoreGui, coreUIEnum) end
end
setCoreUIEnabled(false)
BackgroundFrame.Size = ClosePhaseSize
OpenPhase()
end)
SliderToggler.deselected:Connect(function()
ClosePhase()
task.wait(TransitionTime)
SICSPanelUI.Enabled = false
setCoreUIEnabled(true)
end)
for i,foundTextLabel in pairs(SICSPanelUI:GetDescendants()) do
if foundTextLabel and foundTextLabel:IsA("TextLabel") then
local newSizeConstraint = Instance.new("UITextSizeConstraint",foundTextLabel)
if isMobile then
newSizeConstraint.MaxTextSize = 28
else
newSizeConstraint.MaxTextSize = 40
end
newSizeConstraint.MinTextSize = 14
end
end
end
--[[
BELOW THIS TEXT IS TEST PLACE UI STUFF ---> (SAFE TO KEEP OR DELETE)
This updates the UI in my testing place. This will only check if the game is my test place via GameId.
Meaning, the below block of code is safe to stay or remove, it will not make any changes to your experiences or places.
]]
------------------------------------------------------------------
-- Test Experience UI Handler
if (game.GameId == 4664865401 or game.GameId == 4512400337) then
local TopBar = require(RepStorage:WaitForChild("TopBar",10))
local CoreDropdown
local RayInfoToggler
local nightToggler
local PresetDropdown
local isButtonSelected
local plrGui = plr:WaitForChild("PlayerGui")
local TestUI = plrGui:WaitForChild("AngleFromSun",4)
-------
local isKeyboard
local isMobile
local isGamepad
if UIS.KeyboardEnabled and UIS.GamepadEnabled then
isKeyboard = true
isMobile = false
isGamepad = true
elseif UIS.KeyboardEnabled and not UIS.GamepadEnabled then
isKeyboard = true
isMobile = false
isGamepad = false
elseif UIS.TouchEnabled and not UIS.GamepadEnabled then
isKeyboard = false
isMobile = true
isGamepad = false
elseif UIS.TouchEnabled and UIS.GamepadEnabled then
isKeyboard = false
isMobile = true
isGamepad = true
end
task.spawn(function()
if RayInfoToggler == nil then
RayInfoToggler = TopBar.new()
RayInfoToggler:autoDeselect(false)
RayInfoToggler:setName("RayInfoToggler")
RayInfoToggler:setLabel("Info")
RayInfoToggler:setTip("Toggle (Q)")
RayInfoToggler:select()
end
if nightToggler == nil then
nightToggler = TopBar.new()
nightToggler:autoDeselect(false)
nightToggler:setName("nightToggler")
nightToggler:setLabel("Night")
nightToggler:setTip("Toggle (V)")
end
if PresetDropdown == nil then
PresetDropdown = TopBar.new()
PresetDropdown:autoDeselect(true)
PresetDropdown:setName("PresetsDropdown")
PresetDropdown:setLabel("Presets")
PresetDropdown:set("dropdownSquareCorners", false)
PresetDropdown:setDropdown({
TopBar.new()
:setLabel("Default")
:setName("DefaultPreset")
:bindEvent("selected", function(self)
SunrayConfig:SetSetting("ActivePreset","Default")
end),
TopBar.new()
:setLabel("Realism")
:setName("RealismPreset")
:bindEvent("selected", function(self)
SunrayConfig:SetSetting("ActivePreset","Realism")
end)
:select(),
TopBar.new()
:setLabel("Cinematic")
:setName("CinematicPreset")
:bindEvent("selected", function(self)
SunrayConfig:SetSetting("ActivePreset","Cinematic")
end),
TopBar.new()
:setLabel("Custom")
:setName("CustomPreset")
:bindEvent("selected", function(self)
SunrayConfig:SetSetting("ActivePreset","Custom")
end),
TopBar.new()
:setLabel("Instant")
:setName("InstantPreset")
:bindEvent("selected", function(self)
SunrayConfig:SetSetting("ActivePreset","Instant")
end),
})
end
task.wait(2)
while true do
local character = plr.Character or plr.CharacterAdded:Wait()
isButtonSelected = RayInfoToggler.isSelected
if TestUI ~= nil then
local MainFrame = TestUI:WaitForChild("MainFrame")
local AngleLabel = MainFrame:WaitForChild("Angle")
local OuterForgivenessLabel = MainFrame:WaitForChild("ForgivenessRadius")
local IntensityLabel = MainFrame:WaitForChild("Intensity")
AngleLabel.Text = "Current Angle: "..tostring(RoundNumber(CurrentAngle,2))
IntensityLabel.Text = "Intensity: "..tostring(RoundNumber(FarSunrays.Intensity,2))
OuterForgivenessLabel.Text = "Forgiveness: "..tostring(RoundNumber(getForgivenessRadius(Forgiveness),2)).." ("..tostring(RoundNumber(Forgiveness,2))..")".."("..tostring(RoundNumber(ForgivenessRadius/6,2))..", "..tostring(RoundNumber(ForgivenessRadius/20,2))..")"
end
RunService.Heartbeat:Wait()
end
end)
UIS.InputBegan:Connect(function(input, gameProcessedEvent)
if input.KeyCode == Enum.KeyCode.Q and plr.Character:FindFirstChildOfClass("Humanoid").Health > 0 and not UIS:GetFocusedTextBox() then
if RayInfoToggler.isSelected then
RayInfoToggler:deselect()
else
RayInfoToggler:select()
end
elseif input.KeyCode == Enum.KeyCode.V and plr.Character:FindFirstChildOfClass("Humanoid").Health > 0 and not UIS:GetFocusedTextBox() then
if RayInfoToggler.isSelected then
RayInfoToggler:deselect()
else
RayInfoToggler:select()
end
end
end)
RayInfoToggler.selected:Connect(function()
TestUI.MainFrame.Visible = true
end)
RayInfoToggler.deselected:Connect(function()
TestUI.MainFrame.Visible = false
end)
nightToggler.selected:Connect(function()
SunrayConfig:UpdateNightStatus(true)
end)
nightToggler.deselected:Connect(function()
SunrayConfig:UpdateNightStatus(false)
end)
end
SettingsModule
local Lighting = game:GetService("Lighting")
local TweenService = game:GetService("TweenService")
local RepStorage = game:GetService("ReplicatedStorage")
local Sky
local SunrayPresets = require(script.Parent:WaitForChild("Presets",10))
local DaySky
local NightSky
local DaySkyBackup
MainConfig = {}
-- Stored Settings, Data, & System Information
MainConfig.isNightActive = false
MainConfig.NightSkyData = {
SunTextureId = "rbxassetid://9525441443";
SkyboxFaceLinks = "http://www.roblox.com/asset/?version=1&id=1014344"; --Every face on a default Roblox night sky is the same link, no need to list every one
SunAngularSize = 11;
}
-- Main Sky Setup
if Lighting:FindFirstChildOfClass("Sky") then
DaySky = Lighting:FindFirstChildOfClass("Sky")
else
DaySky = Instance.new("Sky",Lighting)
end
DaySkyBackup = DaySky:Clone()
-- Handle Bloom
local BloomEffect = Lighting:FindFirstChildOfClass("BloomEffect")
local originalBloomSize
local originalBloomThreshold
if not BloomEffect then
local bloom = Instance.new("BloomEffect",Lighting)
bloom.Size = 22
bloom.Threshold = 2.2
originalBloomSize = bloom.Size
originalBloomThreshold = bloom.Threshold
else
originalBloomSize = BloomEffect.Size
originalBloomThreshold = BloomEffect.Threshold
end
MainConfig.PresetList = { --If you add your own preset(s), add their name(s) to this list
--Add your customs starting here. Underneath the line below are necessary for main system functionality.
--"";
--[[
Side note:
Most functions that work with the presets rely on string.lower(). So please keep newly added
presets in this list all lowercase letters.
]]
-------------
"realism";
"cinematic";
"default";
"custom";
"instant"
};
----------------------------------------------------------------------------------------
MainConfig.ObstructedTweenInfo = TweenInfo.new(
5.21,
Enum.EasingStyle.Quart,
Enum.EasingDirection.Out
);
local sunShiftPhase1Info = TweenInfo.new(2.5,Enum.EasingStyle.Cubic,Enum.EasingDirection.In)
local sunShiftPhase2Info = TweenInfo.new(2.5,Enum.EasingStyle.Cubic,Enum.EasingDirection.Out)
function MainConfig:GetSky()
return DaySky
end
local function tweenBloom(bloomInstance : Instance, newBloomSize : number, newBloomThreshold : number)
local oldSize = bloomInstance.Size
local oldThreshold = bloomInstance.Threshold
local bloomTinfo = TweenInfo.new(1,Enum.EasingStyle.Sine,Enum.EasingDirection.Out)
local TweenBloom = TweenService:Create(bloomInstance,bloomTinfo,{Size = newBloomSize, Threshold = newBloomThreshold})
TweenBloom:Play()
end
function MainConfig:UpdateNightStatus(isNight)
if MainConfig.isNightActive and isNight == false then --Turn Day if it's night
local shiftPhase1
if Lighting.ClockTime >= 12 then
shiftPhase1 = TweenService:Create(Lighting,sunShiftPhase1Info,{ClockTime = 24})
else
shiftPhase1 = TweenService:Create(Lighting,sunShiftPhase1Info,{ClockTime = 0})
end
local shiftPhase2 = TweenService:Create(Lighting,sunShiftPhase2Info,{ClockTime = 8})
shiftPhase1:Play()
shiftPhase1.Completed:Connect(function(playbackState)
if playbackState == Enum.PlaybackState.Completed then
if Lighting:FindFirstChildOfClass("BloomEffect") then
local bloom = Lighting:FindFirstChildOfClass("BloomEffect")
tweenBloom(bloom,originalBloomSize,originalBloomThreshold)
end
MainConfig.isNightActive = false
DaySky:Destroy()
DaySky = DaySkyBackup:Clone()
DaySky.Parent = Lighting
shiftPhase2:Play()
end
end)
elseif not MainConfig.isNightActive and isNight == true then --Turn night if it's day
local shiftPhase1
if Lighting.ClockTime >= 12 then
shiftPhase1 = TweenService:Create(Lighting,sunShiftPhase1Info,{ClockTime = 24})
else
shiftPhase1 = TweenService:Create(Lighting,sunShiftPhase1Info,{ClockTime = 0})
end
local shiftPhase2 = TweenService:Create(Lighting,sunShiftPhase2Info,{ClockTime = 12})
shiftPhase1:Play()
DaySky.MoonAngularSize = 0
shiftPhase1.Completed:Connect(function(playbackState)
if playbackState == Enum.PlaybackState.Completed then
if Lighting:FindFirstChildOfClass("BloomEffect") then
local bloom = Lighting:FindFirstChildOfClass("BloomEffect")
tweenBloom(bloom,10,3.6)
end
MainConfig.isNightActive = true
DaySky.SkyboxBk = MainConfig.NightSkyData.SkyboxFaceLinks
DaySky.SkyboxDn = MainConfig.NightSkyData.SkyboxFaceLinks
DaySky.SkyboxFt = MainConfig.NightSkyData.SkyboxFaceLinks
DaySky.SkyboxLf = MainConfig.NightSkyData.SkyboxFaceLinks
DaySky.SkyboxRt = MainConfig.NightSkyData.SkyboxFaceLinks
DaySky.SkyboxUp = MainConfig.NightSkyData.SkyboxFaceLinks
DaySky.SunTextureId = MainConfig.NightSkyData.SunTextureId
DaySky.MoonAngularSize = DaySkyBackup.MoonAngularSize
DaySky.SunAngularSize = MainConfig.NightSkyData.SunAngularSize
shiftPhase2:Play()
end
end)
end
end
--// Forgiveness Radius \\--
MainConfig.Forgiveness = MainConfig:GetSky().SunAngularSize;
function MainConfig:UpdateForgiveness(newForgiveness : number)
if MainConfig.Forgiveness ~= newForgiveness then
MainConfig.Forgiveness = newForgiveness
end
end
function MainConfig:GetSetting(settingName)
if settingName == nil then return warn("Valid setting required") end
for attribute,value in pairs(script:GetAttributes()) do
if string.lower(attribute) == string.lower(settingName) then
return value
end
end
return warn("Setting", settingName, "not found")
end
function MainConfig:SetSetting(settingName, newValue)
for attributeName,value in pairs(script:GetAttributes()) do
if string.lower(attributeName) == string.lower(settingName) then
if string.lower(settingName) == "activepreset" and table.find(MainConfig.PresetList,string.lower(newValue)) then
script:SetAttribute(settingName, newValue)
return
elseif string.lower(settingName) == "activepreset" and not table.find(MainConfig.PresetList,string.lower(newValue)) then
return warn("Preset:", "'"..newValue.."'", "not found!")
end
script:SetAttribute(settingName, newValue)
return
end
end
return warn("Setting", "'"..settingName.."'", "not found!")
end
return MainConfig
PresetModule
local module = {
--// Preset Structures \\--
default = TweenInfo.new(
0.121,
Enum.EasingStyle.Sine,
Enum.EasingDirection.Out);
realism = TweenInfo.new(
0.067,
Enum.EasingStyle.Sine,
Enum.EasingDirection.Out
);
cinematic = TweenInfo.new(
0.652,
Enum.EasingStyle.Sine,
Enum.EasingDirection.Out);
--───────────────────────────────────────────────────────────────────────────────────────--
custom = TweenInfo.new( --Change this section if you set `SettingsModule.ActivePreset` to "Custom"
0.254, --Transition Time
Enum.EasingStyle.Sine, --Transition Style
Enum.EasingDirection.Out); --Transition Direction
}
return module
--[[
These are nothing but simple `TweenInfo.new()` configuration tables. Feel free to play with them
if you want to adjust the different presets!
]]
Disclaimer: This system has the option (enabled by default) to run functions directly using InsertService. If you do not want this system to do so, disable it by unchecking the “AutoUpdate” attribute of the root server script.