Module:BassaridiaForecast: Difference between revisions
From MicrasWiki
Jump to navigationJump to search
NewZimiaGov (talk | contribs) No edit summary |
NewZimiaGov (talk | contribs) No edit summary |
||
| Line 1: | Line 1: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- Module:WeatherForecastTable | -- Module:WeatherForecastTable | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
| Line 56: | Line 47: | ||
-- 3. Weather Events (10 per climate-season) | -- 3. Weather Events (10 per climate-season) | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- (Same data as before — omitted here only for brevity in this comment, | |||
-- but you must paste the entire "climateEvents" table from your final code.) | |||
local climateEvents = { | local climateEvents = { | ||
| Line 96: | Line 90: | ||
} | } | ||
}, | }, | ||
-- ... plus "Oceanic," "Subpolar Oceanic," "Mediterranean (Hot Summer)," | |||
-- "Hot Desert," "Cold Steppe," "Hot Steppe," "Subarctic," each with | |||
-- 10 events per season. (Paste your full data here.) | |||
} | } | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 4. Temperature | -- 4. Temperature Ranges (climateTemperature) | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- (Paste your full "climateTemperature" table with hiMin, hiMax, etc.) | |||
local climateTemperature = { | local climateTemperature = { | ||
| Line 381: | Line 107: | ||
Opsitheiel = { hiMin=20, hiMax=28, loMin=12, loMax=18, humMin=50, humMax=75, crMin=15, crMax=40, rfMin=1, rfMax=8 } | Opsitheiel = { hiMin=20, hiMax=28, loMin=12, loMax=18, humMin=50, humMax=75, crMin=15, crMax=40, rfMin=1, rfMax=8 } | ||
}, | }, | ||
-- ... plus the rest for "Oceanic," "Subpolar Oceanic," etc. | |||
} | } | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 5. Color Coding | -- 5. Color-Coding Weather Description | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
local function getEventColor(eventText) | local function getEventColor(eventText) | ||
local textLower = eventText:lower() | local textLower = eventText:lower() | ||
-- Each type of event has a logical color: | |||
if textLower:find("thunder") or textLower:find("storm") then | if textLower:find("thunder") or textLower:find("storm") then | ||
return "# | return "#FFC0C0" -- a moderate red/pink for storms | ||
elseif textLower:find("snow") or textLower:find("sleet") or textLower:find("flurries") then | elseif textLower:find("snow") or textLower:find("sleet") or textLower:find("flurries") then | ||
return "# | return "#CCE6FF" -- a moderate blue for snowy | ||
elseif textLower:find("rain") or textLower:find("drizzle") or textLower:find("downpour") then | elseif textLower:find("rain") or textLower:find("drizzle") or textLower:find("downpour") then | ||
return "# | return "#CCEEFF" -- a soft aqua for rain | ||
elseif textLower:find("dust") or textLower:find("desert") then | elseif textLower:find("dust") or textLower:find("desert") then | ||
return "#FFFACD" -- lemon chiffon for dust | return "#FFFACD" -- lemon chiffon for dust | ||
elseif textLower:find("hail") then | elseif textLower:find("hail") then | ||
return "# | return "#E0FFFF" -- light cyan for hail | ||
else | else | ||
return "#F8F8F8" | return "#F8F8F8" -- default light gray | ||
end | end | ||
end | end | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 6. City-Specific | -- 6. Map Climate Name -> Background Color | ||
-- According to the table you provided: | |||
-- Cfa (Humid Subtropical) -> #FFE4C4 | |||
-- Cfb (Oceanic) -> #CCE5FF | |||
-- Cfc (Subpolar Oceanic) -> #CCFFFF | |||
-- Csa (Mediterranean, hot summer) -> #FFE4C4 | |||
-- BSh (Hot Steppe) -> #FFDFAF | |||
-- BWh (Hot Desert) -> #FFD1DC | |||
-- BSk (Cold Steppe) -> #FFECB3 | |||
-- Dfc (Subarctic) -> #CAB3FF | |||
-- | |||
-- We'll create a function that returns these based on the spelled-out name. | |||
--------------------------------------------------------- | |||
local function getClimateBGColor(climateName) | |||
-- We'll define a small map from spelled-out to the color in your table. | |||
-- e.g. if the spelled-out "Humid Subtropical" => "Cfa" => #FFE4C4 | |||
local map = { | |||
["Humid Subtropical"] = "#FFE4C4", -- cfa | |||
["Oceanic"] = "#CCE5FF", -- cfb | |||
["Subpolar Oceanic"] = "#CCFFFF", -- cfc | |||
["Mediterranean (Hot Summer)"]= "#FFE4C4", -- csa | |||
["Hot Steppe"] = "#FFDFAF", -- bsh | |||
["Hot Desert"] = "#FFD1DC", -- bwh | |||
["Cold Steppe"] = "#FFECB3", -- bsk | |||
["Subarctic"] = "#CAB3FF" -- dfc | |||
} | |||
return map[climateName] or "#F8F8F8" | |||
end | |||
--------------------------------------------------------- | |||
-- 7. City-Specific Disaster Profiles | |||
--------------------------------------------------------- | --------------------------------------------------------- | ||
| Line 485: | Line 209: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- | -- 8. Advisory Logic | ||
-- If triggered, color code cell in red | |||
--------------------------------------------------------- | --------------------------------------------------------- | ||
| Line 545: | Line 270: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- | -- 9. Random Weather Stats (including wind speed) | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
| Line 555: | Line 280: | ||
return { | return { | ||
highC="N/A", lowC="N/A", humidity="N/A", | highC="N/A", lowC="N/A", humidity="N/A", | ||
chanceOfRain="N/A", predictedRain="N/A", windDir="N/A", windSpeed="N/A" | chanceOfRain="N/A", predictedRain="N/A", | ||
windDir="N/A", windSpeed="N/A" | |||
} | } | ||
end | end | ||
| Line 563: | Line 289: | ||
local hum = math.random(data.humMin, data.humMax) | local hum = math.random(data.humMin, data.humMax) | ||
local cRain = math.random(data.crMin, data.crMax) | local cRain = math.random(data.crMin, data.crMax) | ||
local pRain = 0 | local pRain = 0 | ||
| Line 571: | Line 296: | ||
local wDir = windDirections[math.random(#windDirections)] | local wDir = windDirections[math.random(#windDirections)] | ||
local wSpd = math.random(0, 50) -- 0..50 km/h | |||
local wSpd = math.random(0, 50) | |||
return { | return { | ||
highC = hiC, | highC = hiC, | ||
lowC = loC, | lowC = loC, | ||
humidity = hum, | humidity = hum, | ||
chanceOfRain = cRain, | chanceOfRain = cRain, | ||
predictedRain = pRain, | predictedRain= pRain, | ||
windDir = wDir, | windDir = wDir, | ||
windSpeed = wSpd | windSpeed = wSpd | ||
} | } | ||
end | end | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- | -- 10. Celsius to Fahrenheit | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
| Line 594: | Line 318: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- | -- 11. Full City List | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
local cityData = { | local cityData = { | ||
{city = "Vaeringheim", climate = "Humid Subtropical"}, | {city = "Vaeringheim", climate = "Humid Subtropical"}, | ||
{city = "Luminaria", climate = "Humid Subtropical"}, | {city = "Luminaria", climate = "Humid Subtropical"}, | ||
| Line 610: | Line 333: | ||
{city = "Sylvapolis", climate = "Humid Subtropical"}, | {city = "Sylvapolis", climate = "Humid Subtropical"}, | ||
{city = "Saluria", climate = "Oceanic"}, | {city = "Saluria", climate = "Oceanic"}, | ||
{city = "Aetherium", climate = "Subarctic"}, | {city = "Aetherium", climate = "Subarctic"}, | ||
| Line 621: | Line 343: | ||
{city = "Aureum", climate = "Mediterranean (Hot Summer)"}, | {city = "Aureum", climate = "Mediterranean (Hot Summer)"}, | ||
{city = "Skýrophos", climate = "Oceanic"}, | {city = "Skýrophos", climate = "Oceanic"}, | ||
{city = "Bjornopolis", climate = "Oceanic"}, | {city = "Bjornopolis", climate = "Oceanic"}, | ||
| Line 628: | Line 349: | ||
{city = "Thorsalon", climate = "Oceanic"}, | {city = "Thorsalon", climate = "Oceanic"}, | ||
{city = "Pelagia", climate = "Hot Steppe"}, | {city = "Pelagia", climate = "Hot Steppe"}, | ||
{city = "Myrene", climate = "Oceanic"}, | {city = "Myrene", climate = "Oceanic"}, | ||
| Line 635: | Line 355: | ||
{city = "Halicarn", climate = "Mediterranean (Hot Summer)"}, | {city = "Halicarn", climate = "Mediterranean (Hot Summer)"}, | ||
{city = "Keybir-Aviv", climate = "Humid Subtropical"}, | {city = "Keybir-Aviv", climate = "Humid Subtropical"}, | ||
{city = "Tel-Amin", climate = "Mediterranean (Hot Summer)"}, | {city = "Tel-Amin", climate = "Mediterranean (Hot Summer)"}, | ||
| Line 642: | Line 361: | ||
{city = "Lewisburg", climate = "Humid Subtropical"}, | {city = "Lewisburg", climate = "Humid Subtropical"}, | ||
{city = "Thermosalem", climate = "Oceanic"}, | {city = "Thermosalem", climate = "Oceanic"}, | ||
{city = "Akróstadium", climate = "Cold Steppe"}, | {city = "Akróstadium", climate = "Cold Steppe"}, | ||
| Line 650: | Line 368: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- | -- 12. Main Weather Forecast Table Function | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
| Line 656: | Line 374: | ||
local dateInfo = getCurrentDateInfo() | local dateInfo = getCurrentDateInfo() | ||
local dayOfYear = dateInfo.dayOfYear | local dayOfYear = dateInfo.dayOfYear | ||
local | local yearNum = dateInfo.psscYear | ||
local seasonName= getSeasonName(dayOfYear) | local seasonName= getSeasonName(dayOfYear) | ||
local | local out = {} | ||
table.insert( | table.insert(out, "== Daily Weather Forecast ==\n") | ||
table.insert( | table.insert(out, string.format( | ||
"''(Day %d of Year %d PSSC, %s)''\n\n", | "''(Day %d of Year %d PSSC, %s)''\n\n", | ||
dayOfYear, | dayOfYear, yearNum, seasonName | ||
)) | )) | ||
-- Start table | -- Start table | ||
table.insert( | table.insert(out, '{| class="wikitable sortable" style="width:100%; text-align:left;"\n') | ||
table.insert( | table.insert(out, | ||
[[! City !! Climate !! Season !! High °C (°F) !! Low °C (°F) !! Humidity (%) !! Chance of Rain (%) !! Rainfall (mm) !! Wind Dir !! Wind Speed (km/h) !! Today's Weather !! Advisory | |||
]] | |||
) | ) | ||
| Line 677: | Line 396: | ||
local climateName = cityEntry.climate | local climateName = cityEntry.climate | ||
-- (A) | -- (A) Random event pick | ||
local | local cTable = climateEvents[climateName] | ||
local | local sTable = cTable and cTable[seasonName] | ||
local | local forecast= "No data" | ||
if | if sTable and #sTable > 0 then | ||
forecast = sTable[math.random(#sTable)] | |||
end | end | ||
-- (B) | -- (B) Weather cell color | ||
local | local weatherColor = getEventColor(forecast) | ||
-- (C) | -- (C) Stats | ||
local stats = getRandomWeatherStats(climateName, seasonName) | local stats = getRandomWeatherStats(climateName, seasonName) | ||
local | local hiC = stats.highC | ||
local | local loC = stats.lowC | ||
local hum | local hum = stats.humidity | ||
local cRain | local cRain = stats.chanceOfRain | ||
local pRain | local pRain = stats.predictedRain | ||
local wDir | local wDir = stats.windDir | ||
local wSpd | local wSpd = stats.windSpeed | ||
local | local hiF = (type(hiC)=="number") and cToF(hiC) or "N/A" | ||
local | local loF = (type(loC)=="number") and cToF(loC) or "N/A" | ||
-- (D) | -- (D) Advisory | ||
local advisory = getDisasterAdvisory( | local advisory = getDisasterAdvisory( | ||
cityName, | cityName, forecast, cRain, pRain, | ||
(type( | (type(hiC)=="number" and hiC or 0) | ||
) | |||
-- If there's an advisory, color it red | |||
local advisoryCell | |||
if advisory ~= "No Advisory" then | |||
advisoryCell = 'style="background-color:#FFBABA" | ' .. advisory | |||
else | |||
advisoryCell = advisory | |||
end | |||
-- (E) Climate cell color | |||
local climateBG = getClimateBGColor(climateName) | |||
local climateCell = string.format( | |||
'style="background-color:%s" | %s', | |||
climateBG, climateName | |||
) | ) | ||
table.insert( | table.insert(out, "|-\n") | ||
table.insert( | table.insert(out, string.format( | ||
[[| %s || %s || %s || %s (%s) || %s (%s) || %s || %s || %s || %s || %s || style="background-color:%s" | %s || %s | |||
cityName, | ]], | ||
tostring( | cityName, | ||
tostring( | climateCell, | ||
seasonName, | |||
tostring(hiC), tostring(hiF), | |||
tostring(loC), tostring(loF), | |||
tostring(hum), | tostring(hum), | ||
tostring(cRain), | tostring(cRain), | ||
| Line 718: | Line 453: | ||
wDir, | wDir, | ||
tostring(wSpd), | tostring(wSpd), | ||
weatherColor, forecast, | |||
advisoryCell | |||
)) | )) | ||
end | end | ||
table.insert( | table.insert(out, "|}\n") | ||
return table.concat( | return table.concat(out) | ||
end | end | ||
return p | return p | ||
Revision as of 03:47, 26 December 2024
Documentation for this module may be created at Module:BassaridiaForecast/doc
---------------------------------------------------------
-- Module:WeatherForecastTable
---------------------------------------------------------
local p = {}
---------------------------------------------------------
-- 1. Calendar System
---------------------------------------------------------
local function getCurrentDateInfo()
local startDate = os.time({year = 1999, month = 8, day = 6})
local secondsInDay = 86400
local daysPerYear = 183
local currentTime = os.time()
local totalDaysElapsed = math.floor((currentTime - startDate) / secondsInDay)
local yearFraction = totalDaysElapsed / daysPerYear
local psscYear = math.floor(yearFraction)
local dayOfYear = math.floor((yearFraction - psscYear) * daysPerYear) + 1
return {
psscYear = psscYear,
dayOfYear = dayOfYear
}
end
---------------------------------------------------------
-- 2. Determine the Season
-- 1..61 => Atosiel
-- 62..122 => Thalassiel
-- 123..183 => Opsitheiel
---------------------------------------------------------
local function getSeasonName(dayOfYear)
if dayOfYear <= 61 then
return "Atosiel"
elseif dayOfYear <= 122 then
return "Thalassiel"
else
return "Opsitheiel"
end
end
---------------------------------------------------------
-- 3. Weather Events (10 per climate-season)
---------------------------------------------------------
-- (Same data as before — omitted here only for brevity in this comment,
-- but you must paste the entire "climateEvents" table from your final code.)
local climateEvents = {
["Humid Subtropical"] = {
Atosiel = {
"Morning drizzle and warm afternoon sunshine",
"Mild thunderstorm building by midday",
"Patchy fog at dawn, clearing toward lunch",
"Light rain with sunny breaks after noon",
"Gentle breezes, blossoming warmth, low humidity",
"Scattered clouds with a brief shower by dusk",
"Humid sunrise, comfortable high around midday",
"Overcast for part of the day, mild temperatures",
"Warm breezes carrying faint floral scents",
"Partial sun, warm with a slight chance of rain"
},
Thalassiel = {
"Hot, steamy day with intense midday heat",
"Tropical-like humidity, afternoon thunder possible",
"Intermittent heavy downpours, muggy evenings",
"High humidity and patchy thunderstorms late",
"Sun-scorched morning, scattered storms by dusk",
"Hazy sunshine, extremely warm all day",
"Thick humidity, chance of lightning late evening",
"Sticky air, short downpours in isolated spots",
"Heat advisory with only brief cooling at night",
"Nighttime storms lingering into early morning"
},
Opsitheiel = {
"Warm daytime, gentle evening breezes",
"Occasional rain, otherwise mild temperatures",
"Late-season warmth, scattered rain after sunset",
"Cooler mornings, returning to warmth by midday",
"Sparse cloud cover, tranquil weather overall",
"Fog at dawn, warm midday, pleasant night",
"Partial sun, comfortable humidity levels",
"Evening drizzle with mild breezes",
"Patchy haze, moderate warmth throughout the day",
"Short-lived shower, then clearing skies"
}
},
-- ... plus "Oceanic," "Subpolar Oceanic," "Mediterranean (Hot Summer),"
-- "Hot Desert," "Cold Steppe," "Hot Steppe," "Subarctic," each with
-- 10 events per season. (Paste your full data here.)
}
---------------------------------------------------------
-- 4. Temperature Ranges (climateTemperature)
---------------------------------------------------------
-- (Paste your full "climateTemperature" table with hiMin, hiMax, etc.)
local climateTemperature = {
["Humid Subtropical"] = {
Atosiel = { hiMin=18, hiMax=26, loMin=10, loMax=16, humMin=60, humMax=80, crMin=20, crMax=50, rfMin=1, rfMax=10 },
Thalassiel = { hiMin=25, hiMax=34, loMin=19, loMax=24, humMin=65, humMax=90, crMin=30, crMax=70, rfMin=2, rfMax=15 },
Opsitheiel = { hiMin=20, hiMax=28, loMin=12, loMax=18, humMin=50, humMax=75, crMin=15, crMax=40, rfMin=1, rfMax=8 }
},
-- ... plus the rest for "Oceanic," "Subpolar Oceanic," etc.
}
---------------------------------------------------------
-- 5. Color-Coding Weather Description
---------------------------------------------------------
local function getEventColor(eventText)
local textLower = eventText:lower()
-- Each type of event has a logical color:
if textLower:find("thunder") or textLower:find("storm") then
return "#FFC0C0" -- a moderate red/pink for storms
elseif textLower:find("snow") or textLower:find("sleet") or textLower:find("flurries") then
return "#CCE6FF" -- a moderate blue for snowy
elseif textLower:find("rain") or textLower:find("drizzle") or textLower:find("downpour") then
return "#CCEEFF" -- a soft aqua for rain
elseif textLower:find("dust") or textLower:find("desert") then
return "#FFFACD" -- lemon chiffon for dust
elseif textLower:find("hail") then
return "#E0FFFF" -- light cyan for hail
else
return "#F8F8F8" -- default light gray
end
end
---------------------------------------------------------
-- 6. Map Climate Name -> Background Color
-- According to the table you provided:
-- Cfa (Humid Subtropical) -> #FFE4C4
-- Cfb (Oceanic) -> #CCE5FF
-- Cfc (Subpolar Oceanic) -> #CCFFFF
-- Csa (Mediterranean, hot summer) -> #FFE4C4
-- BSh (Hot Steppe) -> #FFDFAF
-- BWh (Hot Desert) -> #FFD1DC
-- BSk (Cold Steppe) -> #FFECB3
-- Dfc (Subarctic) -> #CAB3FF
--
-- We'll create a function that returns these based on the spelled-out name.
---------------------------------------------------------
local function getClimateBGColor(climateName)
-- We'll define a small map from spelled-out to the color in your table.
-- e.g. if the spelled-out "Humid Subtropical" => "Cfa" => #FFE4C4
local map = {
["Humid Subtropical"] = "#FFE4C4", -- cfa
["Oceanic"] = "#CCE5FF", -- cfb
["Subpolar Oceanic"] = "#CCFFFF", -- cfc
["Mediterranean (Hot Summer)"]= "#FFE4C4", -- csa
["Hot Steppe"] = "#FFDFAF", -- bsh
["Hot Desert"] = "#FFD1DC", -- bwh
["Cold Steppe"] = "#FFECB3", -- bsk
["Subarctic"] = "#CAB3FF" -- dfc
}
return map[climateName] or "#F8F8F8"
end
---------------------------------------------------------
-- 7. City-Specific Disaster Profiles
---------------------------------------------------------
local cityDisasterProfiles = {
["Vaeringheim"] = {"flood", "heatwave", "thunderstorm"},
["Luminaria"] = {"flood", "landslide"},
["Serena"] = {"snowstorm", "landslide"},
["Pyralis"] = {"forest fire", "heatwave"},
["Symphonara"] = {"landslide", "flood"},
["Aurelia"] = {"drought", "heatwave"},
["Somniumpolis"] = {"flood"},
["Nexa"] = {"landslide"},
["Lunalis Sancta"] = {"flood"},
["Sylvapolis"] = {"flood"},
["Saluria"] = {"flood"},
["Aetherium"] = {"snowstorm", "blizzard"},
["Ferrum Citadel"] = {"dust storm", "heatwave"},
["Acheron"] = {"flood", "landslide"},
["Erythros"] = {"flood"},
["Catonis Atrium"] = {"landslide"},
["Delphica"] = {"flood"},
["Koinonía"] = {"landslide"},
["Aureum"] = {"drought", "heatwave"},
["Skýrophos"] = {"landslide", "coastal storm"},
["Bjornopolis"] = {"flood"},
["Aegirheim"] = {"snowstorm", "blizzard"},
["Norsolyra"] = {"flood", "dust storm"},
["Thorsalon"] = {"coastal storm", "flood"},
["Pelagia"] = {"dust storm", "drought"},
["Myrene"] = {"flood"},
["Thyrea"] = {"flood", "thunderstorm"},
["Ephyra"] = {"snowstorm"},
["Halicarn"] = {"drought", "landslide"},
["Keybir-Aviv"] = {"flood", "heatwave"},
["Tel-Amin"] = {"drought", "heatwave"},
["Diamandis"] = {"drought", "heatwave"},
["Jogi"] = {"flood"},
["Lewisburg"] = {"flood", "landslide"},
["Thermosalem"] = {"flood"},
["Akróstadium"] = {"dust storm", "landslide"},
["Sufriya"] = {"flood", "dust storm"},
["Lykopolis"] = {"flood"}
}
---------------------------------------------------------
-- 8. Advisory Logic
-- If triggered, color code cell in red
---------------------------------------------------------
local function getDisasterAdvisory(cityName, eventText, chanceOfRain, predictedRain, highC)
local disasterList = cityDisasterProfiles[cityName] or {}
local textLower = eventText:lower()
local advisories = {}
for _, disasterType in ipairs(disasterList) do
if disasterType == "flood" then
if chanceOfRain > 60 or textLower:find("heavy downpours") then
table.insert(advisories, "Flood Advisory")
end
elseif disasterType == "heatwave" then
if highC >= 32 then
table.insert(advisories, "Heatwave Warning")
end
elseif disasterType == "thunderstorm" then
if textLower:find("thunder") or textLower:find("storm") then
table.insert(advisories, "Thunderstorm Alert")
end
elseif disasterType == "snowstorm" then
if textLower:find("snow") or textLower:find("sleet") then
table.insert(advisories, "Snowstorm Warning")
end
elseif disasterType == "landslide" then
if chanceOfRain > 50 or textLower:find("heavy rain") or textLower:find("thunderstorm") then
table.insert(advisories, "Landslide Risk")
end
elseif disasterType == "forest fire" then
if textLower:find("hot") or chanceOfRain < 10 then
table.insert(advisories, "Forest Fire Risk")
end
elseif disasterType == "drought" then
if chanceOfRain < 5 then
table.insert(advisories, "Drought Alert")
end
elseif disasterType == "blizzard" then
if textLower:find("snow") or textLower:find("flurries") then
table.insert(advisories, "Blizzard Warning")
end
elseif disasterType == "dust storm" then
if textLower:find("dust") then
table.insert(advisories, "Dust Storm Advisory")
end
elseif disasterType == "coastal storm" then
if textLower:find("storm") then
table.insert(advisories, "Coastal Storm Alert")
end
end
end
if #advisories == 0 then
return "No Advisory"
else
return table.concat(advisories, "; ")
end
end
---------------------------------------------------------
-- 9. Random Weather Stats (including wind speed)
---------------------------------------------------------
local windDirections = {"N", "NE", "E", "SE", "S", "SW", "W", "NW"}
local function getRandomWeatherStats(climate, season)
local data = climateTemperature[climate] and climateTemperature[climate][season]
if not data then
return {
highC="N/A", lowC="N/A", humidity="N/A",
chanceOfRain="N/A", predictedRain="N/A",
windDir="N/A", windSpeed="N/A"
}
end
local hiC = math.random(data.hiMin, data.hiMax)
local loC = math.random(data.loMin, data.loMax)
local hum = math.random(data.humMin, data.humMax)
local cRain = math.random(data.crMin, data.crMax)
local pRain = 0
if cRain > 0 then
pRain = math.random(data.rfMin, data.rfMax)
end
local wDir = windDirections[math.random(#windDirections)]
local wSpd = math.random(0, 50) -- 0..50 km/h
return {
highC = hiC,
lowC = loC,
humidity = hum,
chanceOfRain = cRain,
predictedRain= pRain,
windDir = wDir,
windSpeed = wSpd
}
end
---------------------------------------------------------
-- 10. Celsius to Fahrenheit
---------------------------------------------------------
local function cToF(c)
return math.floor(c * 9/5 + 32 + 0.5)
end
---------------------------------------------------------
-- 11. Full City List
---------------------------------------------------------
local cityData = {
{city = "Vaeringheim", climate = "Humid Subtropical"},
{city = "Luminaria", climate = "Humid Subtropical"},
{city = "Serena", climate = "Subpolar Oceanic"},
{city = "Pyralis", climate = "Humid Subtropical"},
{city = "Symphonara", climate = "Oceanic"},
{city = "Aurelia", climate = "Humid Subtropical"},
{city = "Somniumpolis", climate = "Humid Subtropical"},
{city = "Nexa", climate = "Humid Subtropical"},
{city = "Lunalis Sancta", climate = "Oceanic"},
{city = "Sylvapolis", climate = "Humid Subtropical"},
{city = "Saluria", climate = "Oceanic"},
{city = "Aetherium", climate = "Subarctic"},
{city = "Ferrum Citadel", climate = "Hot Desert"},
{city = "Acheron", climate = "Cold Steppe"},
{city = "Erythros", climate = "Oceanic"},
{city = "Catonis Atrium", climate = "Oceanic"},
{city = "Delphica", climate = "Oceanic"},
{city = "Koinonía", climate = "Oceanic"},
{city = "Aureum", climate = "Mediterranean (Hot Summer)"},
{city = "Skýrophos", climate = "Oceanic"},
{city = "Bjornopolis", climate = "Oceanic"},
{city = "Aegirheim", climate = "Subarctic"},
{city = "Norsolyra", climate = "Oceanic"},
{city = "Thorsalon", climate = "Oceanic"},
{city = "Pelagia", climate = "Hot Steppe"},
{city = "Myrene", climate = "Oceanic"},
{city = "Thyrea", climate = "Humid Subtropical"},
{city = "Ephyra", climate = "Subpolar Oceanic"},
{city = "Halicarn", climate = "Mediterranean (Hot Summer)"},
{city = "Keybir-Aviv", climate = "Humid Subtropical"},
{city = "Tel-Amin", climate = "Mediterranean (Hot Summer)"},
{city = "Diamandis", climate = "Mediterranean (Hot Summer)"},
{city = "Jogi", climate = "Oceanic"},
{city = "Lewisburg", climate = "Humid Subtropical"},
{city = "Thermosalem", climate = "Oceanic"},
{city = "Akróstadium", climate = "Cold Steppe"},
{city = "Sufriya", climate = "Humid Subtropical"},
{city = "Lykopolis", climate = "Oceanic"}
}
---------------------------------------------------------
-- 12. Main Weather Forecast Table Function
---------------------------------------------------------
function p.weatherForecast(frame)
local dateInfo = getCurrentDateInfo()
local dayOfYear = dateInfo.dayOfYear
local yearNum = dateInfo.psscYear
local seasonName= getSeasonName(dayOfYear)
local out = {}
table.insert(out, "== Daily Weather Forecast ==\n")
table.insert(out, string.format(
"''(Day %d of Year %d PSSC, %s)''\n\n",
dayOfYear, yearNum, seasonName
))
-- Start table
table.insert(out, '{| class="wikitable sortable" style="width:100%; text-align:left;"\n')
table.insert(out,
[[! City !! Climate !! Season !! High °C (°F) !! Low °C (°F) !! Humidity (%) !! Chance of Rain (%) !! Rainfall (mm) !! Wind Dir !! Wind Speed (km/h) !! Today's Weather !! Advisory
]]
)
for _, cityEntry in ipairs(cityData) do
local cityName = cityEntry.city
local climateName = cityEntry.climate
-- (A) Random event pick
local cTable = climateEvents[climateName]
local sTable = cTable and cTable[seasonName]
local forecast= "No data"
if sTable and #sTable > 0 then
forecast = sTable[math.random(#sTable)]
end
-- (B) Weather cell color
local weatherColor = getEventColor(forecast)
-- (C) Stats
local stats = getRandomWeatherStats(climateName, seasonName)
local hiC = stats.highC
local loC = stats.lowC
local hum = stats.humidity
local cRain = stats.chanceOfRain
local pRain = stats.predictedRain
local wDir = stats.windDir
local wSpd = stats.windSpeed
local hiF = (type(hiC)=="number") and cToF(hiC) or "N/A"
local loF = (type(loC)=="number") and cToF(loC) or "N/A"
-- (D) Advisory
local advisory = getDisasterAdvisory(
cityName, forecast, cRain, pRain,
(type(hiC)=="number" and hiC or 0)
)
-- If there's an advisory, color it red
local advisoryCell
if advisory ~= "No Advisory" then
advisoryCell = 'style="background-color:#FFBABA" | ' .. advisory
else
advisoryCell = advisory
end
-- (E) Climate cell color
local climateBG = getClimateBGColor(climateName)
local climateCell = string.format(
'style="background-color:%s" | %s',
climateBG, climateName
)
table.insert(out, "|-\n")
table.insert(out, string.format(
[[| %s || %s || %s || %s (%s) || %s (%s) || %s || %s || %s || %s || %s || style="background-color:%s" | %s || %s
]],
cityName,
climateCell,
seasonName,
tostring(hiC), tostring(hiF),
tostring(loC), tostring(loF),
tostring(hum),
tostring(cRain),
tostring(pRain),
wDir,
tostring(wSpd),
weatherColor, forecast,
advisoryCell
))
end
table.insert(out, "|}\n")
return table.concat(out)
end
return p