mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-13 00:55:20 +01:00
3729ee3ef3
## 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 /🆑
219 lines
8.6 KiB
Plaintext
219 lines
8.6 KiB
Plaintext
/// Datum that represents one "group" of plane masters
|
|
/// So all the main window planes would be in one, all the spyglass planes in another
|
|
/// Etc
|
|
/datum/plane_master_group
|
|
/// Our key in the group list on /datum/hud
|
|
/// Should be unique for any group of plane masters in the world
|
|
var/key
|
|
/// Our parent hud
|
|
var/datum/hud/our_hud
|
|
/// List in the form "[plane]" = object, the plane masters we own
|
|
var/list/atom/movable/screen/plane_master/plane_masters = list()
|
|
/// The visual offset we are currently using
|
|
var/active_offset = 0
|
|
/// What, if any, submap we render onto
|
|
var/map = ""
|
|
/// Controls the screen_loc that owned plane masters will use when generating relays. Due to a Byond bug, relays using the CENTER positional loc
|
|
/// Will be improperly offset
|
|
var/relay_loc = "1,1"
|
|
|
|
/datum/plane_master_group/New(key, map = "")
|
|
. = ..()
|
|
src.key = key
|
|
src.map = map
|
|
build_plane_masters(0, SSmapping.max_plane_offset)
|
|
|
|
/datum/plane_master_group/Destroy()
|
|
set_hud(null)
|
|
QDEL_LIST_ASSOC_VAL(plane_masters)
|
|
return ..()
|
|
|
|
/datum/plane_master_group/proc/set_hud(datum/hud/new_hud)
|
|
if(new_hud == our_hud)
|
|
return
|
|
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, 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)
|
|
if(viewing_hud.master_groups[key])
|
|
stack_trace("Hey brother, our key [key] is already in use by a plane master group on the passed in hud, belonging to [viewing_hud.mymob]. Ya fucked up, why are there dupes")
|
|
return
|
|
|
|
set_hud(viewing_hud)
|
|
our_hud.master_groups[key] = src
|
|
show_hud()
|
|
build_planes_offset(our_hud, active_offset)
|
|
|
|
/// Well, refresh our group, mostly useful for plane specific updates
|
|
/datum/plane_master_group/proc/refresh_hud()
|
|
hide_hud()
|
|
show_hud()
|
|
|
|
/// Fully regenerate our group, resetting our planes to their compile time values
|
|
/datum/plane_master_group/proc/rebuild_hud()
|
|
hide_hud()
|
|
rebuild_plane_masters()
|
|
show_hud()
|
|
our_hud.update_parallax_pref()
|
|
build_planes_offset(our_hud, active_offset)
|
|
|
|
/// Regenerate our plane masters, this is useful if we don't have a mob but still want to rebuild. Such in the case of changing the screen_loc of relays
|
|
/datum/plane_master_group/proc/rebuild_plane_masters()
|
|
QDEL_LIST_ASSOC_VAL(plane_masters)
|
|
build_plane_masters(0, SSmapping.max_plane_offset)
|
|
|
|
/datum/plane_master_group/proc/hide_hud()
|
|
for(var/thing in plane_masters)
|
|
var/atom/movable/screen/plane_master/plane = plane_masters[thing]
|
|
plane.hide_from(our_hud.mymob)
|
|
|
|
/datum/plane_master_group/proc/show_hud()
|
|
for(var/thing in plane_masters)
|
|
var/atom/movable/screen/plane_master/plane = plane_masters[thing]
|
|
show_plane(plane)
|
|
|
|
/// This is mostly a proc so it can be overriden by popups, since they have unique behavior they want to do
|
|
/datum/plane_master_group/proc/show_plane(atom/movable/screen/plane_master/plane)
|
|
plane.show_to(our_hud.mymob)
|
|
|
|
/// Nice wrapper for the "[]"ing
|
|
/datum/plane_master_group/proc/get_plane(plane)
|
|
return plane_masters["[plane]"]
|
|
|
|
/// Returns a list of all the plane master types we want to create
|
|
/datum/plane_master_group/proc/get_plane_types()
|
|
return subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/rendering_plate
|
|
|
|
/// Actually generate our plane masters, in some offset range (where offset is the z layers to render to, because each "layer" in a multiz stack gets its own plane master cube)
|
|
/datum/plane_master_group/proc/build_plane_masters(starting_offset, ending_offset)
|
|
for(var/atom/movable/screen/plane_master/mytype as anything in get_plane_types())
|
|
for(var/plane_offset in starting_offset to ending_offset)
|
|
if(plane_offset != 0 && (initial(mytype.offsetting_flags) & BLOCKS_PLANE_OFFSETTING))
|
|
continue
|
|
var/atom/movable/screen/plane_master/instance = new mytype(null, null, src, plane_offset)
|
|
plane_masters["[instance.plane]"] = instance
|
|
prep_plane_instance(instance)
|
|
|
|
/// Similarly, exists so subtypes can do unique behavior to planes on creation
|
|
/datum/plane_master_group/proc/prep_plane_instance(atom/movable/screen/plane_master/instance)
|
|
return
|
|
|
|
// It would be nice to setup parallaxing for stairs and things when doing this
|
|
// So they look nicer. if you can't it's all good, if you think you can sanely look at monster's work
|
|
// It's hard, and potentially expensive. be careful
|
|
/datum/plane_master_group/proc/build_planes_offset(datum/hud/source, new_offset, use_scale = TRUE)
|
|
// Check if this feature is disabled for the client, in which case don't use scale.
|
|
var/mob/our_mob = our_hud?.mymob
|
|
if(!our_mob?.client?.prefs?.read_preference(/datum/preference/toggle/multiz_parallax))
|
|
use_scale = FALSE
|
|
|
|
// No offset? piss off
|
|
if(!SSmapping.max_plane_offset)
|
|
return
|
|
|
|
active_offset = new_offset
|
|
|
|
// Each time we go "down" a visual z level, we'll reduce the scale by this amount
|
|
// Chosen because mothblocks liked it, didn't cause motion sickness while also giving a sense of height
|
|
var/scale_by = 0.965
|
|
if(!use_scale)
|
|
// This is a workaround for two things
|
|
// First of all, if a mob can see objects but not turfs, they will not be shown the holder objects we use for
|
|
// What I'd like to do is revert to images if this case throws, but image vis_contents is broken
|
|
// https://www.byond.com/forum/post/2821969
|
|
// If that's ever fixed, please just use that. thanks :)
|
|
scale_by = 1
|
|
|
|
var/list/offsets = list()
|
|
var/multiz_boundary = our_mob?.client?.prefs?.read_preference(/datum/preference/numeric/multiz_performance)
|
|
|
|
// We accept negatives so going down "zooms" away the drop above as it goes
|
|
for(var/offset in -SSmapping.max_plane_offset to SSmapping.max_plane_offset)
|
|
// Multiz boundaries disable transforms
|
|
if(multiz_boundary != MULTIZ_PERFORMANCE_DISABLE && (multiz_boundary < abs(offset)))
|
|
offsets += null
|
|
continue
|
|
|
|
// No transformations if we're landing ON you
|
|
if(offset == 0)
|
|
offsets += null
|
|
continue
|
|
|
|
var/scale = scale_by ** (offset)
|
|
var/matrix/multiz_shrink = matrix()
|
|
multiz_shrink.Scale(scale)
|
|
offsets += multiz_shrink
|
|
|
|
// So we can talk in 1 -> max_offset * 2 + 1, rather then -max_offset -> max_offset
|
|
var/offset_offset = SSmapping.max_plane_offset + 1
|
|
|
|
for(var/plane_key in plane_masters)
|
|
var/atom/movable/screen/plane_master/plane = plane_masters[plane_key]
|
|
if(plane.offsetting_flags & BLOCKS_PLANE_OFFSETTING)
|
|
if(plane.offsetting_flags & OFFSET_RELAYS_MATCH_HIGHEST)
|
|
// Don't offset the plane, do offset where the relays point
|
|
// Required for making things like the blind fullscreen not render over runechat
|
|
plane.offset_relays_in_place(new_offset)
|
|
continue
|
|
|
|
var/visual_offset = plane.offset - new_offset
|
|
|
|
// Basically uh, if we're showing something down X amount of levels, or up any amount of levels
|
|
if(multiz_boundary != MULTIZ_PERFORMANCE_DISABLE && (visual_offset > multiz_boundary || visual_offset < 0))
|
|
plane.outside_bounds(our_mob)
|
|
else if(plane.is_outside_bounds)
|
|
plane.inside_bounds(our_mob)
|
|
|
|
if(!plane.multiz_scaled)
|
|
continue
|
|
|
|
if(plane.force_hidden || plane.is_outside_bounds || visual_offset < 0)
|
|
// We don't animate here because it should be invisble, but we do mark because it'll look nice
|
|
plane.transform = offsets[visual_offset + offset_offset]
|
|
continue
|
|
|
|
animate(plane, transform = offsets[visual_offset + offset_offset], 0.05 SECONDS, easing = LINEAR_EASING)
|
|
|
|
/// Holds plane masters for popups, like camera windows
|
|
/// Note: We do not scale this plane, even though we could
|
|
/// This is because it's annoying to get turfs to position inside it correctly
|
|
/// If you wanna try someday feel free, but I can't manage it
|
|
/datum/plane_master_group/popup
|
|
|
|
/datum/plane_master_group/popup/build_planes_offset(datum/hud/source, new_offset, use_scale = TRUE)
|
|
return ..(source, new_offset, FALSE)
|
|
|
|
/// Holds the main plane master
|
|
/datum/plane_master_group/main
|
|
|
|
/datum/plane_master_group/main/build_planes_offset(datum/hud/source, new_offset, use_scale = TRUE)
|
|
if(use_scale)
|
|
return ..(source, new_offset, source.should_use_scale())
|
|
return ..()
|
|
|
|
/// Hudless group. Exists for testing
|
|
/datum/plane_master_group/hudless
|
|
var/mob/our_mob
|
|
|
|
/datum/plane_master_group/hudless/Destroy()
|
|
. = ..()
|
|
our_mob = null
|
|
|
|
/datum/plane_master_group/hudless/hide_hud()
|
|
for(var/thing in plane_masters)
|
|
var/atom/movable/screen/plane_master/plane = plane_masters[thing]
|
|
plane.hide_from(our_mob)
|
|
|
|
/// This is mostly a proc so it can be overriden by popups, since they have unique behavior they want to do
|
|
/datum/plane_master_group/hudless/show_plane(atom/movable/screen/plane_master/plane)
|
|
plane.show_to(our_mob)
|