mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-30 03:22:41 +00:00
* Nightvision Rework (In the name of color) (#73094) Relies on #72886 for some render relay expansion I use for light_mask stuff. Hello bestie! Night vision pissed me off, so I've come to burn this place to the ground. Two sections to discuss here. First we'll talk about see_in_dark and why I hate it, second we'll discuss the lighting plane and how we brighten it, plus introducing color to the party. https://www.byond.com/docs/ref/#/mob/var/see_in_dark See in dark lets us control how far away from us a turf can be before we hide it/its contents if it's dark (not got luminosity set) We currently set it semi inconsistently to provide nightvision to mobs. The trouble is stuff that produces light != stuff that sets luminosity. The worst case of this can be seen by walking out of escape on icebox, where you'll see this  Snow draws above the lighting plane, so the snow will intermittently draw, depending on see_in_dark and the luminosity from tracking lights. This would in theory be solvable by modifying the area, but the same problem applies across many things in the codebase. As things currently stand, to be emissive you NEED to have a light on your tile. People are bad at this, and honestly it's a bit much to expect of them. An emissive overlay on a canister shouldn't need an element or something and a list on turfs to manage it. This gets worse when you factor in the patterns I'm using to avoid drawing lights above nothing, which leads to lights that should show, but are misoffset because their parent pixel offsets. It's silly. We do it so we can have things like mesons without just handing out night vision, but even there the effect of just hiding objects and mobs looks baddddddd when moving. It's always bothered me. I'll complain about mesons more later, but really just like, they're too bright as it is. I'm proposing here that rather then manually hiding stuff based off distance from the player, we can instead show/hide using just the lighting plane. This means things like mesons are gonna get dimmer, but that's fine because they suck. It does have some side effects, things like view() on mobs won't hide stuff in darkness, but that's fine because none actually thinks about view like that, I think. Oh and I added a case to prevent examining stuff that's in darkness, and not right next to you when you don't have enough nightvision, to match the old behavior `see_in_dark` gave us. Now I'd like to go on a mild tangent about color, please bare with me You ever walk around with mesons on when there's a fire going, or an ethereal or firelocks down. You notice how there isn't really much color to our lights? Doesn't that suck? It's because the way we go about brighting lighting is by making everything on the lighting plane transparent. This is fine for brightening things, but it ends up looking kinda crummy in the end and leads to really washed out colors that should be bright. Playing engineer or miner gets fucking depressing. The central idea of this pr, that everything else falls out of, is instead of making the plane more transparent, we can use color matrixes to make things AT LEAST x bright. https://www.byond.com/docs/ref/#/{notes}/color-matrix Brief recap for color matrixes, fully expanded they're a set of 20 different values in a list Units generally scale 0-1 as multipliers, though since it's multiplication in order to make an rgb(1,1,1) pixel fullbright you would need to use 255s. A "unit matrix" for color looks like this: ``` list(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ) ``` The first four rows are how much each r, g, b and a impact r, g, b and well a. So a first row of `(1, 0, 0, 0)` means 1 unit of r results in 1 unit of r. and 0 units of green, blue and alpha, and so on. A first row of `(0, 1, 0, 0)` would make 1 red component into 1 green component, and leave red, blue and alpha alone, shifting any red of whatever it's applied to a green. Using these we can essentially color transform our world. It's a fun tool. But there's more. That last row there doesn't take a variable input like the others. Instead, it ADDS some fraction of 255 to red, green, blue and alpha. So a fifth row of `(1, 0, 0, 0)` would make every pixel as red as it could possibly be. This is what we're going to exploit here. You see all these values accept negative multipliers, so we can lower colors down instead of raising them up! The key idea is using color matrix filters https://www.byond.com/docs/ref/#/{notes}/filters/color to chain these operations together. Pulling alllll the way back, we want to brighten darkness without affecting brighter colors. Lower rgb values are darker, higher ones are brighter. This relationship isn't really linear because of suffering reasons, but it's good enough for this. Let's try chaining some matrixes on the lighting plane, which is bright where fullbright, and dark where dark. Take a list like this ``` list(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -0.2, -0.2, -0.2, 0 ) ``` That would darken the lighting a bit, but negative values will get rounded to 0 A subsequent raising by the same amount ``` list(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0.2, 0.2, 0.2, 0 ) ``` Will essentially threshold our brightness at that value. This ensures we aren't washing out colors when we make things brighter, while leaving higher values unaffected since they basically just had a constant subtracted and then readded. You may have noticed, we gain access to individual color components here. This means not only can we darken and lighten by thresholds, we can COLOR those thresholds. ``` list(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0.1, 0.2, 0.1, 0 ) ``` Something like the above, if applied with its inverse, would tint the darkness green. The delta between the different scalars will determine how vivid the color is, and the actual value will impact the brightness. Something that's always bothered me about nightvision is it's just greyscale for the most part, there isn't any color to it. There was an old idea of coloring the game plane to match their lenses, but if you've ever played with the colorblind quirk you know that gets headachey really fast. So instead of that, lets color just the darkness that these glasses produce. It provides some reminder that you're wearing them, instead of just being something you forget about while playing, and provides a reason to use flashlights and such since they can give you a clearer, less tinted view of things while retaining the ability to look around things. I've so far applied this pattern to JUST headwear for humans (also those mining wisps) I'm planning on furthering it to mobs that use nightvision, but I wanted to get this up cause I don't wanna pr it the day before the freeze. Mesons are green, sec night vision is red, thermals orange, etc. I think the effect this gives is really really nice. I've tuned most things to work for the station, though mesons works for lavaland for obvious reasons. I've tuned things significantly darker then we have them set currently, since I really hate flat lighting and this system suffers when interacting with it. My goal with these is to give you a rough idea of what's around you, without a good eye for detail. That's the difference between say, mesons, and night vision. One helps you see outlines, the other gives you detail and prevents missing someone in the darkness. It's hard to balance this precisely because of different colored backgrounds (looking at you icebox) More can be done on this front in future but I'm quite happy with things as of now I have since expanded to all uses of nightvision, coloring most all of them. Along the way I turned some toggleable nightvision into just one level. Fullbright sucks, and I'd rather just have one "good" value. I've kept it for a few cases, mostly eyes you rip out of mobs. Impacted mobs are nightmares, aliens, zombies, revenants, states and sort of stands. I've done a pass on all mobs and items that impact nightvision and added what I thought was the right level of color to them. This includes stuff like blobs and shuttle control consoles As with glasses much of this was around reducing vision, though I kept it stronger here, since many of these mobs rely on it for engaging with the game <details> <summary> Technical Changes </summary> filter transitions. Found this when testing this pr, seemed silly. This avoids dumbass overlay lighting lighting up wallmounts. We switch modes if some turfflags are set, to accomplish the same thing with more overhead, and support showing things through the darkness. Also fixes a bug where you'd only get one fullscreen object per mob, so opening and closing a submap would take it away Also also fixes the lighting backdrop not actually spanning the screen. It doesn't actually do anything anymore because of the fullscreen light we have, but just in case that's unsued. Needs cleanup in future. color with a sprite This is to support the above We relay this plane to lighting mask so openspace can like, have lighting vision goggles and such Side affect of removing see_in_dark. This logic is a bit weak atm, needs some work. It's a dupe of the nightvision action button, and newly redundant since I've removed all uses of it trasnparent won't render These sucked Also transparent stuff should never render, if it does you'll get white blobs which suck </details> Videos! (Github doesn't like using a summary here I'm sorry) <details> Demonstration of ghost lighting, and color https://user-images.githubusercontent.com/58055496/215693983-99e00f9e-7214-4cf4-a76a-6e669a8a1103.mp4 Engi-glass mesons and walking in maint (Potentially overtuned, yellow is hard) https://user-images.githubusercontent.com/58055496/215695978-26e7dc45-28aa-4285-ae95-62ea3d79860f.mp4 Diagnostic nightvision goggles and see_in_dark not hiding emissives https://user-images.githubusercontent.com/58055496/215692233-115b4094-1099-4393-9e94-db2088d834f3.mp4 Sec nightvision (I just think it looks neat) https://user-images.githubusercontent.com/58055496/215692269-bc08335e-0223-49c3-9faf-d2d7b22fe2d2.mp4 Medical nightvision goggles and other colors https://user-images.githubusercontent.com/58055496/215692286-0ba3de6a-b1d5-4aed-a6eb-c32794ea45da.mp4 Miner mesons and mobs hiding in lavaland (This is basically the darkest possible environment) https://user-images.githubusercontent.com/58055496/215696327-26958b69-0e1c-4412-9298-4e9e68b3df68.mp4 Thermal goggles and coloring displayed mobs https://user-images.githubusercontent.com/58055496/215692710-d2b101f3-7922-498c-918c-9b528d181430.mp4 </details> I think it's pretty, and see_in_dark sucks butt. <!-- If your PR modifies aspects of the game that can be concretely observed by players or admins you should add a changelog. If your change does NOT meet this description, remove this section. Be sure to properly mark your PRs to prevent unnecessary GBP loss. You can read up on GBP and it's effects on PRs in the tgstation guides for contributors. Please note that maintainers freely reserve the right to remove and add tags should they deem it appropriate. You can attempt to finagle the system all you want, but it's best to shoot for clear communication right off the bat. --> 🆑 add: The darkness that glasses and hud goggles that impact your nightvision (think mesons, nightvision goggles, etc) lighten is now tinted to match the glasses. S pretty IMO, and hopefully it helps with forgetting you're wearing X. balance: Nightvision is darker. I think bright looks bad, and things like mesons do way too much balance: Mesons (and mobs in general) no longer have a static distance you can see stuff in the dark. If a tile is lit, you can now see it. fix: Nightvision no longer dims colored lights, instead simply thresholding off bits of darkness that are dimmer then some level. /🆑 * modular edits * see_in_dark * [MIRROR] Adds a unit test to detect double stacked lights [MDB IGNORE] (#19564) * Adds a unit test to detect double stacked lights * we really need to get that night vision pr done * lints fixes --------- Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: Paxilmaniac <paxilmaniac@gmail.com> * Update augments_eyes.dm * Update augments_eyes.dm * eeee --------- Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Co-authored-by: Paxilmaniac <paxilmaniac@gmail.com> Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
642 lines
29 KiB
Plaintext
642 lines
29 KiB
Plaintext
// I hate this place
|
|
INITIALIZE_IMMEDIATE(/atom/movable/screen/plane_master)
|
|
|
|
/atom/movable/screen/plane_master
|
|
screen_loc = "CENTER"
|
|
icon_state = "blank"
|
|
appearance_flags = PLANE_MASTER
|
|
blend_mode = BLEND_OVERLAY
|
|
plane = LOWEST_EVER_PLANE
|
|
/// Will be sent to the debug ui as a description for each plane
|
|
/// Also useful as a place to explain to coders how/why your plane works, and what it's meant to do
|
|
/// Plaintext and basic html are fine to use here.
|
|
/// I'll bonk you if I find you putting "lmao stuff" in here, make this useful.
|
|
var/documentation = ""
|
|
/// Our real alpha value, so alpha can persist through being hidden/shown
|
|
var/true_alpha = 255
|
|
/// Tracks if we're using our true alpha, or being manipulated in some other way
|
|
var/alpha_enabled = TRUE
|
|
|
|
/// The plane master group we're a member of, our "home"
|
|
var/datum/plane_master_group/home
|
|
|
|
/// If our plane master allows for offsetting
|
|
/// Mostly used for planes that really don't need to be duplicated, like the hud planes
|
|
var/allows_offsetting = TRUE
|
|
/// Our offset from our "true" plane, see below
|
|
var/offset
|
|
/// When rendering multiz, lower levels get their own set of plane masters
|
|
/// Real plane here represents the "true" plane value of something, ignoring the offset required to handle lower levels
|
|
var/real_plane
|
|
|
|
//--rendering relay vars--
|
|
/// list of planes we will relay this plane's render to
|
|
var/list/render_relay_planes = list(RENDER_PLANE_GAME)
|
|
/// blend mode to apply to the render relay in case you dont want to use the plane_masters blend_mode
|
|
var/blend_mode_override
|
|
/// list of current relays this plane is utilizing to render
|
|
var/list/atom/movable/render_plane_relay/relays = list()
|
|
/// if render relays have already be generated
|
|
var/relays_generated = FALSE
|
|
|
|
/// If this plane master should be hidden from the player at roundstart
|
|
/// We do this so PMs can opt into being temporary, to reduce load on clients
|
|
var/start_hidden = FALSE
|
|
/// If this plane master is being forced to hide.
|
|
/// Hidden PMs will dump ANYTHING relayed or drawn onto them. Be careful with this
|
|
/// Remember: a hidden plane master will dump anything drawn directly to it onto the output render. It does NOT hide its contents
|
|
/// Use alpha for that
|
|
var/force_hidden = FALSE
|
|
|
|
/// If this plane should be scaled by multiz
|
|
/// Planes with this set should NEVER be relay'd into each other, as that will cause visual fuck
|
|
var/multiz_scaled = TRUE
|
|
|
|
/// Bitfield that describes how this plane master will render if its z layer is being "optimized"
|
|
/// If a plane master is NOT critical, it will be completely dropped if we start to render outside a client's multiz boundary prefs
|
|
/// Of note: most of the time we will relay renders to non critical planes in this stage. so the plane master will end up drawing roughly "in order" with its friends
|
|
/// This is NOT done for parallax and other problem children, because the rules of BLEND_MULTIPLY appear to not behave as expected :(
|
|
/// This will also just make debugging harder, because we do fragile things in order to ensure things operate as epected. I'm sorry
|
|
/// Compile time
|
|
/// See [code\__DEFINES\layers.dm] for our bitflags
|
|
var/critical = NONE
|
|
|
|
/// If this plane master is outside of our visual bounds right now
|
|
var/is_outside_bounds = FALSE
|
|
|
|
/atom/movable/screen/plane_master/Initialize(mapload, datum/plane_master_group/home, offset = 0)
|
|
. = ..()
|
|
src.offset = offset
|
|
true_alpha = alpha
|
|
real_plane = plane
|
|
|
|
if(!set_home(home))
|
|
return INITIALIZE_HINT_QDEL
|
|
update_offset()
|
|
if(!documentation && !(istype(src, /atom/movable/screen/plane_master) || istype(src, /atom/movable/screen/plane_master/rendering_plate)))
|
|
stack_trace("Plane master created without a description. Document how your thing works so people will know in future, and we can display it in the debug menu")
|
|
if(start_hidden)
|
|
hide_plane(home.our_hud?.mymob)
|
|
generate_render_relays()
|
|
|
|
/atom/movable/screen/plane_master/Destroy()
|
|
if(home)
|
|
// NOTE! We do not clear ourselves from client screens
|
|
// We relay on whoever qdel'd us to reset our hud, and properly purge us
|
|
home.plane_masters -= "[plane]"
|
|
home = null
|
|
. = ..()
|
|
QDEL_LIST(relays)
|
|
|
|
/// Sets the plane group that owns us, it also determines what screen we render to
|
|
/// Returns FALSE if the set_home fails, TRUE otherwise
|
|
/atom/movable/screen/plane_master/proc/set_home(datum/plane_master_group/home)
|
|
if(!istype(home, /datum/plane_master_group))
|
|
return FALSE
|
|
src.home = home
|
|
if(home.map)
|
|
screen_loc = "[home.map]:[screen_loc]"
|
|
assigned_map = home.map
|
|
return TRUE
|
|
|
|
/// Updates our "offset", basically what layer of multiz we're meant to render
|
|
/// Top is 0, goes up as you go down
|
|
/// It's taken into account by render targets and relays, so we gotta make sure they're on the same page
|
|
/atom/movable/screen/plane_master/proc/update_offset()
|
|
name = "[initial(name)] #[offset]"
|
|
SET_PLANE_W_SCALAR(src, real_plane, offset)
|
|
for(var/i in 1 to length(render_relay_planes))
|
|
render_relay_planes[i] = GET_NEW_PLANE(render_relay_planes[i], offset)
|
|
if(initial(render_target))
|
|
render_target = OFFSET_RENDER_TARGET(initial(render_target), offset)
|
|
|
|
/atom/movable/screen/plane_master/proc/set_alpha(new_alpha)
|
|
true_alpha = new_alpha
|
|
if(!alpha_enabled)
|
|
return
|
|
alpha = new_alpha
|
|
|
|
/atom/movable/screen/plane_master/proc/disable_alpha()
|
|
alpha_enabled = FALSE
|
|
alpha = 0
|
|
|
|
/atom/movable/screen/plane_master/proc/enable_alpha()
|
|
alpha_enabled = TRUE
|
|
alpha = true_alpha
|
|
|
|
/// Shows a plane master to the passed in mob
|
|
/// Override this to apply unique effects and such
|
|
/// Returns TRUE if the call is allowed, FALSE otherwise
|
|
/atom/movable/screen/plane_master/proc/show_to(mob/mymob)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
if(force_hidden)
|
|
return FALSE
|
|
|
|
var/client/our_client = mymob?.client
|
|
// Alright, let's get this out of the way
|
|
// Mobs can move z levels without their client. If this happens, we need to ensure critical display settings are respected
|
|
// This is done here. Mild to severe pain but it's nessesary
|
|
if(check_outside_bounds())
|
|
if(!(critical & PLANE_CRITICAL_DISPLAY))
|
|
return FALSE
|
|
if(!our_client)
|
|
return TRUE
|
|
our_client.screen += src
|
|
|
|
if(!(critical & PLANE_CRITICAL_NO_RELAY))
|
|
our_client.screen += relays
|
|
return TRUE
|
|
return TRUE
|
|
|
|
if(!our_client)
|
|
return TRUE
|
|
|
|
our_client.screen += src
|
|
our_client.screen += relays
|
|
return TRUE
|
|
|
|
/// Hook to allow planes to work around is_outside_bounds
|
|
/// Return false to allow a show, true otherwise
|
|
/atom/movable/screen/plane_master/proc/check_outside_bounds()
|
|
return is_outside_bounds
|
|
|
|
/// Hides a plane master from the passeed in mob
|
|
/// Do your effect cleanup here
|
|
/atom/movable/screen/plane_master/proc/hide_from(mob/oldmob)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
var/client/their_client = oldmob?.client
|
|
if(!their_client)
|
|
return
|
|
their_client.screen -= src
|
|
their_client.screen -= relays
|
|
|
|
|
|
/// Forces this plane master to hide, until unhide_plane is called
|
|
/// This allows us to disable unused PMs without breaking anything else
|
|
/atom/movable/screen/plane_master/proc/hide_plane(mob/cast_away)
|
|
force_hidden = TRUE
|
|
hide_from(cast_away)
|
|
|
|
/// Disables any forced hiding, allows the plane master to be used as normal
|
|
/atom/movable/screen/plane_master/proc/unhide_plane(mob/enfold)
|
|
force_hidden = FALSE
|
|
show_to(enfold)
|
|
|
|
/// Mirrors our force hidden state to the hidden state of the plane that came before, assuming it's valid
|
|
/// This allows us to mirror any hidden sets from before we were created, no matter how low that chance is
|
|
/atom/movable/screen/plane_master/proc/mirror_parent_hidden()
|
|
var/mob/our_mob = home?.our_hud?.mymob
|
|
var/atom/movable/screen/plane_master/true_plane = our_mob?.hud_used?.get_plane_master(plane)
|
|
if(true_plane == src || !true_plane)
|
|
return
|
|
|
|
if(true_plane.force_hidden == force_hidden)
|
|
return
|
|
|
|
// If one of us already exists and it's not hidden, unhide ourselves
|
|
if(true_plane.force_hidden)
|
|
hide_plane(our_mob)
|
|
else
|
|
unhide_plane(our_mob)
|
|
|
|
/atom/movable/screen/plane_master/proc/outside_bounds(mob/relevant)
|
|
if(force_hidden || is_outside_bounds)
|
|
return
|
|
is_outside_bounds = TRUE
|
|
// If we're of critical importance, AND we're below the rendering layer
|
|
if(critical & PLANE_CRITICAL_DISPLAY)
|
|
// We here assume that your render target starts with *
|
|
if(critical & PLANE_CRITICAL_CUT_RENDER && render_target)
|
|
render_target = copytext_char(render_target, 2)
|
|
if(!(critical & PLANE_CRITICAL_NO_RELAY))
|
|
return
|
|
var/client/our_client = relevant.client
|
|
if(our_client)
|
|
for(var/atom/movable/render_plane_relay/relay as anything in relays)
|
|
our_client.screen -= relay
|
|
|
|
return
|
|
hide_from(relevant)
|
|
|
|
/atom/movable/screen/plane_master/proc/inside_bounds(mob/relevant)
|
|
is_outside_bounds = FALSE
|
|
if(critical & PLANE_CRITICAL_DISPLAY)
|
|
// We here assume that your render target starts with *
|
|
if(critical & PLANE_CRITICAL_CUT_RENDER && render_target)
|
|
render_target = "*[render_target]"
|
|
|
|
if(!(critical & PLANE_CRITICAL_NO_RELAY))
|
|
return
|
|
var/client/our_client = relevant.client
|
|
if(our_client)
|
|
for(var/atom/movable/render_plane_relay/relay as anything in relays)
|
|
our_client.screen += relay
|
|
|
|
return
|
|
show_to(relevant)
|
|
|
|
/atom/movable/screen/plane_master/clickcatcher
|
|
name = "Click Catcher"
|
|
documentation = "Contains the screen object we use as a backdrop to catch clicks on portions of the screen that would otherwise contain nothing else. \
|
|
<br>Will always be below almost everything else"
|
|
plane = CLICKCATCHER_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
multiz_scaled = FALSE
|
|
critical = PLANE_CRITICAL_DISPLAY
|
|
|
|
/atom/movable/screen/plane_master/clickcatcher/Initialize(mapload, datum/plane_master_group/home, offset)
|
|
. = ..()
|
|
RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(offset_increased))
|
|
offset_increased(SSmapping, 0, SSmapping.max_plane_offset)
|
|
|
|
/atom/movable/screen/plane_master/clickcatcher/proc/offset_increased(datum/source, old_off, new_off)
|
|
SIGNAL_HANDLER
|
|
// We only want need the lowest level
|
|
// If my system better supported changing PM plane values mid op I'd do that, but I do NOT so
|
|
if(new_off > offset)
|
|
hide_plane(home?.our_hud?.mymob)
|
|
|
|
/atom/movable/screen/plane_master/parallax_white
|
|
name = "Parallax whitifier"
|
|
documentation = "Essentially a backdrop for the parallax plane. We're rendered just below it, so we'll be multiplied by its well, parallax.\
|
|
<br>If you want something to look as if it has parallax on it, draw it to this plane."
|
|
plane = PLANE_SPACE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_relay_planes = list(RENDER_PLANE_GAME, LIGHT_MASK_PLANE)
|
|
critical = PLANE_CRITICAL_FUCKO_PARALLAX // goes funny when touched. no idea why I don't trust byond
|
|
|
|
/atom/movable/screen/plane_master/parallax_white/Initialize(mapload, datum/plane_master_group/home, offset)
|
|
. = ..()
|
|
add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_SPACE_LAYER)
|
|
|
|
///Contains space parallax
|
|
/atom/movable/screen/plane_master/parallax
|
|
name = "Parallax"
|
|
documentation = "Contains parallax, or to be more exact the screen objects that hold parallax.\
|
|
<br>Note the BLEND_MULTIPLY. The trick here is how low our plane value is. Because of that, we draw below almost everything in the game.\
|
|
<br>We abuse this to ensure we multiply against the Parallax whitifier plane, or space's plane. It's set to full white, so when you do the multiply you just get parallax out where it well, makes sense to be.\
|
|
<br>Also notice that the parent parallax plane is mirrored down to all children. We want to support viewing parallax across all z levels at once."
|
|
plane = PLANE_SPACE_PARALLAX
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
blend_mode = BLEND_MULTIPLY
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
multiz_scaled = FALSE
|
|
|
|
/atom/movable/screen/plane_master/parallax/Initialize(mapload, datum/plane_master_group/home, offset)
|
|
. = ..()
|
|
if(offset != 0)
|
|
// You aren't the source? don't change yourself
|
|
return
|
|
RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(on_offset_increase))
|
|
offset_increase(0, SSmapping.max_plane_offset)
|
|
|
|
/atom/movable/screen/plane_master/parallax/proc/on_offset_increase(datum/source, old_offset, new_offset)
|
|
SIGNAL_HANDLER
|
|
offset_increase(old_offset, new_offset)
|
|
|
|
/atom/movable/screen/plane_master/parallax/proc/offset_increase(old_offset, new_offset)
|
|
// Parallax will be mirrored down to any new planes that are added, so it will properly render across mirage borders
|
|
for(var/offset in old_offset to new_offset)
|
|
if(offset != 0)
|
|
// Overlay so we don't multiply twice, and thus fuck up our rendering
|
|
add_relay_to(GET_NEW_PLANE(plane, offset), BLEND_OVERLAY)
|
|
|
|
// Hacky shit to ensure parallax works in perf mode
|
|
/atom/movable/screen/plane_master/parallax/outside_bounds(mob/relevant)
|
|
if(offset == 0)
|
|
remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_GAME, 0))
|
|
is_outside_bounds = TRUE // I'm sorry :(
|
|
return
|
|
// If we can't render, and we aren't the bottom layer, don't render us
|
|
// This way we only multiply against stuff that's not fullwhite space
|
|
var/atom/movable/screen/plane_master/parent_parallax = home.our_hud.get_plane_master(PLANE_SPACE_PARALLAX)
|
|
var/turf/viewing_turf = get_turf(relevant)
|
|
if(!viewing_turf || offset != GET_LOWEST_STACK_OFFSET(viewing_turf.z))
|
|
parent_parallax.remove_relay_from(plane)
|
|
else
|
|
parent_parallax.add_relay_to(plane, BLEND_OVERLAY)
|
|
return ..()
|
|
|
|
/atom/movable/screen/plane_master/parallax/inside_bounds(mob/relevant)
|
|
if(offset == 0)
|
|
add_relay_to(GET_NEW_PLANE(RENDER_PLANE_GAME, 0))
|
|
is_outside_bounds = FALSE
|
|
return
|
|
// Always readd, just in case we lost it
|
|
var/atom/movable/screen/plane_master/parent_parallax = home.our_hud.get_plane_master(PLANE_SPACE_PARALLAX)
|
|
parent_parallax.add_relay_to(plane, BLEND_OVERLAY)
|
|
return ..()
|
|
|
|
// Needs to handle rejoining on a lower z level, so we NEED to readd old planes
|
|
/atom/movable/screen/plane_master/parallax/check_outside_bounds()
|
|
// If we're outside bounds AND we're the 0th plane, we need to show cause parallax is hacked to hell
|
|
return offset != 0 && is_outside_bounds
|
|
|
|
/atom/movable/screen/plane_master/gravpulse
|
|
name = "Gravpulse"
|
|
documentation = "Ok so this one's fun. Basically, we want to be able to distort the game plane when a grav annom is around.\
|
|
<br>So we draw the pattern we want to use to this plane, and it's then used as a render target by a distortion filter on the game plane.\
|
|
<br>Note the blend mode and lack of relay targets. This plane exists only to distort, it's never rendered anywhere."
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
plane = GRAVITY_PULSE_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
blend_mode = BLEND_ADD
|
|
render_target = GRAVITY_PULSE_RENDER_TARGET
|
|
render_relay_planes = list()
|
|
|
|
///Contains just the floor
|
|
/atom/movable/screen/plane_master/floor
|
|
name = "Floor"
|
|
documentation = "The well, floor. This is mostly used as a sorting mechanism, but it also lets us create a \"border\" around the game world plane, so its drop shadow will actually work."
|
|
plane = FLOOR_PLANE
|
|
render_relay_planes = list(RENDER_PLANE_GAME, LIGHT_MASK_PLANE)
|
|
|
|
/atom/movable/screen/plane_master/transparent_floor
|
|
name = "Transparent Floor"
|
|
documentation = "Really just openspace, stuff that is a turf but has no color or alpha whatsoever.\
|
|
<br>We use this to draw to just the light mask plane, cause if it's not there we get holes of blackness over openspace"
|
|
plane = TRANSPARENT_FLOOR_PLANE
|
|
render_relay_planes = list(LIGHT_MASK_PLANE)
|
|
// Needs to be critical or it uh, it'll look white
|
|
critical = PLANE_CRITICAL_DISPLAY|PLANE_CRITICAL_NO_RELAY
|
|
|
|
/atom/movable/screen/plane_master/floor/Initialize(mapload, datum/plane_master_group/home, offset)
|
|
. = ..()
|
|
add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_FLOOR_LAYER, relay_color = GLOB.em_block_color)
|
|
|
|
/atom/movable/screen/plane_master/wall
|
|
name = "Wall"
|
|
documentation = "Holds all walls. We render this onto the game world. Separate so we can use this + space and floor planes as a guide for where byond blackness is NOT."
|
|
plane = WALL_PLANE
|
|
render_relay_planes = list(RENDER_PLANE_GAME_WORLD, LIGHT_MASK_PLANE)
|
|
|
|
/atom/movable/screen/plane_master/wall/Initialize(mapload, datum/plane_master_group/home, offset)
|
|
. = ..()
|
|
add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color)
|
|
|
|
/atom/movable/screen/plane_master/game
|
|
name = "Lower game world"
|
|
documentation = "Exists mostly because of FOV shit. Basically, if you've just got a normal not ABOVE fov thing, and you don't want it masked, stick it here yeah?"
|
|
plane = GAME_PLANE
|
|
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
|
|
|
|
/atom/movable/screen/plane_master/game_world_fov_hidden
|
|
name = "lower game world fov hidden"
|
|
documentation = "If you want something to be hidden by fov, stick it on this plane. We're masked by the fov blocker plane, so the items on us can actually well, disappear."
|
|
plane = GAME_PLANE_FOV_HIDDEN
|
|
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
|
|
|
|
/atom/movable/screen/plane_master/game_world_fov_hidden/Initialize(mapload)
|
|
. = ..()
|
|
add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE))
|
|
|
|
/atom/movable/screen/plane_master/field_of_vision_blocker
|
|
name = "Field of vision blocker"
|
|
documentation = "This is one of those planes that's only used as a filter. It masks out things that want to be hidden by fov.\
|
|
<br>Literally just contains FOV images, or masks."
|
|
plane = FIELD_OF_VISION_BLOCKER_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_target = FIELD_OF_VISION_BLOCKER_RENDER_TARGET
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
render_relay_planes = list()
|
|
// We do NOT allow offsetting, because there's no case where you would want to block only one layer, at least currently
|
|
allows_offsetting = FALSE
|
|
start_hidden = TRUE
|
|
// We mark as multiz_scaled FALSE so transforms don't effect us, and we draw to the planes below us as if they were us.
|
|
// This is safe because we will ALWAYS be on the top z layer, so it DON'T MATTER
|
|
multiz_scaled = FALSE
|
|
|
|
/atom/movable/screen/plane_master/field_of_vision_blocker/Initialize(mapload, datum/plane_master_group/home, offset)
|
|
. = ..()
|
|
mirror_parent_hidden()
|
|
|
|
/atom/movable/screen/plane_master/game_world_upper
|
|
name = "Upper game world"
|
|
documentation = "Ok so fov is kinda fucky, because planes in byond serve both as effect groupings and as rendering orderers. Since that's true, we need a plane that we can stick stuff that draws above fov blocked stuff on."
|
|
plane = GAME_PLANE_UPPER
|
|
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
|
|
|
|
/atom/movable/screen/plane_master/wall_upper
|
|
name = "Upper wall"
|
|
documentation = "There are some walls that want to render above most things (mostly minerals since they shift over.\
|
|
<br>We draw them to their own plane so we can hijack them for our emissive mask stuff"
|
|
plane = WALL_PLANE_UPPER
|
|
render_relay_planes = list(RENDER_PLANE_GAME_WORLD, LIGHT_MASK_PLANE)
|
|
|
|
/atom/movable/screen/plane_master/wall_upper/Initialize(mapload, datum/plane_master_group/home, offset)
|
|
. = ..()
|
|
add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color)
|
|
|
|
/atom/movable/screen/plane_master/game_world_upper_fov_hidden
|
|
name = "Upper game world fov hidden"
|
|
documentation = "Just as we need a place to draw things \"above\" the hidden fov plane, we also need to be able to hide stuff that draws over the upper game plane."
|
|
plane = GAME_PLANE_UPPER_FOV_HIDDEN
|
|
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
|
|
|
|
/atom/movable/screen/plane_master/game_world_upper_fov_hidden/Initialize(mapload)
|
|
. = ..()
|
|
// Dupe of the other hidden plane
|
|
add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE))
|
|
|
|
/atom/movable/screen/plane_master/seethrough
|
|
name = "Seethrough"
|
|
documentation = "Holds the seethrough versions (done using image overrides) of large objects. Mouse transparent, so you can click through them."
|
|
plane = SEETHROUGH_PLANE
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
|
|
start_hidden = TRUE
|
|
|
|
/atom/movable/screen/plane_master/game_world_above
|
|
name = "Above game world"
|
|
documentation = "We need a place that's unmasked by fov that also draws above the upper game world fov hidden plane. I told you fov was hacky man."
|
|
plane = ABOVE_GAME_PLANE
|
|
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
|
|
|
|
/**
|
|
* Plane master that byond will by default draw to
|
|
* Shouldn't be used, exists to prevent people using plane 0
|
|
* NOTE: If we used SEE_BLACKNESS on a map format that wasn't SIDE_MAP, this is where its darkness would land
|
|
* This would allow us to control it and do fun things. But we can't because side map doesn't support it, so this is just a stub
|
|
*/
|
|
/atom/movable/screen/plane_master/default
|
|
name = "Default"
|
|
documentation = "This is quite fiddly, so bear with me. By default (in byond) everything in the game is rendered onto plane 0. It's the default plane. \
|
|
<br>But, because we've moved everything we control off plane 0, all that's left is stuff byond internally renders. \
|
|
<br>What I'd like to do with this is capture byond blackness by giving mobs the SEE_BLACKNESS sight flag. \
|
|
<br>But we CAN'T because SEE_BLACKNESS does not work with our rendering format. So I just eat it I guess"
|
|
plane = DEFAULT_PLANE
|
|
multiz_scaled = FALSE
|
|
start_hidden = TRUE // Doesn't DO anything, exists to hold this place
|
|
|
|
/atom/movable/screen/plane_master/area
|
|
name = "Area"
|
|
documentation = "Holds the areas themselves, which ends up meaning it holds any overlays/effects we apply to areas. NOT snow or rad storms, those go on above lighting"
|
|
plane = AREA_PLANE
|
|
|
|
/atom/movable/screen/plane_master/massive_obj
|
|
name = "Massive object"
|
|
documentation = "Huge objects need to render above everything else on the game plane, otherwise they'd well, get clipped and look not that huge. This does that."
|
|
plane = MASSIVE_OBJ_PLANE
|
|
|
|
/atom/movable/screen/plane_master/point
|
|
name = "Point"
|
|
documentation = "I mean like, what do you want me to say? Points draw over pretty much everything else, so they get their own plane. Remember we layer render relays to draw planes in their proper order on render plates."
|
|
plane = POINT_PLANE
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
|
|
///Contains all turf lighting
|
|
/atom/movable/screen/plane_master/turf_lighting
|
|
name = "Turf Lighting"
|
|
documentation = "Contains all lighting drawn to turfs. Not so complex, draws directly onto the lighting plate."
|
|
plane = LIGHTING_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_relay_planes = list(RENDER_PLANE_LIGHTING)
|
|
blend_mode_override = BLEND_ADD
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
critical = PLANE_CRITICAL_DISPLAY
|
|
|
|
/// This will not work through multiz, because of a byond bug with BLEND_MULTIPLY
|
|
/// Bug report is up, waiting on a fix
|
|
/atom/movable/screen/plane_master/o_light_visual
|
|
name = "Overlight light visual"
|
|
documentation = "Holds overlay lighting objects, or the sort of lighting that's a well, overlay stuck to something.\
|
|
<br>Exists because lighting updating is really slow, and movement needs to feel smooth.\
|
|
<br>We draw to the game plane, and mask out space for ourselves on the lighting plane so any color we have has the chance to display."
|
|
plane = O_LIGHTING_VISUAL_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_target = O_LIGHTING_VISUAL_RENDER_TARGET
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
blend_mode = BLEND_MULTIPLY
|
|
critical = PLANE_CRITICAL_DISPLAY
|
|
|
|
/atom/movable/screen/plane_master/above_lighting
|
|
name = "Above lighting"
|
|
plane = ABOVE_LIGHTING_PLANE
|
|
documentation = "Anything on the game plane that needs a space to draw on that will be above the lighting plane.\
|
|
<br>Mostly little alerts and effects, also sometimes contains things that are meant to look as if they glow."
|
|
|
|
/**
|
|
* Handles emissive overlays and emissive blockers.
|
|
*/
|
|
/atom/movable/screen/plane_master/emissive
|
|
name = "Emissive"
|
|
documentation = "Holds things that will be used to mask the lighting plane later on. Masked by the Emissive Mask plane to ensure we don't emiss out under a wall.\
|
|
<br>Relayed onto the Emissive render plane to do the actual masking of lighting, since we need to be transformed and other emissive stuff needs to be transformed too.\
|
|
<br>Don't want to double scale now."
|
|
plane = EMISSIVE_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
render_relay_planes = list(EMISSIVE_RENDER_PLATE)
|
|
critical = PLANE_CRITICAL_DISPLAY
|
|
|
|
/atom/movable/screen/plane_master/pipecrawl
|
|
name = "Pipecrawl"
|
|
documentation = "Holds pipecrawl images generated during well, pipecrawling.\
|
|
<br>Has a few effects and a funky color matrix designed to make things a bit more visually readable."
|
|
plane = PIPECRAWL_IMAGES_PLANE
|
|
start_hidden = TRUE
|
|
|
|
/atom/movable/screen/plane_master/pipecrawl/Initialize(mapload)
|
|
. = ..()
|
|
// Makes everything on this plane slightly brighter
|
|
// Has a nice effect, makes thing stand out
|
|
color = list(1.2,0,0,0, 0,1.2,0,0, 0,0,1.2,0, 0,0,0,1, 0,0,0,0)
|
|
// This serves a similar purpose, I want the pipes to pop
|
|
add_filter("pipe_dropshadow", 1, drop_shadow_filter(x = -1, y= -1, size = 1, color = "#0000007A"))
|
|
mirror_parent_hidden()
|
|
|
|
/atom/movable/screen/plane_master/camera_static
|
|
name = "Camera static"
|
|
documentation = "Holds camera static images. Usually only visible to people who can well, see static.\
|
|
<br>We use images rather then vis contents because they're lighter on maptick, and maptick sucks butt."
|
|
plane = CAMERA_STATIC_PLANE
|
|
start_hidden = TRUE
|
|
|
|
/atom/movable/screen/plane_master/camera_static/show_to(mob/mymob)
|
|
// If we aren't an AI, we have no need for this plane master (most of the time, ai eyes are weird and annoying)
|
|
if(force_hidden && isAI(mymob))
|
|
unhide_plane(mymob)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(isAI(mymob))
|
|
return
|
|
return FALSE
|
|
|
|
/atom/movable/screen/plane_master/high_game
|
|
name = "High Game"
|
|
documentation = "Holds anything that wants to be displayed above the rest of the game plane, and doesn't want to be clickable. \
|
|
<br>This includes atmos debug overlays, blind sound images, and mining scanners. \
|
|
<br>Really only exists for its layering potential, we don't use this for any vfx"
|
|
plane = HIGH_GAME_PLANE
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
|
|
/atom/movable/screen/plane_master/ghost
|
|
name = "Ghost"
|
|
documentation = "Ghosts draw here, so they don't get mixed up in the visuals of the game world. Note, this is not not how we HIDE ghosts from people, that's done with invisible and see_invisible."
|
|
plane = GHOST_PLANE
|
|
render_relay_planes = list(RENDER_PLANE_NON_GAME)
|
|
|
|
/atom/movable/screen/plane_master/fullscreen
|
|
name = "Fullscreen"
|
|
documentation = "Holds anything that applies to or above the full screen. \
|
|
<br>Note, it's still rendered underneath hud objects, but this lets us control the order that things like death/damage effects render in."
|
|
plane = FULLSCREEN_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_relay_planes = list(RENDER_PLANE_NON_GAME)
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
allows_offsetting = FALSE
|
|
|
|
/atom/movable/screen/plane_master/runechat
|
|
name = "Runechat"
|
|
documentation = "Holds runechat images, that text that pops up when someone say something. Uses a dropshadow to well, look nice."
|
|
plane = RUNECHAT_PLANE
|
|
render_relay_planes = list(RENDER_PLANE_NON_GAME)
|
|
|
|
/atom/movable/screen/plane_master/runechat/show_to(mob/mymob)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
remove_filter("AO")
|
|
if(istype(mymob) && mymob.client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion))
|
|
add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA"))
|
|
|
|
/atom/movable/screen/plane_master/balloon_chat
|
|
name = "Balloon chat"
|
|
documentation = "Holds ballon chat images, those little text bars that pop up for a second when you do some things. NOT runechat."
|
|
plane = BALLOON_CHAT_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_relay_planes = list(RENDER_PLANE_NON_GAME)
|
|
|
|
/atom/movable/screen/plane_master/hud
|
|
name = "HUD"
|
|
documentation = "Contains anything that want to be rendered on the hud. Typically is just screen elements."
|
|
plane = HUD_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_relay_planes = list(RENDER_PLANE_NON_GAME)
|
|
allows_offsetting = FALSE
|
|
|
|
/atom/movable/screen/plane_master/above_hud
|
|
name = "Above HUD"
|
|
documentation = "Anything that wants to be drawn ABOVE the rest of the hud. Typically close buttons and other elements that need to be always visible. Think preventing draggable action button memes."
|
|
plane = ABOVE_HUD_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_relay_planes = list(RENDER_PLANE_NON_GAME)
|
|
allows_offsetting = FALSE
|
|
|
|
/atom/movable/screen/plane_master/splashscreen
|
|
name = "Splashscreen"
|
|
documentation = "Cinematics and the splash screen."
|
|
plane = SPLASHSCREEN_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_relay_planes = list(RENDER_PLANE_NON_GAME)
|
|
allows_offsetting = FALSE
|
|
|
|
/atom/movable/screen/plane_master/escape_menu
|
|
name = "Escape Menu"
|
|
documentation = "Anything relating to the escape menu."
|
|
plane = ESCAPE_MENU_PLANE
|
|
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
|
|
render_relay_planes = list(RENDER_PLANE_MASTER)
|
|
allows_offsetting = FALSE
|