You can write your topic however you want, but you need to answer these questions:
-
What do you want to achieve? Make the sign, weather, and day/night update correctly again.
-
What is the issue? My original script worked but when I split into a module and a main, it now is failing to do the stuff from 1 above.
-
What solutions have you tried so far? I’'ve tried asking Gemini (and since I asked her to help me set up a moudle script, something I’d never done before, she likely introduced the error.) I believe it’s happening in the heartbeat and apply weather effects, but can’t seem to figure out what’s causing it.
Original Working script:
=================================================================================================
-- || WEATHER & REAL-TIME SYNC SCRIPT (FINAL, STABLE VERSION) ||
-- =================================================================================================
-- Services
local Lighting = game:GetService("Lighting")
local RunService = game:GetService("RunService")
local HttpService = game:GetService("HttpService")
local Http = game:GetService("HttpService")
local Players = game:GetService("Players")
local InsertService = game:GetService("InsertService")
-- Configuration Variables
local LATITUDE = 41.36 -- Oglesby, IL
local LONGITUDE = -89.04 -- Oglesby, IL
local TIMEZONE = "America/Chicago"
local API_ENDPOINT = "https://api.open-meteo.com/v1/forecast"
-- Weather Check Interval (How often to fetch new data, e.g., every 5 minutes)
local WEATHER_CHECK_INTERVAL = 5 * 60
-- Umbrella Configuration
local UMBRELLA_ASSET_ID = 13710882876
local UMBRELLA_HANDLE = nil -- Cache the Handle once it's loaded
-- Asset Definitions (Place these objects in Workspace/WeatherSource)
local WEATHER_SOURCE = workspace:WaitForChild("WeatherSource", 10)
local RAIN_EMITTER = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("RainEmitter")
local SNOW_EMITTER = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("SnowEmitter")
local HAIL_EMITTER = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("HailEmitter")
local RAIN_SOUND = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("RainSound")
local WIND_SOUND = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("WindSound")
local THUNDER_SOUND = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("ThunderSound")
local Terrain = game:GetService("Terrain")
local CLOUDS = workspace.Terrain:FindFirstChild("Clouds") -- Don't wait, just check
-- || FIXED SIGN CONFIGURATION FOR NEW STRUCTURE ||
-- ====================================================================
-- 1. Get the main container object (Model or Folder)
local WEATHER_SIGN_CONTAINER = workspace:FindFirstChild("WeatherSign")
-- 2. The SurfaceGui is now a DIRECT CHILD of the container, so we find it here.
local SIGN_GUI = WEATHER_SIGN_CONTAINER and WEATHER_SIGN_CONTAINER:FindFirstChildOfClass("SurfaceGui")
-- 3. Get the TextLabel object (which is a direct child of the SurfaceGui)
local WEATHER_TEXT_LABEL = SIGN_GUI and SIGN_GUI:FindFirstChildOfClass("TextLabel")
-- ====================================================================
-- Script State Trackers
local lastAppliedWeather = ""
local currentWeather = ""
-- =================================================================================================
-- || API INTERFACE & CORE WEATHER LOGIC ||
-- =================================================================================================
local function fetchWeatherCondition()
local url = string.format(
"%s?latitude=%f&longitude=%f&timezone=%s¤t=weather_code",
API_ENDPOINT, LATITUDE, LONGITUDE, TIMEZONE
)
local rawData = nil
local success, result = pcall(function()
rawData = Http:GetAsync(url)
return HttpService:JSONDecode(rawData)
end)
if not success or not rawData or not result or not result.current then
warn("API ERROR: Failed to fetch weather data. Defaulting to No Input.")
return "No Input"
end
local weatherCode = result.current.weather_code
local function getWeatherCondition(code)
-- ** UPDATED: Expanded logic based on WMO codes **
-- WMO Code 0: Clear Sky
if code == 0 then return "Clear"
elseif code == 3 then
return "Overcast"
elseif code == 2 then
return "Mostly Cloudy"
elseif code == 1 then
return "Partly Cloudy"
elseif code >= 45 and code <= 48 then return "Fog"
elseif code >= 50 and code <= 69 then return "Rain"
elseif code >= 70 and code <= 89 then return "Snow"
elseif code >= 90 then return "Thunderstorm"
else
return "Unknown"
end
end
return getWeatherCondition(weatherCode)
end
-- ** NEW: Function to update the sign's text **
local function updateWeatherSign(condition)
local textToShow = condition or "No Input"
if WEATHER_TEXT_LABEL then
WEATHER_TEXT_LABEL.Text = textToShow
warn("SIGN UPDATED: Showing '" .. textToShow .. "'")
else
warn("WARNING: Weather Text Label not found or configured incorrectly.")
end
end
local function applyWeatherEffects(weatherCondition)
-- Always update sign, even if weather didn't change
updateWeatherSign(weatherCondition)
-- Always update clouds, even if weather didn't change
if CLOUDS == nil then
warn("WARNING: Clouds object not found in Terrain!")
else
if weatherCondition == "Partly Cloudy" then
CLOUDS.Enabled = true
CLOUDS.Density = 0.2
CLOUDS.Cover = 0.4
CLOUDS.Color = Color3.fromRGB(220, 220, 220)
--Lighting.OutdoorAmbient = Color3.fromRGB(150, 150, 160)
--Lighting.GlobalShadows = true
elseif weatherCondition == "Mostly Cloudy" then
CLOUDS.Enabled = true
CLOUDS.Density = 0.5
CLOUDS.Cover = 0.7
CLOUDS.Color = Color3.fromRGB(180, 180, 190)
-- Lighting.OutdoorAmbient = Color3.fromRGB(120, 120, 130)
-- Lighting.GlobalShadows = false
elseif weatherCondition == "Overcast" then
CLOUDS.Enabled = true
CLOUDS.Density = 1.0
CLOUDS.Cover = 1.0
local currentTime = Lighting.ClockTime
local isNight = (currentTime >= 18.0) or (currentTime < 6.0)
if isNight then
CLOUDS.Color = Color3.fromRGB(50, 50, 60)
--Lighting.OutdoorAmbient = Color3.fromRGB(30, 30, 40)
--Lighting.Ambient = Color3.fromRGB(20, 20, 25)
--Lighting.ExposureCompensation = -2.0
--Lighting.Brightness = 0.1
else
CLOUDS.Color = Color3.fromRGB(150, 150, 160)
--Lighting.OutdoorAmbient = Color3.fromRGB(90, 90, 100)
--Lighting.Ambient = Color3.fromRGB(120, 120, 120)
--Lighting.ExposureCompensation = 0.0
end
Lighting.GlobalShadows = false
if Lighting:FindFirstChild("Sky") then
Lighting.Sky.CelestialBodiesShown = false
end
else
-- Not cloudy, reset clouds
CLOUDS.Enabled = false
-- Lighting.OutdoorAmbient = Color3.fromRGB(192, 192, 192)
-- Lighting.Ambient = Color3.fromRGB(128, 128, 128)
Lighting.GlobalShadows = true
if Lighting:FindFirstChild("Sky") then
Lighting.Sky.CelestialBodiesShown = true
end
end
end
-- Only reset emitters/sounds and log when weather changes
if weatherCondition ~= lastAppliedWeather and weatherCondition ~= "No Input" then
lastAppliedWeather = weatherCondition
warn("WEATHER CHANGE: New Condition: " .. weatherCondition)
if RAIN_EMITTER then RAIN_EMITTER.Enabled = false end
if SNOW_EMITTER then SNOW_EMITTER.Enabled = false end
if HAIL_EMITTER then HAIL_EMITTER.Enabled = false end
if RAIN_SOUND then RAIN_SOUND.Playing = false end
if WIND_SOUND then WIND_SOUND.Playing = false end
if THUNDER_SOUND then THUNDER_SOUND.Playing = false end
end
end
-- =================================================================================================
-- || MAIN CONTROL LOGIC (Instant Execution) ||
-- =================================================================================================
local function updateLightingTime()
local currentTimeUnix = os.time()
local hour = tonumber(os.date("%H", currentTimeUnix))
local minute = tonumber(os.date("%M", currentTimeUnix))
local second = tonumber(os.date("%S", currentTimeUnix))
Lighting.ClockTime = hour + (minute / 60) + (second / 3600)
end
-- 1. Initial Data Fetch (Run once immediately)
currentWeather = fetchWeatherCondition()
applyWeatherEffects(currentWeather)
warn("WEATHER INITIALIZED: Condition: " .. currentWeather)
-- 2. Polling loop for Time Sync and Weather Check
local lastCheckTime = 0
RunService.Heartbeat:Connect(function()
updateLightingTime()
local currentTime = os.time()
if currentTime - lastCheckTime >= WEATHER_CHECK_INTERVAL then
local newCondition = fetchWeatherCondition()
if newCondition then
currentWeather = newCondition
end
lastCheckTime = currentTime
warn("WEATHER CHECK: Refreshed data.")
end
applyWeatherEffects(currentWeather)
end)
print("Weather and Real-Time Sync script is now running.")
WeatherService (new one as a modulescript)
-- =================================================================================================
-- || WEATHER & REAL-TIME SYNC MODULESCRIPT (Complete) ||
-- =================================================================================================
-- 1. Define the module table that will be returned.
local WeatherService = {}
-- Services
local Lighting = game:GetService("Lighting")
local RunService = game:GetService("RunService")
local HttpService = game:GetService("HttpService")
local Players = game:GetService("Players")
local InsertService = game:GetService("InsertService")
local Terrain = game:GetService("Terrain")
-- ====================================================================
-- || CONFIGURATION (Publicly accessible) ||
-- ====================================================================
WeatherService.LATITUDE = 41.36 -- Oglesby, IL
WeatherService.LONGITUDE = -89.04 -- Oglesby, IL
WeatherService.TIMEZONE = "America/Chicago"
local API_ENDPOINT = "https://api.open-meteo.com/v1/forecast"
local WEATHER_CHECK_INTERVAL = 5 * 60 -- How often to fetch new data
-- Umbrella Configuration (Private)
local UMBRELLA_ASSET_ID = 13710882876
local UMBRELLA_HANDLE = nil
-- ====================================================================
-- || INITIALIZATION (Finding Assets) ||
-- ====================================================================
-- Note: Using WaitForChild ensures assets exist before module initializes
local WEATHER_SOURCE = workspace:WaitForChild("WeatherSource", 10)
local RAIN_EMITTER = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("RainEmitter")
local SNOW_EMITTER = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("SnowEmitter")
local HAIL_EMITTER = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("HailEmitter")
local RAIN_SOUND = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("RainSound")
local WIND_SOUND = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("WindSound")
local THUNDER_SOUND = WEATHER_SOURCE and WEATHER_SOURCE:FindFirstChild("ThunderSound")
local CLOUDS = Terrain:FindFirstChild("Clouds")
local WEATHER_SIGN_CONTAINER = workspace:FindFirstChild("WeatherSign")
local SIGN_GUI = WEATHER_SIGN_CONTAINER and WEATHER_SIGN_CONTAINER:FindFirstChildOfClass("SurfaceGui")
local WEATHER_TEXT_LABEL = SIGN_GUI and SIGN_GUI:FindFirstChildOfClass("TextLabel")
-- Script State Trackers (Private)
local lastAppliedWeather = ""
WeatherSerivce.currentWeather = ""
-- =================================================================================================
-- || PUBLIC FUNCTION: Fetch Weather (Can be called externally) ||
-- =================================================================================================
function WeatherService.fetchWeatherCondition()
-- 🟢 Inner Function (Helper: remains local/private)
local function getWeatherCondition(code)
-- WMO Code 0: Clear Sky
if code == 0 then return "Clear"
elseif code == 3 then return "Overcast"
elseif code == 2 then return "Mostly Cloudy"
elseif code == 1 then return "Partly Cloudy"
elseif code >= 45 and code <= 48 then return "Fog"
elseif code >= 50 and code <= 69 then return "Rain"
elseif code >= 70 and code <= 89 then return "Snow"
elseif code >= 90 then return "Thunderstorm"
else
return "Unknown"
end
end
-- API fetching logic
local url = string.format(
"%s?latitude=%f&longitude=%f&timezone=%s¤t=weather_code",
API_ENDPOINT, WeatherService.LATITUDE, WeatherService.LONGITUDE, WeatherService.TIMEZONE
)
local rawData = nil
local success, result = pcall(function()
rawData = HttpService:GetAsync(url)
return HttpService:JSONDecode(rawData)
end)
if not success or not rawData or not result or not result.current then
warn("API ERROR: Failed to fetch weather data. Defaulting to No Input.")
return "No Input"
end
local weatherCode = result.current.weather_code
-- Returns the result of the local helper function
return getWeatherCondition(weatherCode)
end
-- =================================================================================================
-- || PRIVATE HELPER: Update Sign ||
-- =================================================================================================
local function updateWeatherSign(condition)
local textToShow = condition or "No Input"
if WEATHER_TEXT_LABEL then
WEATHER_TEXT_LABEL.Text = textToShow
else
warn("WARNING: Weather Text Label not found or configured incorrectly.")
end
end
-- =================================================================================================
-- || PUBLIC FUNCTION: Apply Effects (Can be called externally) ||
-- =================================================================================================
function WeatherService.applyWeatherEffects(weatherCondition)
-- Update the module's private state tracker
WeatherService.currentWeather = weatherCondition
-- Always update sign
updateWeatherSign(weatherCondition)
-- Update clouds
if CLOUDS == nil then
warn("WARNING: Clouds object not found in Terrain!")
else
if weatherCondition == "Partly Cloudy" then
CLOUDS.Enabled = true
CLOUDS.Density = 0.2
CLOUDS.Cover = 0.4
CLOUDS.Color = Color3.fromRGB(220, 220, 220)
elseif weatherCondition == "Mostly Cloudy" then
CLOUDS.Enabled = true
CLOUDS.Density = 0.5
CLOUDS.Cover = 0.7
CLOUDS.Color = Color3.fromRGB(180, 180, 190)
elseif weatherCondition == "Overcast" then
CLOUDS.Enabled = true
CLOUDS.Density = 1.0
CLOUDS.Cover = 1.0
local currentTime = Lighting.ClockTime
local isNight = (currentTime >= 18.0) or (currentTime < 6.0)
if isNight then
CLOUDS.Color = Color3.fromRGB(50, 50, 60)
else
CLOUDS.Color = Color3.fromRGB(150, 150, 160)
end
Lighting.GlobalShadows = false
if Lighting:FindFirstChild("Sky") then
Lighting.Sky.CelestialBodiesShown = false
end
else
-- Not cloudy, reset clouds
CLOUDS.Enabled = false
Lighting.GlobalShadows = true
if Lighting:FindFirstChild("Sky") then
Lighting.Sky.CelestialBodiesShown = true
end
end
end
-- Only reset emitters/sounds and log when weather changes
if weatherCondition ~= lastAppliedWeather and weatherCondition ~= "No Input" then
lastAppliedWeather = weatherCondition
warn("WEATHER CHANGE: New Condition: " .. weatherCondition)
-- Turn everything OFF first
if RAIN_EMITTER then RAIN_EMITTER.Enabled = false end
if SNOW_EMITTER then SNOW_EMITTER.Enabled = false end
if HAIL_EMITTER then HAIL_EMITTER.Enabled = false end
if RAIN_SOUND then RAIN_SOUND.Playing = false end
if WIND_SOUND then WIND_SOUND.Playing = false end
if THUNDER_SOUND then THUNDER_SOUND.Playing = false end
-- Re-enable specific effects for the new weather
if weatherCondition == "Rain" then
if RAIN_EMITTER then RAIN_EMITTER.Enabled = true end
if RAIN_SOUND then RAIN_SOUND.Playing = true end
elseif weatherCondition == "Snow" then
if SNOW_EMITTER then SNOW_EMITTER.Enabled = true end
if WIND_SOUND then WIND_SOUND.Playing = true end
elseif weatherCondition == "Thunderstorm" then
if RAIN_EMITTER then RAIN_EMITTER.Enabled = true end
if THUNDER_SOUND then THUNDER_SOUND.Playing = true end
end
end
end
-- =================================================================================================
-- || PUBLIC FUNCTION: Start System (The main entry point) ||
-- =================================================================================================
-- =================================================================================================
-- || OTHER PUBLIC GETTERS ||
-- =================================================================================================
-- =================================================================================================
-- || MODULE RETURN STATEMENT ||
-- =================================================================================================
-- 2. Return the table, making all assigned functions and data public.
return WeatherService
New main
-- =================================================================================================
-- || MAIN CONTROL LOGIC (Instant Execution) ||
-- =================================================================================================
local WEATHER_CHECK_INTERVAL = 5 * 60
-- Services
local RunService = game:GetService("RunService")
local Lighting = game:GetService("Lighting")
-- Load the ModuleScript
local WeatherService = require(workspace.ServerScriptService.WeatherService)
local function updateLightingTime()
local currentTimeUnix = os.time()
local hour = tonumber(os.date("%H", currentTimeUnix))
local minute = tonumber(os.date("%M", currentTimeUnix))
local second = tonumber(os.date("%S", currentTimeUnix))
Lighting.ClockTime = hour + (minute / 60) + (second / 3600)
end
-- 1. Initial Data Fetch (Run once immediately)
currentWeather = WeatherService.fetchWeatherCondition()
WeatherService.applyWeatherEffects(currentWeather)
warn("WEATHER INITIALIZED: Condition: " .. currentWeather)
-- 2. Polling loop for Time Sync and Weather Check
local lastCheckTime = 0
RunService.Heartbeat:Connect(function()
updateLightingTime()
local currentTime = os.time()
if currentTime - lastCheckTime >= WEATHER_CHECK_INTERVAL then
local newCondition = WeatherService.fetchWeatherCondition()
if newCondition then
currentWeather = newCondition
end
lastCheckTime = currentTime
warn("WEATHER CHECK: Refreshed data.")
end
WeatherService.applyWeatherEffects(currentWeather)
end)
print("Weather and Real-Time Sync script is now running.")
I had a getCurrentWeather() function in the module but Gemini suggested that that was causing the problem and to change it to public and that that would fix it, but it didn’t.
I don’t believe that the error is anywhere outside the applyWeatherEffects() in the module or inside the Heartbeat in the main (though I could be wrong.)
Again, the top code, the longest one, IS working, but the two split ones (a module and a main) seem to be goofing up somewhre with applyweathereffects() and the Heartbeat.
My sign should be showing something like “Snow” or “Overcast” but it’s showing “Put Text Here” or whatever it said. The time should be a little after midnight but it’s about noon (the default).
So somthing is going wrong either in the heartbeat or apply weather effects. I don’t see how it could be anything else.
UPDATE----
Maybe this line is the cause. It’s the only thing that, if going wrong, could mess with everything:
local WeatherService = require(workspace.ServerScriptService.WeatherService)
UPDATE----
Fixed a typo in the module script and it’s still not working.
UPDATE—
Now checking into loading (i.e. it may be being operated upon before it’s loaded. Since part of it is now in a module, that could be the problem.)