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 6: | Line 6: | ||
-- 2) weatherForCity(frame) -> single table for a city | -- 2) weatherForCity(frame) -> single table for a city | ||
-- 3) starsForTonight(frame) -> example star-visibility | -- 3) starsForTonight(frame) -> example star-visibility | ||
-- | -- | ||
-- All cells are re-randomized once per day | -- All cells are re-randomized once per day | ||
-- by seeding math.random with (year*1000 + dayOfYear). | -- by seeding math.random with (year*1000 + dayOfYear). | ||
| Line 52: | Line 52: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
local climateTemperature = { | local climateTemperature = { | ||
-- (unchanged climate data) | |||
["Humid Subtropical"] = { | ["Humid Subtropical"] = { | ||
Atosiel = { hiMin=18, hiMax=26, loMin=10, loMax=16, humMin=60, humMax=80 }, | Atosiel = { hiMin=18, hiMax=26, loMin=10, loMax=16, humMin=60, humMax=80 }, | ||
| Line 96: | Line 97: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 4. Weather Events by Climate & Season | -- 4. Weather Events by Climate & Season | ||
-- (unchanged event text) | |||
--------------------------------------------------------- | --------------------------------------------------------- | ||
local climateEvents = { | local climateEvents = { | ||
-- (unchanged big table of strings) | |||
["Humid Subtropical"] = { | ["Humid Subtropical"] = { | ||
Atosiel = { | Atosiel = { | ||
| Line 137: | Line 140: | ||
}, | }, | ||
["Oceanic"] = { | ["Oceanic"] = { | ||
-- ... etc ... | |||
}, | }, | ||
-- (rest of climateEvents unchanged) | |||
} | } | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 5. triggeredDisasters, getAdvisory | -- 5. triggeredDisasters, getAdvisory (unchanged) | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
local triggeredDisasters = { | local triggeredDisasters = { | ||
-- (unchanged hazard keywords) | |||
} | } | ||
| Line 460: | Line 156: | ||
local extras = {} | local extras = {} | ||
local randomChance = 0.40 | local randomChance = 0.40 | ||
for _, item in ipairs(triggeredDisasters) do | for _, item in ipairs(triggeredDisasters) do | ||
| Line 491: | Line 186: | ||
end | end | ||
-- Updated function: narrower typical wind speeds (1..25 km/h). | |||
-- We'll adjust the code so we can later override chanceOfRain if precipitation is mentioned. | |||
local windDirections = {"N","NE","E","SE","S","SW","W","NW"} | local windDirections = {"N","NE","E","SE","S","SW","W","NW"} | ||
| Line 509: | Line 206: | ||
local lo = math.random(data.loMin, data.loMax) | local lo = math.random(data.loMin, data.loMax) | ||
local hum = math.random(data.humMin, data.humMax) | local hum = math.random(data.humMin, data.humMax) | ||
local cRain= math.random(0,100) | local cRain= math.random(0,100) -- default random chance of rain | ||
local wDir = windDirections[math.random(#windDirections)] | local wDir = windDirections[math.random(#windDirections)] | ||
local wSpd = math.random( | local wSpd = math.random(1,25) -- narrower range for more realistic wind speeds | ||
return { | return { | ||
| Line 524: | Line 221: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 8. City Data | -- 8. City Data (unchanged) | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
local cityData = { | local cityData = { | ||
{city = "[https://micras.org/mwiki/List_of_cities_in_Bassaridia_Vaeringheim#Vaeringheim Vaeringheim]", climate = "Humid Subtropical"}, | {city = "[https://micras.org/mwiki/List_of_cities_in_Bassaridia_Vaeringheim#Vaeringheim Vaeringheim]", climate = "Humid Subtropical"}, | ||
-- (rest of the city list unchanged) | |||
{city = "[https://micras.org/mwiki/List_of_cities_in_Bassaridia_Vaeringheim#Lykopolis Lykopolis]", climate = "Oceanic"} | {city = "[https://micras.org/mwiki/List_of_cities_in_Bassaridia_Vaeringheim#Lykopolis Lykopolis]", climate = "Oceanic"} | ||
} | } | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 9. Color-coded cell background | -- 9. Color-coded cell background (unchanged) | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
local function getEventColor(eventText) | local function getEventColor(eventText) | ||
| Line 592: | Line 248: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 10A. ALL-CITIES FORECAST FUNCTION | -- 10A. ALL-CITIES FORECAST FUNCTION (modified to enforce higher chance of rain if precipitation is detected) | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
function p.weatherForecast(frame) | function p.weatherForecast(frame) | ||
| Line 641: | Line 297: | ||
end | end | ||
-- 2) | -- 2) Stats | ||
local | local stats = getRandomWeatherStats(climateName, seasonName) | ||
-- If the daily weather text has precipitation words, override chanceOfRain with a high value: | |||
local forecastLower = fStr:lower() | |||
if forecastLower:find("rain") | |||
or forecastLower:find("drizzle") | |||
or forecastLower:find("downpour") | |||
or forecastLower:find("shower") | |||
or forecastLower:find("snow") | |||
or forecastLower:find("storm") | |||
or forecastLower:find("thunder") | |||
or forecastLower:find("sleet") | |||
then | |||
stats.chanceOfRain = math.random(70, 99) | |||
end | |||
local hiF = cToF(stats.high) | local hiF = cToF(stats.high) | ||
local loF = cToF(stats.low) | local loF = cToF(stats.low) | ||
| Line 653: | Line 321: | ||
local wSpd = stats.windSpeed | local wSpd = stats.windSpeed | ||
local rowColor = getEventColor(fStr) | |||
local advisory = getAdvisory(fStr) | local advisory = getAdvisory(fStr) | ||
table.insert(out, "|-\n") | table.insert(out, "|-\n") | ||
table.insert(out, string.format([[ | table.insert(out, string.format([[ | ||
| Line 670: | Line 337: | ||
| style="background-color:%s" | %s | | style="background-color:%s" | %s | ||
| %s | | %s | ||
]], cityLink, climateName, seasonName, hiF, loF, hum, cRain, | ]], | ||
cityLink, climateName, seasonName, | |||
hiF, loF, hum, cRain, wDir, wSpd, | |||
rowColor, fStr, advisory | |||
)) | |||
end | end | ||
| Line 679: | Line 349: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 10B. SINGLE-CITY FORECAST FUNCTION | -- 10B. SINGLE-CITY FORECAST FUNCTION (similarly updated) | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
function p.weatherForCity(frame) | function p.weatherForCity(frame) | ||
| Line 699: | Line 369: | ||
local foundEntry = nil | local foundEntry = nil | ||
for _, entry in ipairs(cityData) do | for _, entry in ipairs(cityData) do | ||
local plainCity = entry.city:match("%s+([A-Za-zÀ-ž%-]+)%]?$") | local plainCity = entry.city:match("%s+([A-Za-zÀ-ž%-]+)%]?$") | ||
if entry.city == cityRequested or plainCity == cityRequested then | if entry.city == cityRequested or plainCity == cityRequested then | ||
| Line 721: | Line 390: | ||
fStr = seasonTbl[idx] | fStr = seasonTbl[idx] | ||
end | end | ||
local stats = getRandomWeatherStats(climateName, seasonName) | |||
-- Override chance of rain if we detect precipitation in fStr | |||
local forecastLower = fStr:lower() | |||
if forecastLower:find("rain") | |||
or forecastLower:find("drizzle") | |||
or forecastLower:find("downpour") | |||
or forecastLower:find("shower") | |||
or forecastLower:find("snow") | |||
or forecastLower:find("storm") | |||
or forecastLower:find("thunder") | |||
or forecastLower:find("sleet") | |||
then | |||
stats.chanceOfRain = math.random(70, 99) | |||
end | |||
local hiF = cToF(stats.high) | |||
local loF = cToF(stats.low) | |||
local hum = stats.humidity | |||
local cRain = stats.chanceOfRain | |||
local wDir = stats.windDir | |||
local wSpd = stats.windSpeed | |||
local rowColor = getEventColor(fStr) | local rowColor = getEventColor(fStr) | ||
local advisory = getAdvisory(fStr) | local advisory = getAdvisory(fStr) | ||
| Line 760: | Line 444: | ||
| style="background-color:%s" | %s | | style="background-color:%s" | %s | ||
| %s | | %s | ||
]], climateName, seasonName, hiF, loF, hum, cRain, wDir, wSpd, | ]], climateName, seasonName, hiF, loF, hum, cRain, wDir, wSpd, rowColor, fStr, advisory) | ||
table.insert(out, row) | table.insert(out, row) | ||
| Line 770: | Line 453: | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
-- 10C. STARS FOR TONIGHT | -- 10C. STARS FOR TONIGHT (unchanged) | ||
--------------------------------------------------------- | --------------------------------------------------------- | ||
local starChartData = { | local starChartData = { | ||
-- (unchanged star data) | |||
} | } | ||
| Line 806: | Line 462: | ||
local function isVisibleAt630(lat) | local function isVisibleAt630(lat) | ||
local offset = math.random(5,55) | |||
local offset = math.random(5,55) | |||
return (lat < (observerLat + offset)) | return (lat < (observerLat + offset)) | ||
end | end | ||
| Line 829: | Line 482: | ||
if isVisibleAt630(star.lat) then | if isVisibleAt630(star.lat) then | ||
yesNo = "Yes" | yesNo = "Yes" | ||
alt = math.random(20,80) | alt = math.random(20,80) | ||
end | end | ||
Revision as of 19:37, 2 January 2025
Documentation for this module may be created at Module:BassaridiaForecast/doc
---------------------------------------------------------
-- Module: BassaridiaForecast
-- Provides three functions:
-- 1) weatherForecast(frame) -> returns a single
-- big table for ALL cities
-- 2) weatherForCity(frame) -> single table for a city
-- 3) starsForTonight(frame) -> example star-visibility
--
-- All cells are re-randomized once per day
-- by seeding math.random with (year*1000 + dayOfYear).
---------------------------------------------------------
local p = {}
---------------------------------------------------------
-- 1. Determine Today's Date for PSSC
-- Also used to create a daily random seed.
---------------------------------------------------------
local function getCurrentDateInfo()
local startDate = os.time({year=1999, month=8, day=6})
local secondsInDay= 86400
local daysPerYear = 183
local currentTime = os.time()
local totalDays = math.floor((currentTime - startDate) / secondsInDay)
local fractionYear= totalDays / daysPerYear
local psscYear = math.floor(fractionYear)
local dayOfYear = math.floor((fractionYear - psscYear) * daysPerYear) + 1
return {
psscYear = psscYear,
dayOfYear= dayOfYear
}
end
---------------------------------------------------------
-- 2. Determine Season (Atosiel=1..61, Thalassiel=62..122, Opsitheiel=123..183)
---------------------------------------------------------
local function getSeasonName(dayOfYear)
if dayOfYear <= 61 then
return "Atosiel"
elseif dayOfYear <= 122 then
return "Thalassiel"
else
return "Opsitheiel"
end
end
---------------------------------------------------------
-- 3. climateTemperature (°C behind scenes, displayed °F)
---------------------------------------------------------
local climateTemperature = {
-- (unchanged climate data)
["Humid Subtropical"] = {
Atosiel = { hiMin=18, hiMax=26, loMin=10, loMax=16, humMin=60, humMax=80 },
Thalassiel = { hiMin=25, hiMax=34, loMin=19, loMax=24, humMin=65, humMax=90 },
Opsitheiel = { hiMin=20, hiMax=28, loMin=12, loMax=18, humMin=50, humMax=75 }
},
["Oceanic"] = {
Atosiel = { hiMin=10, hiMax=17, loMin=5, loMax=12, humMin=70, humMax=90 },
Thalassiel = { hiMin=14, hiMax=21, loMin=9, loMax=14, humMin=65, humMax=85 },
Opsitheiel = { hiMin=5, hiMax=12, loMin=1, loMax=6, humMin=70, humMax=90 }
},
["Subpolar Oceanic"] = {
Atosiel = { hiMin=3, hiMax=9, loMin=-2, loMax=3, humMin=70, humMax=95 },
Thalassiel = { hiMin=6, hiMax=12, loMin=1, loMax=6, humMin=70, humMax=90 },
Opsitheiel = { hiMin=-1, hiMax=4, loMin=-6, loMax=-1, humMin=75, humMax=95 }
},
["Mediterranean (Hot Summer)"] = {
Atosiel = { hiMin=15, hiMax=21, loMin=7, loMax=12, humMin=40, humMax=60 },
Thalassiel = { hiMin=25, hiMax=35, loMin=15, loMax=20, humMin=30, humMax=50 },
Opsitheiel = { hiMin=12, hiMax=18, loMin=5, loMax=10, humMin=45, humMax=65 }
},
["Hot Desert"] = {
Atosiel = { hiMin=25, hiMax=35, loMin=12, loMax=18, humMin=10, humMax=30 },
Thalassiel = { hiMin=35, hiMax=45, loMin=25, loMax=30, humMin=5, humMax=20 },
Opsitheiel = { hiMin=22, hiMax=30, loMin=10, loMax=16, humMin=5, humMax=25 }
},
["Cold Steppe"] = {
Atosiel = { hiMin=10, hiMax=18, loMin=2, loMax=8, humMin=30, humMax=50 },
Thalassiel = { hiMin=20, hiMax=28, loMin=10, loMax=15, humMin=25, humMax=45 },
Opsitheiel = { hiMin=0, hiMax=7, loMin=-6, loMax=0, humMin=35, humMax=55 }
},
["Hot Steppe"] = {
Atosiel = { hiMin=23, hiMax=30, loMin=12, loMax=18, humMin=20, humMax=40 },
Thalassiel = { hiMin=30, hiMax=40, loMin=20, loMax=25, humMin=15, humMax=35 },
Opsitheiel = { hiMin=25, hiMax=33, loMin=15, loMax=20, humMin=15, humMax=40 }
},
["Subarctic"] = {
Atosiel = { hiMin=0, hiMax=8, loMin=-10,loMax=-1, humMin=50, humMax=80 },
Thalassiel = { hiMin=5, hiMax=15, loMin=-1, loMax=5, humMin=50, humMax=85 },
Opsitheiel = { hiMin=-5, hiMax=0, loMin=-15,loMax=-5, humMin=60, humMax=90 }
}
}
---------------------------------------------------------
-- 4. Weather Events by Climate & Season
-- (unchanged event text)
---------------------------------------------------------
local climateEvents = {
-- (unchanged big table of strings)
["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"
}
},
["Oceanic"] = {
-- ... etc ...
},
-- (rest of climateEvents unchanged)
}
---------------------------------------------------------
-- 5. triggeredDisasters, getAdvisory (unchanged)
---------------------------------------------------------
local triggeredDisasters = {
-- (unchanged hazard keywords)
}
local function getAdvisory(forecastStr)
local textLower = forecastStr:lower()
local extras = {}
local randomChance = 0.40
for _, item in ipairs(triggeredDisasters) do
for _, kw in ipairs(item.keywords) do
if textLower:find(kw) then
local roll = math.random()
if roll <= randomChance then
table.insert(extras, item.hazard)
end
break
end
end
end
if #extras > 0 then
return table.concat(extras, "; ")
else
return "No reports"
end
end
---------------------------------------------------------
-- 7. Conversions and Random Stats
---------------------------------------------------------
local function cToF(c)
if type(c) == "number" then
return math.floor(c * 9/5 + 32 + 0.5)
end
return c
end
-- Updated function: narrower typical wind speeds (1..25 km/h).
-- We'll adjust the code so we can later override chanceOfRain if precipitation is mentioned.
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 {
high = "N/A",
low = "N/A",
humidity = "N/A",
chanceOfRain = "N/A",
windDir = "N/A",
windSpeed = "N/A"
}
end
local hi = math.random(data.hiMin, data.hiMax)
local lo = math.random(data.loMin, data.loMax)
local hum = math.random(data.humMin, data.humMax)
local cRain= math.random(0,100) -- default random chance of rain
local wDir = windDirections[math.random(#windDirections)]
local wSpd = math.random(1,25) -- narrower range for more realistic wind speeds
return {
high = hi,
low = lo,
humidity = hum,
chanceOfRain= cRain,
windDir = wDir,
windSpeed = wSpd
}
end
---------------------------------------------------------
-- 8. City Data (unchanged)
---------------------------------------------------------
local cityData = {
{city = "[https://micras.org/mwiki/List_of_cities_in_Bassaridia_Vaeringheim#Vaeringheim Vaeringheim]", climate = "Humid Subtropical"},
-- (rest of the city list unchanged)
{city = "[https://micras.org/mwiki/List_of_cities_in_Bassaridia_Vaeringheim#Lykopolis Lykopolis]", climate = "Oceanic"}
}
---------------------------------------------------------
-- 9. Color-coded cell background (unchanged)
---------------------------------------------------------
local function getEventColor(eventText)
local textLower = eventText:lower()
if textLower:find("thunder") or textLower:find("storm") then
return "#FFD2D2"
elseif textLower:find("snow") or textLower:find("sleet") or textLower:find("flurries") then
return "#D2ECFF"
elseif textLower:find("rain") or textLower:find("drizzle") or textLower:find("downpour") then
return "#D2DFFF"
elseif textLower:find("dust") or textLower:find("desert") then
return "#FFFACD"
else
return "#F8F8F8"
end
end
---------------------------------------------------------
-- 10A. ALL-CITIES FORECAST FUNCTION (modified to enforce higher chance of rain if precipitation is detected)
---------------------------------------------------------
function p.weatherForecast(frame)
-- Random seed changes daily
local date = os.date("*t")
local dailySeed = (date.year * 1000) + date.yday
math.randomseed(dailySeed)
local dateInfo = getCurrentDateInfo()
local dayOfYear = dateInfo.dayOfYear
local yearNumber = 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, yearNumber, seasonName
)
)
table.insert(out, '{| class="wikitable sortable" style="width:100%; text-align:left;"\n')
table.insert(out, [[
! City
! Climate
! Season
! High °F
! Low °F
! Humidity (%)
! Chance of Rain (%)
! Wind Direction
! Wind Speed (km/h)
! Today's Weather
! Natural Disaster Advisory
]])
for _, entry in ipairs(cityData) do
local cityLink = entry.city
local climateName = entry.climate
-- 1) Random weather event
local climateTbl = climateEvents[climateName]
local seasonTbl = climateTbl and climateTbl[seasonName]
local fStr = "No data"
if seasonTbl and #seasonTbl > 0 then
local idx = math.random(#seasonTbl)
fStr = seasonTbl[idx]
end
-- 2) Stats
local stats = getRandomWeatherStats(climateName, seasonName)
-- If the daily weather text has precipitation words, override chanceOfRain with a high value:
local forecastLower = fStr:lower()
if forecastLower:find("rain")
or forecastLower:find("drizzle")
or forecastLower:find("downpour")
or forecastLower:find("shower")
or forecastLower:find("snow")
or forecastLower:find("storm")
or forecastLower:find("thunder")
or forecastLower:find("sleet")
then
stats.chanceOfRain = math.random(70, 99)
end
local hiF = cToF(stats.high)
local loF = cToF(stats.low)
local hum = stats.humidity
local cRain = stats.chanceOfRain
local wDir = stats.windDir
local wSpd = stats.windSpeed
local rowColor = getEventColor(fStr)
local advisory = getAdvisory(fStr)
table.insert(out, "|-\n")
table.insert(out, string.format([[
| %s
| %s
| %s
| %d
| %d
| %d
| %d
| %s
| %d
| style="background-color:%s" | %s
| %s
]],
cityLink, climateName, seasonName,
hiF, loF, hum, cRain, wDir, wSpd,
rowColor, fStr, advisory
))
end
table.insert(out, "|}\n")
return table.concat(out)
end
---------------------------------------------------------
-- 10B. SINGLE-CITY FORECAST FUNCTION (similarly updated)
---------------------------------------------------------
function p.weatherForCity(frame)
local cityRequested = frame.args.city
if not cityRequested or cityRequested == "" then
return "Error: Please specify a city. E.g. {{#invoke:BassaridiaForecast|weatherForCity|city=Vaeringheim}}"
end
-- daily seed
local date = os.date("*t")
local dailySeed = (date.year * 1000) + date.yday
math.randomseed(dailySeed)
local dateInfo = getCurrentDateInfo()
local dayOfYear = dateInfo.dayOfYear
local yearNumber = dateInfo.psscYear
local seasonName = getSeasonName(dayOfYear)
local foundEntry = nil
for _, entry in ipairs(cityData) do
local plainCity = entry.city:match("%s+([A-Za-zÀ-ž%-]+)%]?$")
if entry.city == cityRequested or plainCity == cityRequested then
foundEntry = entry
break
end
end
if not foundEntry then
return "Error: City '" .. cityRequested .. "' not found in cityData."
end
local cityLink = foundEntry.city
local climateName= foundEntry.climate
local climateTbl = climateEvents[climateName]
local seasonTbl = climateTbl and climateTbl[seasonName]
local fStr = "No data"
if seasonTbl and #seasonTbl > 0 then
local idx = math.random(#seasonTbl)
fStr = seasonTbl[idx]
end
local stats = getRandomWeatherStats(climateName, seasonName)
-- Override chance of rain if we detect precipitation in fStr
local forecastLower = fStr:lower()
if forecastLower:find("rain")
or forecastLower:find("drizzle")
or forecastLower:find("downpour")
or forecastLower:find("shower")
or forecastLower:find("snow")
or forecastLower:find("storm")
or forecastLower:find("thunder")
or forecastLower:find("sleet")
then
stats.chanceOfRain = math.random(70, 99)
end
local hiF = cToF(stats.high)
local loF = cToF(stats.low)
local hum = stats.humidity
local cRain = stats.chanceOfRain
local wDir = stats.windDir
local wSpd = stats.windSpeed
local rowColor = getEventColor(fStr)
local advisory = getAdvisory(fStr)
local out = {}
table.insert(out, '{| class="wikitable" style="width:100%; text-align:left;"\n')
table.insert(out, [[
! Climate
! Season
! High °F
! Low °F
! Humidity (%)
! Chance of Rain (%)
! Wind Dir
! Wind Speed (km/h)
! style="width:20em;" | Today's Weather
! Natural Disaster Advisory
]])
table.insert(out, "|-\n")
local row = string.format([[
| %s
| %s
| %d
| %d
| %d
| %d
| %s
| %d
| style="background-color:%s" | %s
| %s
]], climateName, seasonName, hiF, loF, hum, cRain, wDir, wSpd, rowColor, fStr, advisory)
table.insert(out, row)
table.insert(out, "|}\n\n")
return table.concat(out)
end
---------------------------------------------------------
-- 10C. STARS FOR TONIGHT (unchanged)
---------------------------------------------------------
local starChartData = {
-- (unchanged star data)
}
local observerLat = 49 -- Vaeringheim ~49°N
local function isVisibleAt630(lat)
local offset = math.random(5,55)
return (lat < (observerLat + offset))
end
function p.starsForTonight(frame)
local date = os.date("*t")
local dailySeed = (date.year * 1000) + date.yday + 777
math.randomseed(dailySeed)
local out = {}
table.insert(out, "=== Northern Host Stars Visible at ~6:30 PM ===\n\n")
table.insert(out, '{| class="wikitable" style="width:100%; text-align:left;"\n')
table.insert(out, "! Star !! Class !! Star-Lat !! Visible? !! Approx. Altitude\n")
for _, star in ipairs(starChartData) do
local yesNo = "No"
local alt = 0
if isVisibleAt630(star.lat) then
yesNo = "Yes"
alt = math.random(20,80)
end
table.insert(out, "|-\n")
table.insert(out, string.format(
"| %s || %s || %.1f°N || %s || %d°\n",
star.name, star.starClass, star.lat, yesNo, alt
))
end
table.insert(out, "|}\n\n")
return table.concat(out)
end
---------------------------------------------------------
-- Return the module table
---------------------------------------------------------
return p