diff --git a/code/__defines/planets.dm b/code/__defines/planets.dm
index 98b10b328a..3c2a1abcfc 100644
--- a/code/__defines/planets.dm
+++ b/code/__defines/planets.dm
@@ -10,5 +10,16 @@
#define WEATHER_HOT "hot"
#define WEATHER_BLOOD_MOON "blood moon" // For admin fun or cult later on.
+#define MOON_PHASE_NEW_MOON "new moon"
+#define MOON_PHASE_WAXING_CRESCENT "waxing crescent"
+#define MOON_PHASE_FIRST_QUARTER "first quarter"
+#define MOON_PHASE_WAXING_GIBBOUS "waxing gibbous"
+#define MOON_PHASE_FULL_MOON "full moon" // ware-shantaks sold seperately.
+#define MOON_PHASE_WANING_GIBBOUS "waning gibbous"
+#define MOON_PHASE_LAST_QUARTER "last quarter"
+#define MOON_PHASE_WANING_CRESCENT "waning crescent"
+
#define PLANET_PROCESS_SUN 0x1
-#define PLANET_PROCESS_TEMP 0x2
\ No newline at end of file
+#define PLANET_PROCESS_TEMP 0x2
+
+#define PLANET_TIME_MODIFIER 1 // If you want planet time to go faster than realtime, increase this number.
\ No newline at end of file
diff --git a/code/game/objects/items/devices/communicator/UI.dm b/code/game/objects/items/devices/communicator/UI.dm
index fca4cf4866..a699ea9afe 100644
--- a/code/game/objects/items/devices/communicator/UI.dm
+++ b/code/game/objects/items/devices/communicator/UI.dm
@@ -77,7 +77,9 @@
"Weather" = planet.weather_holder.current_weather.name,
"Temperature" = planet.weather_holder.temperature - T0C,
"High" = planet.weather_holder.current_weather.temp_high - T0C,
- "Low" = planet.weather_holder.current_weather.temp_low - T0C)
+ "Low" = planet.weather_holder.current_weather.temp_low - T0C,
+ "Forecast" = english_list(planet.weather_holder.forecast, and_text = "→", comma_text = "→", final_comma_text = "→") // Unicode RIGHTWARDS ARROW.
+ )
weather[++weather.len] = W
injection = "
Test
"
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index c6f2c0321e..db1f365afd 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -641,6 +641,7 @@
var/datum/weather/new_weather = input(usr, "What weather do you want to change to?", "Change Weather") as null|anything in planet.weather_holder.allowed_weather_types
if(new_weather)
planet.weather_holder.change_weather(new_weather)
+ planet.weather_holder.rebuild_forecast()
var/log = "[key_name(src)] changed [planet.name]'s weather to [new_weather]."
message_admins(log)
log_admin(log)
diff --git a/code/modules/flufftext/look_up.dm b/code/modules/flufftext/look_up.dm
new file mode 100644
index 0000000000..6be3713c03
--- /dev/null
+++ b/code/modules/flufftext/look_up.dm
@@ -0,0 +1,60 @@
+// Implements a verb to make your character look upward, mostly intended for the surface.
+
+/mob/living/verb/look_up()
+ set name = "Look Up"
+ set category = "IC"
+ set desc = "Look above you, and hope there's no ceiling spiders."
+
+ to_chat(usr, "You look upwards...")
+
+ var/turf/T = get_turf(usr)
+ if(!T) // In null space.
+ to_chat(usr, span("warning", "You appear to be in a place without any sort of concept of direction. You have bigger problems to worry about."))
+ return
+
+ if(!T.outdoors) // They're inside.
+ to_chat(usr, "You see nothing interesting.")
+ return
+
+ else // They're outside and hopefully on a planet.
+ var/datum/planet/P = SSplanets.z_to_planet[T.z]
+ if(!P)
+ to_chat(usr, span("warning", "You appear to be outside, but not on a planet... Something is wrong."))
+ return
+
+ var/datum/weather_holder/WH = P.weather_holder
+
+ // Describe the current weather.
+ if(WH.current_weather.observed_message)
+ to_chat(usr, WH.current_weather.observed_message)
+
+ // If we can see the sky, we'll see things like sun position, phase of the moon, etc.
+ if(!WH.current_weather.sky_visible)
+ to_chat(usr, "You can't see the sky clearly due to the [WH.current_weather.name].")
+ else
+ // Sun-related output.
+ if(P.sun_name)
+ var/afternoon = P.current_time.seconds_stored > (P.current_time.seconds_in_day / 2)
+
+ var/sun_message = null
+ switch(P.sun_position)
+ if(0 to 0.4) // Night
+ sun_message = "It is night time, [P.sun_name] is not visible."
+ if(0.4 to 0.5) // Twilight
+ sun_message = "The sky is in twilight, however [P.sun_name] is not visible."
+ if(0.5 to 0.7) // Sunrise/set.
+ sun_message = "[P.sun_name] is slowly [!afternoon ? "rising from" : "setting on"] the horizon."
+ if(0.7 to 0.9) // Morning/evening
+ sun_message = "[P.sun_name]'s position implies it is currently [!afternoon ? "early" : "late"] in the day."
+ if(0.9 to 1.0) // Noon
+ sun_message = "It's high noon. [P.sun_name] hangs directly above you."
+
+ to_chat(usr, sun_message)
+
+ // Now for the moon.
+ if(P.moon_name)
+ if(P.moon_phase == MOON_PHASE_NEW_MOON)
+ to_chat(usr, "[P.moon_name] is not visible. It must be a new moon.")
+ else
+ to_chat(usr, "[P.moon_name] appears to currently be a [P.moon_phase].")
+
diff --git a/code/modules/planet/planet.dm b/code/modules/planet/planet.dm
index 8fb7604417..0364056b69 100644
--- a/code/modules/planet/planet.dm
+++ b/code/modules/planet/planet.dm
@@ -21,19 +21,34 @@
var/list/turf/simulated/floor/planet_floors = list()
var/list/turf/unsimulated/wall/planetary/planet_walls = list()
-
var/needs_work = 0 // Bitflags to signal to the planet controller these need (properly deferrable) work. Flags defined in controller.
+ var/sun_name = "the sun" // For flavor.
+
+ var/moon_name = null // Purely for flavor. Null means no moon exists.
+ var/moon_phase = null // Set if above is defined.
+
/datum/planet/New()
..()
weather_holder = new(src)
current_time = current_time.make_random_time()
+ if(moon_name)
+ moon_phase = pick(list(
+ MOON_PHASE_NEW_MOON,
+ MOON_PHASE_WAXING_CRESCENT,
+ MOON_PHASE_FIRST_QUARTER,
+ MOON_PHASE_WAXING_GIBBOUS,
+ MOON_PHASE_FULL_MOON,
+ MOON_PHASE_WANING_GIBBOUS,
+ MOON_PHASE_LAST_QUARTER,
+ MOON_PHASE_WANING_CRESCENT
+ ))
update_sun()
/datum/planet/proc/process(last_fire)
if(current_time)
var/difference = world.time - last_fire
- current_time = current_time.add_seconds(difference SECONDS)
+ current_time = current_time.add_seconds((difference / 10) * PLANET_TIME_MODIFIER)
update_weather() // We update this first, because some weather types decease the brightness of the sun.
if(sun_last_process <= world.time - sun_process_interval)
update_sun()
diff --git a/code/modules/planet/sif.dm b/code/modules/planet/sif.dm
index 3491fac2db..6ec5a9dfe4 100644
--- a/code/modules/planet/sif.dm
+++ b/code/modules/planet/sif.dm
@@ -12,6 +12,9 @@ var/datum/planet/sif/planet_sif = null
// expected_z_levels = list(1) // To be changed when real map is finished.
planetary_wall_type = /turf/unsimulated/wall/planetary/sif
+ sun_name = "Vir"
+ moon_name = "Thor"
+
/datum/planet/sif/New()
..()
planet_sif = src
@@ -131,7 +134,7 @@ var/datum/planet/sif/planet_sif = null
WEATHER_HAIL = 2.5
)
-datum/weather/sif
+/datum/weather/sif
name = "sif base"
temp_high = 283.15 // 10c
temp_low = 263.15 // -10c
@@ -142,6 +145,13 @@ datum/weather/sif
WEATHER_CLEAR = 60,
WEATHER_OVERCAST = 40
)
+ transition_messages = list(
+ "The sky clears up.",
+ "The sky is visible.",
+ "The weather is calm."
+ )
+ sky_visible = TRUE
+ observed_message = "The sky is clear."
/datum/weather/sif/overcast
name = "overcast"
@@ -154,6 +164,12 @@ datum/weather/sif
WEATHER_RAIN = 5,
WEATHER_HAIL = 5
)
+ observed_message = "It is overcast, all you can see are clouds."
+ transition_messages = list(
+ "All you can see above are clouds.",
+ "Clouds cut off your view of the sky.",
+ "It's very cloudy."
+ )
/datum/weather/sif/light_snow
name = "light snow"
@@ -167,6 +183,11 @@ datum/weather/sif
WEATHER_SNOW = 25,
WEATHER_HAIL = 5
)
+ observed_message = "It is snowing lightly."
+ transition_messages = list(
+ "Small snowflakes begin to fall from above.",
+ "It begins to snow lightly.",
+ )
/datum/weather/sif/snow
name = "moderate snow"
@@ -182,6 +203,11 @@ datum/weather/sif
WEATHER_HAIL = 5,
WEATHER_OVERCAST = 5
)
+ observed_message = "It is snowing."
+ transition_messages = list(
+ "It's starting to snow.",
+ "The air feels much colder as snowflakes fall from above."
+ )
/datum/weather/sif/snow/process_effects()
..()
@@ -206,6 +232,11 @@ datum/weather/sif
WEATHER_HAIL = 10,
WEATHER_OVERCAST = 5
)
+ observed_message = "A blizzard blows snow everywhere."
+ transition_messages = list(
+ "Strong winds howl around you as a blizzard appears.",
+ "It starts snowing heavily, and it feels extremly cold now."
+ )
/datum/weather/sif/blizzard/process_effects()
..()
@@ -230,6 +261,10 @@ datum/weather/sif
WEATHER_STORM = 10,
WEATHER_HAIL = 5
)
+ observed_message = "It is raining."
+ transition_messages = list(
+ "The sky is dark, and rain falls down upon you."
+ )
/datum/weather/sif/rain/process_effects()
..()
@@ -267,6 +302,12 @@ datum/weather/sif
var/next_lightning_strike = 0 // world.time when lightning will strike.
var/min_lightning_cooldown = 5 SECONDS
var/max_lightning_cooldown = 1 MINUTE
+ observed_message = "An intense storm pours down over the region."
+ transition_messages = list(
+ "You feel intense winds hit you as the weather takes a turn for the worst.",
+ "Loud thunder is heard in the distance.",
+ "A bright flash heralds the approach of a storm."
+ )
transition_chances = list(
@@ -329,6 +370,12 @@ datum/weather/sif
WEATHER_HAIL = 10,
WEATHER_OVERCAST = 5
)
+ observed_message = "Ice is falling from the sky."
+ transition_messages = list(
+ "Ice begins to fall from the sky.",
+ "It begins to hail.",
+ "An intense chill is felt, and chunks of ice start to fall from the sky, towards you."
+ )
/datum/weather/sif/hail/process_effects()
..()
@@ -375,3 +422,7 @@ datum/weather/sif
transition_chances = list(
WEATHER_BLOODMOON = 100
)
+ observed_message = "Everything is red. Something really wrong is going on."
+ transition_messages = list(
+ "The sky turns blood red!"
+ )
\ No newline at end of file
diff --git a/code/modules/planet/weather.dm b/code/modules/planet/weather.dm
index c3939956c5..a13cb87cc7 100644
--- a/code/modules/planet/weather.dm
+++ b/code/modules/planet/weather.dm
@@ -1,12 +1,13 @@
/datum/weather_holder
- var/datum/planet/our_planet = null
- var/datum/weather/current_weather = null
- var/temperature = T20C
- var/wind_dir = 0
- var/wind_speed = 0
- var/list/allowed_weather_types = list()
- var/list/roundstart_weather_chances = list()
- var/next_weather_shift = null
+ var/datum/planet/our_planet = null // Reference to the planet datum that holds this datum.
+ var/datum/weather/current_weather = null // The current weather that is affecting the planet.
+ var/temperature = T20C // The temperature to set planetary walls to.
+ var/wind_dir = 0 // Not implemented.
+ var/wind_speed = 0 // Not implemented.
+ var/list/allowed_weather_types = list() // Assoc list of weather identifiers, containing the actual weather datum.
+ var/list/roundstart_weather_chances = list() // Assoc list of weather identifiers and their odds of being picked to happen at roundstart.
+ var/next_weather_shift = null // world.time when the weather subsystem will advance the forecast.
+ var/list/forecast = list() // A list of what the weather will be in the future. This allows it to be pre-determined and planned around.
// Holds the weather icon, using vis_contents. Documentation says an /atom/movable is required for placing inside another atom's vis_contents.
var/atom/movable/weather_visuals/visuals = null
@@ -24,28 +25,75 @@
/datum/weather_holder/proc/change_weather(var/new_weather)
var/old_light_modifier = null
+ var/old_weather = null
if(current_weather)
old_light_modifier = current_weather.light_modifier // We store the old one, so we can determine if recalculating the sun is needed.
+ old_weather = current_weather
current_weather = allowed_weather_types[new_weather]
next_weather_shift = world.time + rand(current_weather.timer_low_bound, current_weather.timer_high_bound) MINUTES
+ if(new_weather != old_weather)
+ show_transition_message()
update_icon_effects()
update_temperature()
if(old_light_modifier && current_weather.light_modifier != old_light_modifier) // Updating the sun should be done sparingly.
our_planet.update_sun()
- message_admins("[our_planet.name]'s weather is now [new_weather], with a temperature of [temperature]°K ([temperature - T0C]°C | [temperature * 1.8 - 459.67]°F).")
+ log_debug("[our_planet.name]'s weather is now [new_weather], with a temperature of [temperature]°K ([temperature - T0C]°C | [temperature * 1.8 - 459.67]°F).")
/datum/weather_holder/proc/process()
if(world.time >= next_weather_shift)
- var/new_weather
- if(!current_weather)
- new_weather = pickweight(roundstart_weather_chances)
+ if(!current_weather) // Roundstart (hopefully).
+ initialize_weather()
else
- new_weather = pickweight(current_weather.transition_chances)
- change_weather(new_weather)
+ advance_forecast()
else
current_weather.process_effects()
+
+
+// Should only have to be called once.
+/datum/weather_holder/proc/initialize_weather()
+ if(!current_weather)
+ change_weather(get_next_weather())
+ build_forecast()
+
+// Used to determine what the weather will be soon, in a semi-random fashion.
+// The forecast is made by calling this repeatively, from the bottom (highest index) of the forecast list.
+/datum/weather_holder/proc/get_next_weather(var/datum/weather/W)
+ if(!current_weather) // At roundstart, choose a suitable initial weather.
+ return pickweight(roundstart_weather_chances)
+ return pickweight(W.transition_chances)
+
+/datum/weather_holder/proc/advance_forecast()
+ var/new_weather = forecast[1]
+ forecast.Cut(1, 2) // Remove what we just took out, shortening the list.
+ change_weather(new_weather)
+ build_forecast() // To fill the forecast to the desired length.
+
+// Creates a list of future weather shifts, that the planet will undergo at some point in the future.
+// Determining it ahead of time allows for attentive players to plan further ahead, if they can see the forecast.
+/datum/weather_holder/proc/build_forecast()
+ var/desired_length = 3
+ if(forecast.len >= desired_length)
+ return
+
+ while(forecast.len < desired_length)
+ if(!forecast.len) // If the forecast is empty, the current_weather is used as a base instead.
+ forecast += get_next_weather(current_weather)
+ else
+ var/position = forecast[forecast.len] // Go to the bottom of the list.
+ var/datum/weather/W = allowed_weather_types[position] // Get the actual datum and not a string.
+ var/new_weather = get_next_weather(W) // Get a suitable weather pattern to shift to from this one.
+ forecast += new_weather
+ log_debug("[our_planet.name]'s weather forecast is now '[english_list(forecast, and_text = " then ", final_comma_text = ", ")]'.")
+
+// Wipes the forecast and regenerates it. Used for when the weather is forcefully changed, such as with admin verbs.
+/datum/weather_holder/proc/rebuild_forecast()
+ forecast.Cut()
+ build_forecast()
+
+
+
/datum/weather_holder/proc/update_icon_effects()
visuals.icon_state = current_weather.icon_state
@@ -57,25 +105,41 @@
return allowed_weather_types[desired_type]
+/datum/weather_holder/proc/show_transition_message()
+ if(!current_weather.transition_messages.len)
+ return
+
+ var/message = pick(current_weather.transition_messages) // So everyone gets the same message.
+ for(var/mob/M in player_list) // Don't need to care about clientless mobs.
+ if(M.z in our_planet.expected_z_levels)
+ var/turf/T = get_turf(M)
+ if(!T.outdoors)
+ continue
+ to_chat(M, message)
+
/datum/weather
var/name = "weather base"
var/icon = 'icons/effects/weather.dmi'
var/icon_state = null // Icon to apply to turf undergoing weather.
- var/temp_high = T20C
- var/temp_low = T0C
+ var/temp_high = T20C // Temperature to apply when at noon.
+ var/temp_low = T0C // Temperature to apply when at midnight.
var/light_modifier = 1.0 // Lower numbers means more darkness.
var/light_color = null // If set, changes how the day/night light looks.
- var/flight_failure_modifier = 0 // Some types of weather make flying harder, and therefore make crashes more likely.
- var/transition_chances = list() // Assoc list
- var/datum/weather_holder/holder = null
+ var/flight_failure_modifier = 0 // Some types of weather make flying harder, and therefore make crashes more likely. (This is not implemented)
+ var/transition_chances = list() // Assoc list of weather identifiers and the odds to shift to a specific type of weather. Can contain its own identifier to prolong it.
+ var/datum/weather_holder/holder = null // Reference to the datum that manages the planet's weather.
var/timer_low_bound = 5 // How long this weather must run before it tries to change, in minutes
var/timer_high_bound = 10 // How long this weather can run before it tries to change, in minutes
+ var/sky_visible = FALSE // If the sky can be clearly seen while this is occuring, used for flavor text when looking up.
var/effect_message = null // Should be a string, this is what is shown to a mob caught in the weather
var/last_message = 0 // Keeps track of when the weather last tells EVERY player it's hitting them
var/message_delay = 10 SECONDS // Delay in between weather hit messages
var/show_message = FALSE // Is set to TRUE and plays the messsage every [message_delay]
+ var/list/transition_messages = list()// List of messages shown to all outdoor mobs when this weather is transitioned to, for flavor. Not shown if already this weather.
+ var/observed_message = null // What is shown to a player 'examining' the weather.
+
/datum/weather/proc/process_effects()
show_message = FALSE // Need to reset the show_message var, just in case
if(effect_message) // Only bother with the code below if we actually need to display something
diff --git a/nano/templates/communicator.tmpl b/nano/templates/communicator.tmpl
index 716034a7f4..6d75cbbe4f 100644
--- a/nano/templates/communicator.tmpl
+++ b/nano/templates/communicator.tmpl
@@ -254,7 +254,8 @@ Used In File(s): code\game\objects\items\devices\communicator\communicator.dm
Time: {{:value.Time}}
Weather: {{:value.Weather}}, {{:helper.fixed(value.Temperature)}}°C
- High: {{:helper.fixed(value.High)}}°C | Low: {{:helper.fixed(value.Low)}}°C
+ High: {{:helper.fixed(value.High)}}°C | Low: {{:helper.fixed(value.Low)}}°C
+ Forecast: {{:helper.Forecast}}
{{empty}}
diff --git a/polaris.dme b/polaris.dme
index 0d0d11afc5..068cb8c8bb 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -1540,6 +1540,7 @@
#include "code\modules\ext_scripts\python.dm"
#include "code\modules\flufftext\Dreaming.dm"
#include "code\modules\flufftext\Hallucination.dm"
+#include "code\modules\flufftext\look_up.dm"
#include "code\modules\flufftext\TextFilters.dm"
#include "code\modules\food\recipe_dump.dm"
#include "code\modules\food\recipes_microwave.dm"