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"