From a4dcdebf3974a8ab6e4179e5c6490f5017d63ade Mon Sep 17 00:00:00 2001 From: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Date: Mon, 15 Jun 2026 21:57:30 +0200 Subject: [PATCH] Invests /tg/station Foundation funds into NVIDIA stocks (Adds a particle weather toggle) (#96510) ## About The Pull Request Due to a BYOND bug, large amounts of particles will cause immense lag and framedrops on AMD GPUs, no matter how good the GPU itself is. Unfortunately this is most noticeable with new particle weather, to the point where storms reduce framerate to single digits for players who haven't invested into NVIDIA before the AI market boom. By rendering overlays twice and only enabling one of the planes, we can allow players to toggle between new particle weather (semi-transparent overlays + particles) and old (fully opaque overlays) Unfortunately we don't have a way to check the player's GPU (at least not easily) so players will have to change it themselves https://github.com/user-attachments/assets/0c36fca4-c8a4-42ed-895f-42e853b10f44 (Particle weather is a bit more dense without ghost vision but i'm too lazy to re-record) Closes #96460 (Sort of) ## Changelog :cl: qol: Particle weather can now be disabled in favor of old weather. !!! AMD GPU USERS NEED TO SWITCH TO OLD WEATHER TO FIX THE LAG!!! /:cl: --- code/__DEFINES/layers.dm | 9 +++--- .../plane_masters/plane_master_subtypes.dm | 29 +++++++++++++++++-- code/_onclick/hud/rendering/render_plate.dm | 27 +++++++++++++---- code/datums/weather/particle_weather.dm | 8 +++++ code/datums/weather/weather.dm | 14 +++++---- .../datums/weather/weather_types/ash_storm.dm | 2 +- .../weather/weather_types/floor_is_lava.dm | 2 +- .../client/preferences/particle_weather.dm | 15 ++++++++++ tgstation.dme | 1 + .../game_preferences/particle_weather.tsx | 9 ++++++ 10 files changed, 96 insertions(+), 20 deletions(-) create mode 100644 code/modules/client/preferences/particle_weather.dm create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/particle_weather.tsx diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 4d7a26858a2..c9140515a06 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -36,10 +36,11 @@ #define DEFAULT_PLANE 0 //Marks out the default plane, even if we don't use it #define WEATHER_PLANE 1 -#define AREA_PLANE 2 -#define MASSIVE_OBJ_PLANE 3 -#define GHOST_PLANE 4 -#define POINT_PLANE 5 +#define PARTICLE_WEATHER_PLANE 2 +#define AREA_PLANE 3 +#define MASSIVE_OBJ_PLANE 4 +#define GHOST_PLANE 5 +#define POINT_PLANE 6 //---------- LIGHTING ------------- /// Normal 1 per turf dynamic lighting objects diff --git a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm index 094371cc9e6..b5dc090c9aa 100644 --- a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm +++ b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm @@ -316,17 +316,40 @@ plane = AREA_PLANE /atom/movable/screen/plane_master/weather - name = "Weather" - documentation = "Holds the main tiling 32x32 sprites of weather. We mask against walls that are on the edge of weather effects." + name = "Non-Particle Weather" + documentation = "Holds the main tiling 32x32 sprites of weather. We mask against walls that are on the edge of weather effects. Used when the player has particle weather disabled." plane = WEATHER_PLANE start_hidden = TRUE critical = PLANE_CRITICAL_DISPLAY + /// Is this a particle variant? + var/particle_weather = FALSE /atom/movable/screen/plane_master/weather/set_home(datum/plane_master_group/home) . = ..() if(!.) return - home.AddComponent(/datum/component/hide_weather_planes, src) + home.AddComponent(/datum/component/hide_weather_planes, src, particle_weather) + +/atom/movable/screen/plane_master/weather/proc/update_state(mob/mymob) + if(!istype(mymob)) + return + + // If the client wants particle weather, only show the PARTICLE_WEATHER_PLANE, otherwise only show the normal WEATHER_PLANE + if (mymob.canon_client?.prefs?.read_preference(/datum/preference/toggle/particle_weather) != particle_weather) + hide_from(mymob) + else + show_to(mymob) + +/atom/movable/screen/plane_master/weather/show_to(mob/mymob) + // Only show ourselves if the player wants it + if (mymob.canon_client?.prefs?.read_preference(/datum/preference/toggle/particle_weather) == particle_weather) + return ..() + +/atom/movable/screen/plane_master/weather/particle + name = "Particle Weather" + documentation = "Holds the main tiling 32x32 sprites of weather. Used when the player has particle weather enabled." + plane = PARTICLE_WEATHER_PLANE + particle_weather = TRUE /atom/movable/screen/plane_master/weather_mask name = "Weather Mask" diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm index 11979dde2ac..7dd681d378c 100644 --- a/code/_onclick/hud/rendering/render_plate.dm +++ b/code/_onclick/hud/rendering/render_plate.dm @@ -84,6 +84,27 @@ . = ..() // We can actually do this just fine as we do not render anything onto ourselves but our particles add_filter("weather_mask", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(WEATHER_MASK_RENDER_TARGET, offset))) + +/atom/movable/screen/plane_master/rendering_plate/particle_weather/set_home(datum/plane_master_group/home) + . = ..() + if(!.) + return + home.AddComponent(/datum/component/hide_weather_planes, src, TRUE) + RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed)) + update_state(home.our_hud?.mymob) + +/atom/movable/screen/plane_master/rendering_plate/particle_weather/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud) + SIGNAL_HANDLER + update_state(new_hud?.mymob) + +/// Updates ourselves based on our mob's preferences state +/atom/movable/screen/plane_master/rendering_plate/particle_weather/proc/update_state(mob/mymob) + SSweather.particle_planemasters -= src + vis_contents.Cut() + + if(!istype(mymob) || !mymob.canon_client?.prefs?.read_preference(/datum/preference/toggle/particle_weather)) + return + SSweather.particle_planemasters += src // And add all ongoing weather to ourselves for (var/holder_offset, holder_list in SSweather.particle_holders) @@ -95,12 +116,6 @@ SSweather.particle_planemasters -= src return ..() -/atom/movable/screen/plane_master/rendering_plate/particle_weather/set_home(datum/plane_master_group/home) - . = ..() - if(!.) - return - home.AddComponent(/datum/component/hide_weather_planes, src, TRUE) - /atom/movable/screen/plane_master/rendering_plate/particle_weather/emissive name = "Emissive Particle Weather" documentation = "Secondary particle weather plane for emissive parts of weather, which is additionally rendered onto the emissive plane after being masked." diff --git a/code/datums/weather/particle_weather.dm b/code/datums/weather/particle_weather.dm index 7ac48e4ae36..1c7f385b092 100644 --- a/code/datums/weather/particle_weather.dm +++ b/code/datums/weather/particle_weather.dm @@ -16,6 +16,8 @@ /// How often can we change our severity? /// Don't set this too low or it'll look jank var/severity_cooldown = 5 SECONDS + /// Alpha of the area overlay when the particle weather pref is enabled + var/particle_weather_alpha = 255 /// Current weather severity var/severity = 0 @@ -101,9 +103,15 @@ /datum/weather/particle/generate_overlay_cache() . = ..() + if (stage == END_STAGE) return + // Change alpha of overlays on the particle weather plane + for(var/mutable_appearance/overlay as anything in .) + if (PLANE_TO_TRUE(overlay.plane) == PARTICLE_WEATHER_PLANE) + overlay.alpha = particle_weather_alpha + for (var/offset in 0 to SSmapping.max_plane_offset) . += mutable_appearance('icons/effects/weather_overlay.dmi', "weather_overlay", overlay_layer, null, WEATHER_MASK_PLANE, offset_const = offset) diff --git a/code/datums/weather/weather.dm b/code/datums/weather/weather.dm index de0d5cfc057..36260744241 100644 --- a/code/datums/weather/weather.dm +++ b/code/datums/weather/weather.dm @@ -76,8 +76,9 @@ /// Since it's above everything else, this is the layer used by default. var/overlay_layer = AREA_LAYER - /// Plane for the overlay - var/overlay_plane = WEATHER_PLANE + /// Planes for the overlay + /// Base visuals should always render to both particle and non-particle planes as to work regardless of the toggle + var/list/overlay_planes = list(WEATHER_PLANE, PARTICLE_WEATHER_PLANE) /// Used by mobs (or movables containing mobs, such as enviro bags) to prevent them from being affected by the weather. var/immunity_type /// If this bit of weather should also draw an overlay that's uneffected by lighting onto the area @@ -586,9 +587,12 @@ glow_overlay.color = weather_color gen_overlay_cache += glow_overlay - var/mutable_appearance/new_weather_overlay = mutable_appearance('icons/effects/weather_effects.dmi', weather_state, overlay_layer, plane = overlay_plane, alpha = weather_alpha, offset_const = offset) - new_weather_overlay.color = weather_color - gen_overlay_cache += new_weather_overlay + // By default we render ourselves to both particle and non-particle weather, as those are mutually exclusive + // So that particle weather can have full alpha overlays when the pref is disabled, but partially transparent overlays when its enabled + for (var/overlay_plane in overlay_planes) + var/mutable_appearance/new_weather_overlay = mutable_appearance('icons/effects/weather_effects.dmi', weather_state, overlay_layer, plane = overlay_plane, alpha = weather_alpha, offset_const = offset) + new_weather_overlay.color = weather_color + gen_overlay_cache += new_weather_overlay return gen_overlay_cache diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm index ae12115e367..16d04c8e19b 100644 --- a/code/datums/weather/weather_types/ash_storm.dm +++ b/code/datums/weather/weather_types/ash_storm.dm @@ -7,7 +7,7 @@ emissive_type = /particles/weather/ash_storm/embers min_severity = 60 optimal_severity = 80 - weather_alpha = 100 + particle_weather_alpha = 100 wind_sign = -1 // Always blows left to sync with the animated overlays telegraph_message = span_boldwarning("An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter.") diff --git a/code/datums/weather/weather_types/floor_is_lava.dm b/code/datums/weather/weather_types/floor_is_lava.dm index 2dcbbed0aa7..536c3461a2b 100644 --- a/code/datums/weather/weather_types/floor_is_lava.dm +++ b/code/datums/weather/weather_types/floor_is_lava.dm @@ -19,7 +19,7 @@ target_trait = ZTRAIT_STATION overlay_layer = ABOVE_OPEN_TURF_LAYER //Covers floors only - overlay_plane = FLOOR_PLANE + overlay_planes = list(FLOOR_PLANE) immunity_type = TRAIT_LAVA_IMMUNE /// We don't draw on walls, so this ends up lookin weird /// Can't really use like, the emissive system here because I am not about to make diff --git a/code/modules/client/preferences/particle_weather.dm b/code/modules/client/preferences/particle_weather.dm new file mode 100644 index 00000000000..a9185eb5d13 --- /dev/null +++ b/code/modules/client/preferences/particle_weather.dm @@ -0,0 +1,15 @@ +/// Whether or not to toggle ambient occlusion, the shadows around people +/datum/preference/toggle/particle_weather + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "particle_weather" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/toggle/particle_weather/apply_to_client(client/client, value) + for(var/atom/movable/screen/plane_master/rendering_plate/particle_weather/plane_master as anything in client.mob?.hud_used?.get_true_plane_masters(RENDER_PLANE_PARTICLE_WEATHER)) + plane_master.update_state(client.mob) + + for(var/atom/movable/screen/plane_master/weather/plane_master as anything in client.mob?.hud_used?.get_true_plane_masters(WEATHER_PLANE)) + plane_master.update_state(client.mob) + + for(var/atom/movable/screen/plane_master/weather/plane_master as anything in client.mob?.hud_used?.get_true_plane_masters(PARTICLE_WEATHER_PLANE)) + plane_master.update_state(client.mob) diff --git a/tgstation.dme b/tgstation.dme index 68072b4f464..a26c769c843 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4139,6 +4139,7 @@ #include "code\modules\client\preferences\paint_color.dm" #include "code\modules\client\preferences\parallax.dm" #include "code\modules\client\preferences\paraplegic.dm" +#include "code\modules\client\preferences\particle_weather.dm" #include "code\modules\client\preferences\pda.dm" #include "code\modules\client\preferences\persistent_scars.dm" #include "code\modules\client\preferences\phobia.dm" diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/particle_weather.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/particle_weather.tsx new file mode 100644 index 00000000000..cf4824e6ddb --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/particle_weather.tsx @@ -0,0 +1,9 @@ +import { CheckboxInput, type FeatureToggle } from '../base'; + +export const particle_weather: FeatureToggle = { + name: 'Enable fancy (particle) weather (AMD GPU incompatible)', + category: 'GAMEPLAY', + description: + 'Enable fancy particle weather. Incompatible with AMD GPUs and will cause heavy lag on them due to a BYOND bug.', + component: CheckboxInput, +};