Getting Mad at Parallax (Includes Boomerspace!) (#95382)

## About The Pull Request

I want to do stuff with parallax (like placing stuff in the backdrop and
moving it around), but I'm in my not doing 100 things in one pr arc so
we're doing this piecemeal.

To start, I wanted to try adding oldspace back as a parallax layer. This
is something I was considering when it was first removed but never got
around to, so here we are.

I started by [writing a rust
program](https://github.com/LemonInTheDark/old_space_gen) to fabricate
the required icon state, then realized that all the different parts of
space have their own animation delays. There's no way I could make one
icon state with all them looping, so instead I split them up into
multiple components and then overlayed them together.

This works, but is infeasible (my gpu died) at the scale required for
parallax (17 480x480 sets, 15 unique delays per 1 set) so I used render
targets to render one copy, then mirror it to the rest of the overlays.
This works wonderfully, and gets us down to (on my machine) a gpu cost
comprable with about medium parallax intensity.

I'm open to making these tile bound but I thought making them look "far
away" feels better.

In the process of all this I got very mad at the existing parallax code,
soooooooo

Parallax layers are no longer stored on the client, they are stored on
and managed by the parallax home atom that holds them for display. Said
atom also tracks all the information about how they are selected.

Parallax layers no longer take a hud as input, instead expecting a
client. (we were just swapping them back and forth and I thought it was
dumb).

Parallax no longer tries to support passing in a mob that does not
actually own the hud it is displayed on. This feature wasn't even being
used anymore because it was fully broken, so all it was doing was making
the code worse.

Parallax no longer has to do a full refresh anytime something about WHAT
layers are displayed might have changed. We cache based off the
variables we care about, and use the change in state to determine what
should happen (this is improved by moving "rendering the layers" fully
to the control of the home datum).

Parallax no longer directly modifies the hud's plane masters, instead
relying on trait based signals to manipulate them (this avoids wasted
time in the common event of a needless parallax prefs check).

Parallax no longer has 2 procs that are only called together to
"remove/readd/update" the layers, instead doing both in a new check()
proc.

Cleans up some plane master cruft to do with tracking/managing huds
(might break, tested, think it's fine).

## Why It's Good For The Game


https://github.com/user-attachments/assets/79138a0f-9f6d-447d-843e-0d237db13276

## Changelog
🆑
add: Added an option for rendering space parallax with old space sprites
(the ones from before we invented parallax), they're animated and I feel
quite pretty.
fix: Space parallax should hopefully behave a little more consistently
now
refactor: Rewrote a lot of how space parallax handled itself, please
yell at me if any bugs make themselves known
/🆑
This commit is contained in:
LemonInTheDark
2026-03-19 18:46:30 -07:00
committed by GitHub
parent d03ae827f4
commit 3729ee3ef3
16 changed files with 320 additions and 194 deletions
@@ -1,2 +1,2 @@
/// from /datum/plane_master_group/proc/set_hud(): (datum/hud/new_hud)
/// from /datum/plane_master_group/proc/set_hud(): (datum/hud/old_hud, datum/hud/new_hud)
#define COMSIG_GROUP_HUD_CHANGED "group_hud_changed"
+1
View File
@@ -47,6 +47,7 @@
#define PARALLAX_HIGH "High"
#define PARALLAX_MED "Medium"
#define PARALLAX_LOW "Low"
#define PARALLAX_BOOMER "Old"
#define PARALLAX_DISABLE "Disabled"
#define SCALING_METHOD_NORMAL "normal"
+2
View File
@@ -52,6 +52,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
// Hud traits
/// This hud is owned by a client with an open escape menu
#define TRAIT_ESCAPE_MENU_OPEN "escape_menu_open"
/// This hud has parallax displayed on it
#define TRAIT_PARALLAX_DISPLAYED "parallax_displayed"
// Mob traits
/// Forces the user to stay unconscious.
+1
View File
@@ -144,6 +144,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/datum/hud = list(
"TRAIT_ESCAPE_MENU_OPEN" = TRAIT_ESCAPE_MENU_OPEN,
"TRAIT_PARALLAX_DISPLAYED" = TRAIT_PARALLAX_DISPLAYED,
),
/datum/wound = list(
"TRAIT_WOUND_SCANNED" = TRAIT_WOUND_SCANNED,
+1 -1
View File
@@ -393,7 +393,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
screenmob.reload_fullscreen()
if(screenmob == mymob)
update_parallax_pref(screenmob)
update_parallax_pref()
else
viewmob.hud_used.update_parallax_pref()
+214 -138
View File
@@ -1,117 +1,76 @@
/// Decides if parallax should be rendered or not, and sets things up accordingly
/datum/hud/proc/check_parallax()
var/client/displaying_client = mymob.client
if(isnull(displaying_client.parallax_rock))
displaying_client.parallax_rock = new(null, null, displaying_client)
/datum/hud/proc/create_parallax(mob/viewmob)
var/mob/screenmob = viewmob || mymob
var/client/C = screenmob.client
/// Applies our preferences to our existing display
apply_parallax_pref()
var/atom/movable/screen/parallax_home/rock = displaying_client?.parallax_rock
if (!apply_parallax_pref(viewmob)) //don't want shit computers to crash when specing someone with insane parallax, so use the viewer's pref
for(var/atom/movable/screen/plane_master/parallax as anything in get_true_plane_masters(PLANE_SPACE_PARALLAX))
parallax.hide_plane(screenmob)
return
// Because other parts of the code can just REMOVE US FROM THE SCREEN for no reason as a joke
if (rock.displaying_layers)
ADD_TRAIT(src, TRAIT_PARALLAX_DISPLAYED, TRAIT_GENERIC)
displaying_client.screen |= rock
else
REMOVE_TRAIT(src, TRAIT_PARALLAX_DISPLAYED, TRAIT_GENERIC)
displaying_client.screen -= rock
for(var/atom/movable/screen/plane_master/parallax as anything in get_true_plane_masters(PLANE_SPACE_PARALLAX))
parallax.unhide_plane(screenmob)
if(isnull(C.parallax_rock))
C.parallax_rock = new(null, src)
C.screen |= C.parallax_rock
if(!length(C.parallax_layers_cached))
C.parallax_layers_cached = list()
C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_1(null, src)
C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_2(null, src)
C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/planet(null, src)
if(SSparallax.random_layer)
C.parallax_layers_cached += new SSparallax.random_layer.type(null, src, FALSE, SSparallax.random_layer)
C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_3(null, src)
C.parallax_layers = C.parallax_layers_cached.Copy()
if (length(C.parallax_layers) > C.parallax_layers_max)
C.parallax_layers.len = C.parallax_layers_max
C.parallax_rock.vis_contents = C.parallax_layers
// We could do not do parallax for anything except the main plane group
// This could be changed, but it would require refactoring this whole thing
// And adding non client particular hooks for all the inputs, and I do not have the time I'm sorry :(
for(var/atom/movable/screen/plane_master/plane_master as anything in screenmob.hud_used.get_true_plane_masters(PLANE_SPACE))
if(screenmob != mymob)
C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
C.screen += plane_master
// this color makes parallax not black
plane_master.color = list(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0
)
/datum/hud/proc/remove_parallax(mob/viewmob)
var/mob/screenmob = viewmob || mymob
var/client/C = screenmob.client
C.screen -= (C.parallax_rock)
for(var/atom/movable/screen/plane_master/plane_master as anything in screenmob.hud_used.get_true_plane_masters(PLANE_SPACE))
if(screenmob != mymob)
C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
C.screen += plane_master
plane_master.color = initial(plane_master.color)
C.parallax_layers = null
/datum/hud/proc/apply_parallax_pref(mob/viewmob)
var/mob/screenmob = viewmob || mymob
var/turf/screen_location = get_turf(screenmob)
/datum/hud/proc/apply_parallax_pref()
var/turf/screen_location = get_turf(mymob)
var/client/displaying_client = mymob.client
var/atom/movable/screen/parallax_home/rock = displaying_client.parallax_rock
if(SSmapping.level_trait(screen_location?.z, ZTRAIT_NOPARALLAX))
return FALSE
rock.set_layer_settings(layers_to_draw = 0, draw_old_space = FALSE, animate_parallax = FALSE)
return
if (SSlag_switch.measures[DISABLE_PARALLAX] && !HAS_TRAIT(viewmob, TRAIT_BYPASS_MEASURES))
return FALSE
if (SSlag_switch.measures[DISABLE_PARALLAX] && !HAS_TRAIT(mymob, TRAIT_BYPASS_MEASURES))
rock.set_layer_settings(layers_to_draw = 0, draw_old_space = FALSE, animate_parallax = FALSE)
return
var/client/C = screenmob.client
// Default to HIGH
var/parallax_selection = C?.prefs.read_preference(/datum/preference/choiced/parallax) || PARALLAX_HIGH
var/parallax_selection = displaying_client?.prefs.read_preference(/datum/preference/choiced/parallax) || PARALLAX_HIGH
switch(parallax_selection)
if (PARALLAX_INSANE)
C.parallax_layers_max = 5
C.do_parallax_animations = TRUE
return TRUE
rock.set_layer_settings(layers_to_draw = 5, draw_old_space = FALSE, animate_parallax = TRUE)
return
if(PARALLAX_HIGH)
C.parallax_layers_max = 4
C.do_parallax_animations = TRUE
return TRUE
rock.set_layer_settings(layers_to_draw = 4, draw_old_space = FALSE, animate_parallax = TRUE)
return
if (PARALLAX_MED)
C.parallax_layers_max = 3
C.do_parallax_animations = TRUE
return TRUE
rock.set_layer_settings(layers_to_draw = 3, draw_old_space = FALSE, animate_parallax = TRUE)
return
if (PARALLAX_LOW)
C.parallax_layers_max = 1
C.do_parallax_animations = FALSE
return TRUE
rock.set_layer_settings(layers_to_draw = 1, draw_old_space = FALSE, animate_parallax = FALSE)
return
if (PARALLAX_BOOMER)
rock.set_layer_settings(layers_to_draw = 0, draw_old_space = TRUE, animate_parallax = TRUE)
return
if (PARALLAX_DISABLE)
return FALSE
rock.set_layer_settings(layers_to_draw = 0, draw_old_space = FALSE, animate_parallax = FALSE)
return
/datum/hud/proc/update_parallax_pref(mob/viewmob)
var/mob/screen_mob = viewmob || mymob
if(!screen_mob.client)
/datum/hud/proc/update_parallax_pref()
if(!mymob.client)
return
remove_parallax(screen_mob)
create_parallax(screen_mob)
update_parallax(screen_mob)
check_parallax()
update_parallax()
// This sets which way the current shuttle is moving (returns true if the shuttle has stopped moving so the caller can append their animation)
/datum/hud/proc/set_parallax_movedir(new_parallax_movedir = NONE, skip_windups, mob/viewmob)
/datum/hud/proc/set_parallax_movedir(new_parallax_movedir = NONE, skip_windups)
. = FALSE
var/mob/screenmob = viewmob || mymob
var/client/C = screenmob.client
if(new_parallax_movedir == C.parallax_movedir)
var/client/displaying_client = mymob.client
if(new_parallax_movedir == displaying_client.parallax_movedir)
return
var/animation_dir = new_parallax_movedir || C.parallax_movedir
var/animation_dir = new_parallax_movedir || displaying_client.parallax_movedir
var/matrix/new_transform
switch(animation_dir)
if(NORTH)
@@ -124,17 +83,17 @@
new_transform = matrix(1, 0,-480, 0, 1, 0)
var/longest_timer = 0
for(var/key in C.parallax_animate_timers)
deltimer(C.parallax_animate_timers[key])
C.parallax_animate_timers = list()
for(var/atom/movable/screen/parallax_layer/layer as anything in C.parallax_layers)
for(var/key in displaying_client.parallax_animate_timers)
deltimer(displaying_client.parallax_animate_timers[key])
displaying_client.parallax_animate_timers = list()
for(var/atom/movable/screen/parallax_layer/layer as anything in displaying_client.parallax_rock.parallax_layers)
var/scaled_time = PARALLAX_LOOP_TIME / layer.speed
if(new_parallax_movedir == NONE) // If we're stopping, we need to stop on the same dime, yeah?
scaled_time = PARALLAX_LOOP_TIME
longest_timer = max(longest_timer, scaled_time)
if(skip_windups)
update_parallax_motionblur(C, layer, new_parallax_movedir, new_transform)
update_parallax_motionblur(displaying_client, layer, new_parallax_movedir, new_transform)
continue
layer.transform = new_transform
@@ -144,15 +103,15 @@
//queue up another animate so lag doesn't create a shutter
animate(transform = new_transform, time = 0)
animate(transform = matrix(), time = scaled_time / 2)
C.parallax_animate_timers[layer] = addtimer(CALLBACK(src, PROC_REF(update_parallax_motionblur), C, layer, new_parallax_movedir, new_transform), scaled_time, TIMER_CLIENT_TIME|TIMER_STOPPABLE)
displaying_client.parallax_animate_timers[layer] = addtimer(CALLBACK(src, PROC_REF(update_parallax_motionblur), displaying_client, layer, new_parallax_movedir, new_transform), scaled_time, TIMER_CLIENT_TIME|TIMER_STOPPABLE)
C.dont_animate_parallax = world.time + min(longest_timer, PARALLAX_LOOP_TIME)
C.parallax_movedir = new_parallax_movedir
displaying_client.dont_animate_parallax = world.time + min(longest_timer, PARALLAX_LOOP_TIME)
displaying_client.parallax_movedir = new_parallax_movedir
/datum/hud/proc/update_parallax_motionblur(client/C, atom/movable/screen/parallax_layer/layer, new_parallax_movedir, matrix/new_transform)
if(!C)
/datum/hud/proc/update_parallax_motionblur(client/displaying_client, atom/movable/screen/parallax_layer/layer, new_parallax_movedir, matrix/new_transform)
if(!displaying_client)
return
C.parallax_animate_timers -= layer
displaying_client.parallax_animate_timers -= layer
// If we are moving in a direction, we used the QUAD_EASING function with EASE_IN
// This means our position function is x^2. This is always LESS then the linear we're using here
@@ -164,33 +123,34 @@
animate(layer, transform = new_transform, time = 0, loop = -1, flags = ANIMATION_END_NOW)
animate(transform = matrix(), time = scaled_time)
/datum/hud/proc/update_parallax(mob/viewmob)
var/mob/screenmob = viewmob || mymob
var/client/C = screenmob.client
var/turf/posobj = get_turf(C.eye)
/datum/hud/proc/update_parallax()
var/client/displaying_client = mymob.client
var/turf/posobj = get_turf(displaying_client.eye)
if(!posobj)
return
var/area/areaobj = posobj.loc
// Update the movement direction of the parallax if necessary (for shuttles)
set_parallax_movedir(areaobj.parallax_movedir, FALSE, screenmob)
set_parallax_movedir(areaobj.parallax_movedir, FALSE, mymob)
if(!C.previous_turf || (C.previous_turf.z != posobj.z))
C.previous_turf = posobj
if(!displaying_client.previous_turf || (displaying_client.previous_turf.z != posobj.z))
displaying_client.previous_turf = posobj
//Doing it this way prevents parallax layers from "jumping" when you change Z-Levels.
var/offset_x = posobj.x - C.previous_turf.x
var/offset_y = posobj.y - C.previous_turf.y
var/offset_x = posobj.x - displaying_client.previous_turf.x
var/offset_y = posobj.y - displaying_client.previous_turf.y
var/glide_rate = round(ICON_SIZE_ALL / screenmob.glide_size * world.tick_lag, world.tick_lag)
C.previous_turf = posobj
var/glide_rate = round(ICON_SIZE_ALL / mymob.glide_size * world.tick_lag, world.tick_lag)
displaying_client.previous_turf = posobj
var/largest_change = max(abs(offset_x), abs(offset_y))
var/max_allowed_dist = (glide_rate / world.tick_lag) + 1
// If we aren't already moving/don't allow parallax, have made some movement, and that movement was smaller then our "glide" size, animate
var/run_parralax = (C.do_parallax_animations && glide_rate && !areaobj.parallax_movedir && C.dont_animate_parallax <= world.time && largest_change <= max_allowed_dist)
var/atom/movable/screen/parallax_home/rock = displaying_client.parallax_rock
for(var/atom/movable/screen/parallax_layer/parallax_layer as anything in C.parallax_layers)
// If we aren't already moving/don't allow parallax, have made some movement, and that movement was smaller then our "glide" size, animate
var/run_parralax = (rock.animate_parallax && glide_rate && !areaobj.parallax_movedir && displaying_client.dont_animate_parallax <= world.time && largest_change <= max_allowed_dist)
for(var/atom/movable/screen/parallax_layer/parallax_layer as anything in rock.parallax_layers)
var/our_speed = parallax_layer.speed
var/change_x
var/change_y
@@ -232,15 +192,15 @@
/atom/movable/proc/update_parallax_contents()
for(var/mob/client_mob as anything in client_mobs_in_contents)
if(length(client_mob?.client?.parallax_layers) && client_mob.hud_used)
if(client_mob?.client?.parallax_rock?.displaying_layers && client_mob.hud_used)
client_mob.hud_used.update_parallax()
/mob/proc/update_parallax_teleport() //used for arrivals shuttle
if(client?.eye && hud_used && length(client.parallax_layers))
if(client?.eye && hud_used && client?.parallax_rock?.displaying_layers)
var/area/areaobj = get_area(client.eye)
hud_used.set_parallax_movedir(areaobj.parallax_movedir, TRUE)
// Root object for parallax, all parallax layers are drawn onto this
// Root object for parallax, all parallax layers are drawn onto this and it manages them
INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_home)
/atom/movable/screen/parallax_home
icon = null
@@ -248,6 +208,87 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_home)
plane = PLANE_SPACE_PARALLAX
screen_loc = "CENTER-7,CENTER-7"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/// Layers we are currently displaying
var/list/atom/movable/screen/parallax_layer/parallax_layers = list()
/// Pallet of layers we CAN display if we choose to, depending on our client's prefs
/// ensures quick removal/reinsertion doesn't cause cycling qdels
var/list/atom/movable/screen/parallax_layer/parallax_layers_cached = list()
/// How many normal space layers we want to draw, in increasing order of "depth"
var/layers_to_draw = 0
/// If we want to draw the old space layer
var/draw_old_space = FALSE
/// Are we currently displaying any layers?
var/displaying_layers = FALSE
/// Are we animating parallax?
var/animate_parallax = FALSE
/// The client that owns us
var/client/owner
/atom/movable/screen/parallax_home/Initialize(mapload, datum/hud/hud_owner, client/owner)
. = ..()
src.owner = owner
/atom/movable/screen/parallax_home/Destroy()
clear_layers()
owner = null
return ..()
/atom/movable/screen/parallax_home/proc/display_layers()
if(displaying_layers || length(parallax_layers_cached) == 0)
return
parallax_layers = parallax_layers_cached
vis_contents = parallax_layers_cached
displaying_layers = TRUE
/atom/movable/screen/parallax_home/proc/hide_layers()
if(!displaying_layers)
return
parallax_layers = list()
vis_contents = list()
displaying_layers = FALSE
/atom/movable/screen/parallax_home/proc/set_layer_settings(layers_to_draw, draw_old_space, animate_parallax)
src.animate_parallax = animate_parallax
if(src.layers_to_draw == layers_to_draw && src.draw_old_space == draw_old_space)
return
src.layers_to_draw = layers_to_draw
src.draw_old_space = draw_old_space
regenerate_layers()
/atom/movable/screen/parallax_home/proc/generate_space_layer(index)
switch(index)
if(1)
return new /atom/movable/screen/parallax_layer/layer_1(null, null, owner)
if(2)
return new /atom/movable/screen/parallax_layer/layer_2(null, null, owner)
if(3)
return new /atom/movable/screen/parallax_layer/planet(null, null, owner)
if(4)
if(SSparallax.random_layer)
return new SSparallax.random_layer.type(null, null, owner, FALSE, SSparallax.random_layer)
else
return new /atom/movable/screen/parallax_layer/layer_3(null, null, owner)
if(5)
if(SSparallax.random_layer)
return new /atom/movable/screen/parallax_layer/layer_3(null, null, owner)
/atom/movable/screen/parallax_home/proc/regenerate_layers()
clear_layers()
if(layers_to_draw == 0 && !draw_old_space)
return
parallax_layers_cached = list()
for(var/space_layer in 1 to layers_to_draw)
parallax_layers_cached += generate_space_layer(space_layer)
if(draw_old_space)
parallax_layers_cached += new /atom/movable/screen/parallax_layer/old(null, null, owner)
display_layers()
/atom/movable/screen/parallax_home/proc/clear_layers()
hide_layers()
QDEL_LIST(parallax_layers_cached)
// We need parallax to always pass its args down into initialize, so we immediate init it
INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
@@ -261,8 +302,10 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
blend_mode = BLEND_ADD
plane = PLANE_SPACE_PARALLAX
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/// View size we're being rendered with
var/working_view = ""
/atom/movable/screen/parallax_layer/Initialize(mapload, datum/hud/hud_owner, template = FALSE)
/atom/movable/screen/parallax_layer/Initialize(mapload, datum/hud/hud_owner, client/owner, template = FALSE)
. = ..()
// Parallax layers are independent of hud, they care about client
// Not doing this will just create a bunch of hard deletes
@@ -271,42 +314,48 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
if(template)
return
var/client/boss = hud_owner?.mymob?.canon_client
if(!boss) // If this typepath all starts to harddel your culprit is likely this
if(!owner) // If this typepath all starts to harddel your culprit is likely this
return INITIALIZE_HINT_QDEL
// I do not want to know bestie
var/view = boss.view || world.view
var/view = owner.view || world.view
update_o(view)
RegisterSignal(boss, COMSIG_VIEW_SET, PROC_REF(on_view_change))
RegisterSignal(owner, COMSIG_VIEW_SET, PROC_REF(on_view_change))
/atom/movable/screen/parallax_layer/proc/on_view_change(datum/source, new_size)
SIGNAL_HANDLER
update_o(new_size)
/atom/movable/screen/parallax_layer/proc/update_o(view)
if (!view)
view = world.view
var/static/pixel_grid_size = ICON_SIZE_ALL * 15
var/static/parallax_scaler = ICON_SIZE_ALL / pixel_grid_size
/atom/movable/screen/parallax_layer/proc/update_o(new_view)
if(working_view == new_view)
return
working_view = new_view
update_appearance()
/atom/movable/screen/parallax_layer/update_overlays()
. = ..()
var/overlay_view = working_view
if (!overlay_view)
overlay_view = world.view
var/pixel_grid_size = ICON_SIZE_ALL * 15
var/parallax_scaler = ICON_SIZE_ALL / pixel_grid_size
// Turn the view size into a grid of correctly scaled overlays
var/list/viewscales = getviewsize(view)
var/list/viewscales = getviewsize(overlay_view)
// This could be half the size but we need to provide space for parallax movement on mob movement, and movement on scroll from shuttles, so like this instead
var/countx = (CEILING((viewscales[1] / 2) * parallax_scaler, 1) + 1)
var/county = (CEILING((viewscales[2] / 2) * parallax_scaler, 1) + 1)
var/list/new_overlays = new
for(var/x in -countx to countx)
for(var/y in -county to county)
if(x == 0 && y == 0)
continue
var/mutable_appearance/texture_overlay = mutable_appearance(icon, icon_state)
var/mutable_appearance/texture_overlay = tileable_appearance()
texture_overlay.pixel_w += pixel_grid_size * x
texture_overlay.pixel_z += pixel_grid_size * y
new_overlays += texture_overlay
cut_overlays()
add_overlay(new_overlays)
. += texture_overlay
/atom/movable/screen/parallax_layer/proc/tileable_appearance()
return mutable_appearance(icon, icon_state)
/atom/movable/screen/parallax_layer/layer_1
icon_state = "layer1"
@@ -323,6 +372,34 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
speed = 1.4
layer = 3
/atom/movable/screen/parallax_layer/old
icon = null
icon_state = null // dog there's gonna be so many overlays...
speed = 0.6
layer = 1 // Draws on its own
/atom/movable/screen/parallax_layer/old/tileable_appearance()
var/mutable_appearance/copy = mutable_appearance(null, "")
// We have to use render targets to draw one of these flat and reuse it for this because FOR SOME REASON
// 16 (tile count) * (14 (animated state count) * 4 (frame count) + 1 (1 is not animated)) 480x480 states
// is TOO MUCH for the client. Whatever, see if I care.
copy.render_source = "*old_space_parallax"
return copy
/atom/movable/screen/parallax_layer/old/update_overlays()
. = ..()
var/mutable_appearance/relayed_overlay = mutable_appearance('icons/effects/old_parallax.dmi', "1", appearance_flags = RESET_TRANSFORM|PIXEL_SCALE|KEEP_TOGETHER|KEEP_APART)
var/list/old_states = list("19", "21", "23", "24", "26", "29", "30", "31", "34", "35", "36", "37", "43", "46")
var/list/holder_overlays = list()
for(var/state in old_states)
holder_overlays += mutable_appearance('icons/effects/old_parallax.dmi', state)
relayed_overlay.overlays = holder_overlays
relayed_overlay.render_target = "*old_space_parallax"
// Renders the like, "input" appearance we draw to everything else
. += relayed_overlay
// The 0,0 appearance, can't reuse relayed_overlay for this because otherwise transforms would stack
. += tileable_appearance()
/atom/movable/screen/parallax_layer/planet
icon_state = "planet"
blend_mode = BLEND_OVERLAY
@@ -330,17 +407,16 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
speed = 3
layer = 30
/atom/movable/screen/parallax_layer/planet/Initialize(mapload, datum/hud/hud_owner)
/atom/movable/screen/parallax_layer/planet/Initialize(mapload, datum/hud/hud_owner, client/owner)
. = ..()
var/client/boss = hud_owner?.mymob?.canon_client
if(!boss)
if(!owner)
return
var/static/list/connections = list(
COMSIG_MOVABLE_Z_CHANGED = PROC_REF(on_z_change),
COMSIG_MOB_LOGOUT = PROC_REF(on_mob_logout),
)
AddComponent(/datum/component/connect_mob_behalf, boss, connections)
on_z_change(hud_owner?.mymob)
AddComponent(/datum/component/connect_mob_behalf, owner, connections)
on_z_change(owner.mob)
/atom/movable/screen/parallax_layer/planet/proc/on_mob_logout(mob/source)
SIGNAL_HANDLER
+1 -1
View File
@@ -4,7 +4,7 @@
speed = 2
layer = 3
/atom/movable/screen/parallax_layer/random/Initialize(mapload, datum/hud/hud_owner, template, atom/movable/screen/parallax_layer/random/twin)
/atom/movable/screen/parallax_layer/random/Initialize(mapload, datum/hud/hud_owner, client/owner, template, atom/movable/screen/parallax_layer/random/twin)
. = ..()
if(twin)
@@ -34,12 +34,13 @@
if(our_hud)
our_hud.master_groups -= key
hide_hud()
var/datum/hud/old_hud = our_hud
our_hud = new_hud
if(new_hud)
our_hud.master_groups[key] = src
show_hud()
build_planes_offset(our_hud, active_offset)
SEND_SIGNAL(src, COMSIG_GROUP_HUD_CHANGED, our_hud)
SEND_SIGNAL(src, COMSIG_GROUP_HUD_CHANGED, old_hud, our_hud)
/// Display a plane master group to some viewer, so show all our planes to it
/datum/plane_master_group/proc/attach_to(datum/hud/viewing_hud)
@@ -67,6 +67,36 @@
. = ..()
add_relay_to(GET_NEW_PLANE(RENDER_PLANE_EMISSIVE, offset), relay_layer = EMISSIVE_SPACE_LAYER)
/atom/movable/screen/plane_master/parallax_white/set_home(datum/plane_master_group/home)
. = ..()
if(home)
RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
hud_changed(null, null, home.our_hud)
/atom/movable/screen/plane_master/parallax_white/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
SIGNAL_HANDLER
if(old_hud)
UnregisterSignal(old_hud, list(SIGNAL_ADDTRAIT(TRAIT_PARALLAX_DISPLAYED), SIGNAL_REMOVETRAIT(TRAIT_PARALLAX_DISPLAYED)), PROC_REF(parallax_updated))
if(new_hud)
RegisterSignals(new_hud, list(SIGNAL_ADDTRAIT(TRAIT_PARALLAX_DISPLAYED), SIGNAL_REMOVETRAIT(TRAIT_PARALLAX_DISPLAYED)), PROC_REF(parallax_updated))
parallax_updated(new_hud)
/atom/movable/screen/plane_master/parallax_white/proc/parallax_updated(datum/source)
SIGNAL_HANDLER
if(isnull(home.our_hud?.mymob))
return
if(HAS_TRAIT(home.our_hud, TRAIT_PARALLAX_DISPLAYED))
// Gives parallax a fullwhite backdrop to multiply against
color = list(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0
)
else
color = initial(color)
///Contains space parallax
/atom/movable/screen/plane_master/parallax
name = "Parallax"
@@ -92,6 +122,29 @@
narsie_start_midway(GLOB.narsie_effect_last_modified) // We assume we're on the start, so we can use this number
offset_increase(0, SSmapping.max_plane_offset)
/atom/movable/screen/plane_master/parallax/set_home(datum/plane_master_group/home)
. = ..()
if(home)
RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
hud_changed(null, null, home.our_hud)
/atom/movable/screen/plane_master/parallax/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
SIGNAL_HANDLER
if(old_hud)
UnregisterSignal(old_hud, list(SIGNAL_ADDTRAIT(TRAIT_PARALLAX_DISPLAYED), SIGNAL_REMOVETRAIT(TRAIT_PARALLAX_DISPLAYED)), PROC_REF(parallax_updated))
if(new_hud)
RegisterSignals(new_hud, list(SIGNAL_ADDTRAIT(TRAIT_PARALLAX_DISPLAYED), SIGNAL_REMOVETRAIT(TRAIT_PARALLAX_DISPLAYED)), PROC_REF(parallax_updated))
parallax_updated(new_hud)
/atom/movable/screen/plane_master/parallax/proc/parallax_updated(datum/source)
SIGNAL_HANDLER
if(isnull(home.our_hud?.mymob))
return
if(HAS_TRAIT(home.our_hud, TRAIT_PARALLAX_DISPLAYED))
show_to(home.our_hud.mymob)
else
hide_from(home.our_hud.mymob)
/atom/movable/screen/plane_master/parallax/proc/on_offset_increase(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
offset_increase(old_offset, new_offset)
@@ -401,19 +454,19 @@
plane = CAMERA_STATIC_PLANE
render_relay_planes = list(RENDER_PLANE_GAME)
/atom/movable/screen/plane_master/camera_static/show_to(mob/mymob)
/atom/movable/screen/plane_master/camera_static/set_home(datum/plane_master_group/home)
. = ..()
if(!.)
return
var/datum/hud/our_hud = home.our_hud
if(isnull(our_hud))
return
if(home)
RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
hud_changed(null, null, home.our_hud)
// We'll hide the slate if we're not seeing through a camera eye
// This can call on a cycle cause we don't clear in hide_from
// Yes this is the best way of hooking into the hud, I hate myself too
RegisterSignal(our_hud, COMSIG_HUD_EYE_CHANGED, PROC_REF(eye_changed), override = TRUE)
eye_changed(our_hud, null, our_hud.mymob?.canon_client?.eye)
/atom/movable/screen/plane_master/camera_static/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
SIGNAL_HANDLER
if(old_hud)
UnregisterSignal(old_hud, COMSIG_HUD_EYE_CHANGED, PROC_REF(eye_changed))
if(new_hud)
RegisterSignal(new_hud, COMSIG_HUD_EYE_CHANGED, PROC_REF(eye_changed))
eye_changed(new_hud, null, new_hud.mymob?.canon_client?.eye)
/atom/movable/screen/plane_master/camera_static/proc/eye_changed(datum/hud/source, atom/old_eye, atom/new_eye)
SIGNAL_HANDLER
+26 -27
View File
@@ -202,6 +202,12 @@
add_filter("emissives", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(EMISSIVE_RENDER_TARGET, offset), flags = MASK_INVERSE))
set_light_cutoff(10)
/atom/movable/screen/plane_master/rendering_plate/lighting/set_home(datum/plane_master_group/home)
. = ..()
if(home)
RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
hud_changed(null, null, home.our_hud)
/atom/movable/screen/plane_master/rendering_plate/lighting/show_to(mob/mymob)
. = ..()
if(!.)
@@ -217,23 +223,20 @@
backdrop = mymob.overlay_fullscreen("lighting_backdrop_unlit_[home.key]#[offset]", /atom/movable/screen/fullscreen/lighting_backdrop/unlit)
SET_PLANE_EXPLICIT(backdrop, PLANE_TO_TRUE(backdrop.plane), src)
// Sorry, this is a bit annoying
// Basically, we only want the lighting plane we can actually see to attempt to render
// If we don't our lower plane gets totally overriden by the black void of the upper plane
var/datum/hud/hud = home.our_hud
// show_to can be called twice successfully with no hide_from call. Ensure no runtimes off the registers from this
if(hud)
RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change), override = TRUE)
offset_change(hud?.current_plane_offset || 0)
set_light_cutoff(mymob.lighting_cutoff, mymob.lighting_color_cutoffs)
/atom/movable/screen/plane_master/rendering_plate/lighting/hide_from(mob/oldmob)
. = ..()
oldmob.clear_fullscreen("lighting_backdrop_lit_[home.key]#[offset]")
oldmob.clear_fullscreen("lighting_backdrop_unlit_[home.key]#[offset]")
var/datum/hud/hud = home.our_hud
if(hud)
UnregisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
/atom/movable/screen/plane_master/rendering_plate/lighting/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
SIGNAL_HANDLER
if(old_hud)
UnregisterSignal(old_hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
if(new_hud)
RegisterSignal(new_hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
offset_change(new_hud?.current_plane_offset || 0)
/atom/movable/screen/plane_master/rendering_plate/lighting/proc/on_offset_change(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
@@ -442,26 +445,22 @@
plane = RENDER_PLANE_MASTER
render_relay_planes = list()
/atom/movable/screen/plane_master/rendering_plate/master/show_to(mob/mymob)
/atom/movable/screen/plane_master/rendering_plate/master/set_home(datum/plane_master_group/home)
. = ..()
if(!.)
return
if(offset == 0)
return
// Non 0 offset render plates will relay up to the transparent plane above them, assuming they're not on the same z level as their target of course
var/datum/hud/hud = home.our_hud
// show_to can be called twice successfully with no hide_from call. Ensure no runtimes off the registers from this
if(hud)
RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change), override = TRUE)
offset_change(hud?.current_plane_offset || 0)
/atom/movable/screen/plane_master/rendering_plate/master/hide_from(mob/oldmob)
. = ..()
if(offset == 0)
return
var/datum/hud/hud = home.our_hud
if(hud)
UnregisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
if(home)
RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
hud_changed(null, null, home.our_hud)
/atom/movable/screen/plane_master/rendering_plate/master/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
SIGNAL_HANDLER
if(old_hud)
UnregisterSignal(old_hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
if(new_hud)
RegisterSignal(new_hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
offset_change(new_hud?.current_plane_offset || 0)
/atom/movable/screen/plane_master/rendering_plate/master/proc/on_offset_change(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
+4 -3
View File
@@ -71,7 +71,7 @@ SUBSYSTEM_DEF(parallax)
if(picked_parallax == PARALLAX_NONE)
return
random_layer = new picked_parallax(null, /* hud_owner = */ null, /* template = */ TRUE)
random_layer = new picked_parallax(null, /* hud_owner = */ null, /* owner = */ null, /* template = */ TRUE)
RegisterSignal(random_layer, COMSIG_QDELETING, PROC_REF(clear_references))
random_layer.get_random_look()
@@ -85,8 +85,9 @@ SUBSYSTEM_DEF(parallax)
//Parallax is one of the first things to be set (during client join), so rarely is anything fast enough to swap it out
//That's why we need to swap the layers out for fast joining clients :/
for(var/client/client as anything in GLOB.clients)
client.parallax_layers_cached?.Cut()
client.mob?.hud_used?.update_parallax_pref(client.mob)
// gotta clear things out
client?.parallax_rock?.set_layer_settings(0, FALSE, FALSE)
client.mob?.hud_used?.update_parallax_pref()
/datum/controller/subsystem/parallax/proc/clear_references()
SIGNAL_HANDLER
@@ -55,7 +55,7 @@
else
care_about.hide_plane(our_lad)
/datum/component/hide_weather_planes/proc/new_hud_attached(datum/source, datum/hud/new_hud)
/datum/component/hide_weather_planes/proc/new_hud_attached(datum/source, datum/hud/old_hud, datum/hud/new_hud)
SIGNAL_HANDLER
attach_hud(new_hud)
-7
View File
@@ -186,8 +186,6 @@
///A lazy list of atoms we've examined in the last RECENT_EXAMINE_MAX_WINDOW (default 2) seconds, so that we will call [/atom/proc/examine_more] instead of [/atom/proc/examine] on them when examining
var/list/recent_examines
var/list/parallax_layers
var/list/parallax_layers_cached
var/atom/movable/screen/parallax_home/parallax_rock
///this is the last recorded client eye by SSparallax/fire()
var/atom/movable/movingmob
@@ -196,13 +194,8 @@
var/dont_animate_parallax
/// Direction our current area wants to move parallax
var/parallax_movedir = 0
/// How many parallax layers to show our client
var/parallax_layers_max = 4
/// Timers for the area directional animation, one for each layer
var/list/parallax_animate_timers
/// Do we want to do parallax animations at all?
/// Exists to prevent laptop fires
var/do_parallax_animations = TRUE
///Are we locking our movement input?
var/movement_locked = FALSE
-2
View File
@@ -645,8 +645,6 @@ GLOBAL_LIST_INIT(unrecommended_builds, list(
QDEL_NULL(tooltips)
QDEL_NULL(loot_panel)
QDEL_NULL(parallax_rock)
QDEL_LIST(parallax_layers_cached)
parallax_layers = null
seen_messages = null
Master.UpdateTickRate()
..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening
+2 -1
View File
@@ -10,6 +10,7 @@
PARALLAX_HIGH,
PARALLAX_MED,
PARALLAX_LOW,
PARALLAX_BOOMER,
PARALLAX_DISABLE,
)
@@ -17,7 +18,7 @@
return PARALLAX_HIGH
/datum/preference/choiced/parallax/apply_to_client(client/client, value)
client.mob?.hud_used?.update_parallax_pref(client?.mob)
client.mob?.hud_used?.update_parallax_pref()
/datum/preference/choiced/parallax/deserialize(input, datum/preferences/preferences)
// Old preferences were numbers, which causes annoyances when
Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB