From 9afa91d2a7e5d3eac95e583c536411e64fbd444f Mon Sep 17 00:00:00 2001 From: NovaBot <154629622+NovaBot13@users.noreply.github.com> Date: Thu, 25 Apr 2024 19:06:52 -0400 Subject: [PATCH] [MIRROR] Redoes how appearance VV works because it scares me (#2173) * Redoes how appearance VV works because it scares me (#82851) * Redoes how appearance VV works because it scares me --------- Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> --- .github/guides/VISUALS.md | 234 ++++++------- code/__DEFINES/vv.dm | 2 - code/_globalvars/bitfields.dm | 13 + .../debug_variable_appearance.dm | 325 +++++------------- .../admin/view_variables/debug_variables.dm | 10 +- .../admin/view_variables/view_variables.dm | 24 +- code/modules/unit_tests/unit_test.dm | 2 - 7 files changed, 237 insertions(+), 373 deletions(-) diff --git a/.github/guides/VISUALS.md b/.github/guides/VISUALS.md index 82ee0c6b5d6..9c385ee8765 100644 --- a/.github/guides/VISUALS.md +++ b/.github/guides/VISUALS.md @@ -4,10 +4,10 @@ Welcome to a breakdown of visuals and visual effects in our codebase, and in BYO I will be describing all of the existing systems we use, alongside explaining and providing references to BYOND's ref for each tool. -Note, I will not be covering things that are trivial to understand, and which we don't mess with much. +Note, I will not be covering things that are trivial to understand, and which we don't mess with much. For a complete list of byond ref stuff relevant to this topic, see [here](https://www.byond.com/docs/ref/#/atom/var/appearance). -This is to some extent a collation of the BYOND ref, alongside a description of how we actually use these tools. +This is to some extent a collation of the BYOND ref, alongside a description of how we actually use these tools. My hope is after reading this you'll be able to understand and implement different visual effects in our codebase. Also please see the ref entry on the [renderer](https://www.byond.com/docs/ref/#/{notes}/renderer). @@ -53,10 +53,10 @@ You'll find links to the relevant reference entries at the heading of each entry ## Appearances in BYOND - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/appearance) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/appearance) Everything that is displayed on the map has an appearance variable that describes exactly how it should be rendered. -To be clear, it doesn't contain EVERYTHING, [plane masters](#planes) exist separately and so do many other factors. +To be clear, it doesn't contain EVERYTHING, [plane masters](#planes) exist separately and so do many other factors. But it sets out a sort of recipe of everything that could effect rendering. Appearances have a few quirks that can be helpful or frustrating depending on what you're trying to do. @@ -65,25 +65,27 @@ To start off with, appearances are static. You can't directly edit an appearance The way to edit them most of the time is to just modify the corresponding variable on the thing the appearance represents. -This doesn't mean it's impossible to modify them directly however. While appearances are static, +This doesn't mean it's impossible to modify them directly however. While appearances are static, their cousins mutable appearances [(Ref Entry)](https://www.byond.com/docs/ref/info.html#/mutable_appearance) **are**. -What we can do is create a new mutable appearance, set its appearance to be a copy of the static one (remember all appearance variables are static), +What we can do is create a new mutable appearance, set its appearance to be a copy of the static one (remember all appearance variables are static), edit it, and then set the desired thing's appearance var to the appearance var of the mutable. Somewhat like this ```byond -// NOTE: we do not actually have access to a raw appearance type, so we will often +// NOTE: we do not actually have access to a raw appearance type, so we will often // Lie to the compiler, and pretend we are using a mutable appearance // This lets us access vars as expected. Be careful with it tho -/proc/mutate_icon_state(mutable_appearance/thing) +/proc/mutate_icon_state(mutable_appearance/thing) var/mutable_appearance/temporary_lad = new() temporary_lad.appearance = thing temporary_lad.icon_state += "haha_owned" return temporary_lad.appearance ``` +> **Note:** More then being static, appearances are unique. Only one copy of each set of appearance vars exists, and when you modify any of those vars, the corrosponding appearance variable changes its value to whatever matches the new hash. That's why appearance vars can induce inconsistent cost on modification. + > **Warning:** BYOND has been observed to have issues with appearance corruption, it's something to be weary of when "realizing" appearances in this manner. ## Overlays @@ -91,7 +93,7 @@ Somewhat like this - [Table of Contents](#table-of-contents) - [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/overlays) (Also see [rendering](https://www.byond.com/docs/ref/#/{notes}/renderer)) -Overlays are a list of static [appearances](#appearances-in-byond) that we render on top of ourselves. +Overlays are a list of static [appearances](#appearances-in-byond) that we render on top of ourselves. Said appearances can be edited via the realizing method mentioned above. Their rendering order is determined by [layer](#layers) and [plane](#planes), but conflicts are resolved based off order of appearance inside the list. @@ -104,67 +106,67 @@ It's not significant, but it is there, and something to be aware of. ### Our Implementation -We use overlays as our primary method of overlaying visuals. +We use overlays as our primary method of overlaying visuals. However, since overlays are COPIES of a thing's appearance, ensuring that they can be cleared is semi troublesome. To solve this problem, we manage most overlays using `update_overlays()`. -This proc is called whenever an atom's appearance is updated with `update_appearance()` -(essentially just a way to tell an object to rerender anything static about it, like icon state or name), +This proc is called whenever an atom's appearance is updated with `update_appearance()` +(essentially just a way to tell an object to rerender anything static about it, like icon state or name), which will often call `update_icon()`. `update_icon()` handles querying the object for its desired icon, and also manages its overlays, by calling `update_overlays()`. -Said proc returns a list of things to turn into static appearances, which are then passed into `add_overlay()`, +Said proc returns a list of things to turn into static appearances, which are then passed into `add_overlay()`, which makes them static with `build_appearance_list()` before queuing an overlay compile. -This list of static appearances is then queued inside a list called `managed_overlays` on `/atom`. +This list of static appearances is then queued inside a list called `managed_overlays` on `/atom`. This is so we can clear old overlays out before running an update. -We actually compile queued overlay builds once every tick using a dedicated subsystem. +We actually compile queued overlay builds once every tick using a dedicated subsystem. This is done to avoid adding/removing/adding again to the overlays list in cases like humans where it's mutated a lot. -You can bypass this managed overlays system if you'd like, using `add_overlay()` and `cut_overlay()`, +You can bypass this managed overlays system if you'd like, using `add_overlay()` and `cut_overlay()`, but this is semi dangerous because you don't by default have a way to "clear" the overlay. Be careful of this. ## Visual Contents - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/vis_contents) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/vis_contents) The `vis_contents` list allows you to essentially say "Hey, render this thing ON me". -The definition of "ON" varies significantly with the `vis_flags` value of the *thing* being relayed. -See the ref [here](https://www.byond.com/docs/ref/#/atom/var/vis_flags). +The definition of "ON" varies significantly with the `vis_flags` value of the *thing* being relayed. +See the ref [here](https://www.byond.com/docs/ref/#/atom/var/vis_flags). Some flags of interest: -- `VIS_INHERIT_ID`: This allows you to link the object DIRECTLY to the thing it's drawn on, +- `VIS_INHERIT_ID`: This allows you to link the object DIRECTLY to the thing it's drawn on, so clicking on the `vis_contents`'d object is just like clicking on the thing -- `VIS_INHERIT_PLANE`: We will discuss [planes](#planes) more in future, but we use them to both effect rendering order and apply effects as a group. -This flag changes the plane of any `vis_contents`'d object (while displayed on the source object) to the source's. +- `VIS_INHERIT_PLANE`: We will discuss [planes](#planes) more in future, but we use them to both effect rendering order and apply effects as a group. +This flag changes the plane of any `vis_contents`'d object (while displayed on the source object) to the source's. This is occasionally useful, but should be used with care as it breaks any effects that rely on plane. -Anything inside a `vis_contents` list will have its loc stored in its `vis_locs` variable. +Anything inside a `vis_contents` list will have its loc stored in its `vis_locs` variable. We very rarely use this, primarily just for clearing references from `vis_contents`. -`vis_contents`, unlike `overlays` is a reference, not a copy. So you can update a `vis_contents`'d thing and have it mirror properly. +`vis_contents`, unlike `overlays` is a reference, not a copy. So you can update a `vis_contents`'d thing and have it mirror properly. This is how we do multiz by the by, with uh, some more hell discussed under [multiz](#multiz). -To pay for this additional behavior however, vis_contents has additional cost in maptick. -Because it's not a copy, we need to constantly check if it's changed at all, which leads to cost scaling with player count. +To pay for this additional behavior however, vis_contents has additional cost in maptick. +Because it's not a copy, we need to constantly check if it's changed at all, which leads to cost scaling with player count. Careful how much you use it. ## Images - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/image) +- [Reference Entry](https://www.byond.com/docs/ref/#/image) Images are technically parents of [mutable appearances](#appearances-in-byond). We don't often use them, mostly because we can accomplish their behavior with just MAs. -Images exist both to be used in overlays, and to display things to only select clients on the map. +Images exist both to be used in overlays, and to display things to only select clients on the map. See [/client/var/images](#client-images) > Note: the inheritance between the two is essentially for engine convenience. Don't rely on it. @@ -172,7 +174,7 @@ See [/client/var/images](#client-images) ## Client Images - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/client/var/images) +- [Reference Entry](https://www.byond.com/docs/ref/#/client/var/images) `/client/var/images` is a list of image objects to display to JUST that particular client. @@ -180,35 +182,35 @@ The image objects are displayed at their loc variable, and can be shown to more ### Our Implementation -We use client images in a few ways. Often they will be used just as intended, to modify the view of just one user. +We use client images in a few ways. Often they will be used just as intended, to modify the view of just one user. Think tray scanner or technically ai static. -However, we often want to show a set of images to the same GROUP of people, but in a limited manner. +However, we often want to show a set of images to the same GROUP of people, but in a limited manner. For this, we use the `/datum/atom_hud` (hereafter hud) system. This is different from `/datum/hud`, which I will discuss later. -HUDs are datums that represent categories of images to display to users. +HUDs are datums that represent categories of images to display to users. They are most often global, but can be created on an atom to atom bases in rare cases. They store a list of images to display (sorted by source z level to reduce lag) and a list of clients to display to. -We then mirror this group of images into/out of the client's images list, based on what HUDs they're able to see. +We then mirror this group of images into/out of the client's images list, based on what HUDs they're able to see. This is the pattern we use for things like the medihud, or robot trails. ## View - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/client/var/view) +- [Reference Entry](https://www.byond.com/docs/ref/#/client/var/view) -`/client/var/view` is actually a pretty simple topic, -but I'm gonna take this chance to discuss the other things we do to manage pixel sizing and such since there isn't a better place for it, +`/client/var/view` is actually a pretty simple topic, +but I'm gonna take this chance to discuss the other things we do to manage pixel sizing and such since there isn't a better place for it, and they're handled in the same place by us. Alright then, view. This is pretty simple, but it basically just lets us define the tile bound we want to show to our client. This can either be a number for an X by X square, or a string in the form "XxY" for more control. -We use `/datum/view_data` to manage and track view changes, so zoom effects can work without canceling or being canceled by anything else. +We use `/datum/view_data` to manage and track view changes, so zoom effects can work without canceling or being canceled by anything else. ### Client Rendering Modes @@ -218,29 +220,29 @@ Clients get some choice in literally how they want the game to be rendered to th The two I'm gonna discuss here are `zoom`, and `zoom-mode` mode, both of which are skin params (basically just variables that live on the client) -`zoom` decides how the client wants to display the turfs shown to it. -It can have two types of values. -If it's equal to 0 it will stretch the tiles sent to the client to fix the size of the map-window. -Otherwise, any other numbers will lead to pixels being scaled by some multiple. +`zoom` decides how the client wants to display the turfs shown to it. +It can have two types of values. +If it's equal to 0 it will stretch the tiles sent to the client to fix the size of the map-window. +Otherwise, any other numbers will lead to pixels being scaled by some multiple. This effect can only really result in nice clean edges if you pass in whole numbers which is why most of the constant scaling we give players are whole numbers. -`zoom-mode` controls how a pixel will be up-scaled, if it needs to be. -See the ref for more details, but `normal` is gonna have the sharpest output, `distort` uses nearest neighbor, +`zoom-mode` controls how a pixel will be up-scaled, if it needs to be. +See the ref for more details, but `normal` is gonna have the sharpest output, `distort` uses nearest neighbor, which causes some blur, and `blur` uses bilinear sampling, which causes a LOT of blur. ## Eye - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/client/var/eye) +- [Reference Entry](https://www.byond.com/docs/ref/#/client/var/eye) -`/client/var/eye` is the atom or mob at which our view should be centered. +`/client/var/eye` is the atom or mob at which our view should be centered. Any screen objects we display will show "off" this, as will our actual well eye position. -It is by default `/client/var/mob` but it can be modified. +It is by default `/client/var/mob` but it can be modified. This is how we accomplish ai eyes and ventcrawling, alongside most other effects that involve a player getting "into" something. ## Client Screen - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/HUD) +- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/HUD) Similar to client images but not *quite* the same, we can also insert objects onto our client's literal screen @@ -256,21 +258,21 @@ The classic `screen_loc` format looks something like this (keeping in mind it co The pixel offsets can be discarded as optional, but crucially the x and y values do not NEED to be absolute. -We can use cardinal keywords like `NORTH` to anchor screen objects to the view size of the client (a topic that will be discussed soon). -You can also use directional keywords like `TOP` to anchor to the actual visible map-window, which prevents any accidental out of bounds. -Oh yeah you can use absolute offsets to position screen objects out of the view range, which will cause the map-window to forcefully expand, +We can use cardinal keywords like `NORTH` to anchor screen objects to the view size of the client (a topic that will be discussed soon). +You can also use directional keywords like `TOP` to anchor to the actual visible map-window, which prevents any accidental out of bounds. +Oh yeah you can use absolute offsets to position screen objects out of the view range, which will cause the map-window to forcefully expand, exposing the parts of the map byond uses to ahead of time render border things so moving is smooth. ### Secondary Maps While we're here, this is a bit of a side topic but you can have more then one map-window on a client's screen at once. -This gets into dmf fuckery but you can use [window ids](https://www.byond.com/docs/ref/#/{skin}/param/id) to tell a screen object to render to a secondary map. +This gets into dmf fuckery but you can use [window ids](https://www.byond.com/docs/ref/#/{skin}/param/id) to tell a screen object to render to a secondary map. Useful for creating popup windows and such. ## Blend Mode - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/blend_mode) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/blend_mode) `/atom/var/blend_mode` defines how an atom well, renders onto the map. @@ -280,7 +282,7 @@ This is how we do lighting effects, since the lighting [plane](#planes) can be u ## Appearance Flags - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/appearance_flags) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/appearance_flags) `/atom/var/appearance_flags` is a catch all for toggles that apply to visual elements of an atom. I won't go over all of them, but I will discuss a few. @@ -293,8 +295,8 @@ Flags of interest: ## Gliding - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/gliding) - +- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/gliding) + You may have noticed that moving between tiles is smooth, or at least as close as we can get it. Moving at 0.2 or 10 tiles per second will be smooth. This is because we have control over the speed at which atoms animate between moves. @@ -306,37 +308,37 @@ This is done using `/atom/movable/proc/set_glide_size`, which will inform anythi Glide size is often set in the context of some rate of movement. Either the movement delay of a mob, set in `/client/Move()`, or the delay of a movement subsystem. We use defines to turn delays into pixels per tick. -Client moves will be limited by `DELAY_TO_GLIDE_SIZE` which will allow at most 32 pixels a tick. -Subsystems and other niche uses use `MOVEMENT_ADJUSTED_GLIDE_SIZE`. -We will also occasionally use glide size as a way to force a transition between different movement types, like space-drift into normal walking. +Client moves will be limited by `DELAY_TO_GLIDE_SIZE` which will allow at most 32 pixels a tick. +Subsystems and other niche uses use `MOVEMENT_ADJUSTED_GLIDE_SIZE`. +We will also occasionally use glide size as a way to force a transition between different movement types, like space-drift into normal walking. There's extra cruft here. -> Something you should know: Our gliding system attempts to account for time dilation when setting move rates. +> Something you should know: Our gliding system attempts to account for time dilation when setting move rates. This is done in a very simplistic way however, so a spike in td will lead to jumping around as glide rate is outpaced by mob movement rate. -On that note, it is VERY important that glide rate is the same or near the same as actual move rate. -Otherwise you will get strange jumping and jitter. +On that note, it is VERY important that glide rate is the same or near the same as actual move rate. +Otherwise you will get strange jumping and jitter. This can also lead to stupid shit where people somehow manage to intentionally shorten a movement delay to jump around. Dumb. Related to the above, we are not always able to maintain sync between glide rate and mob move rate. -This is because mob move rate is a function of the initial move delay and a bunch of slowdown/speedup modifiers. -In order to maintain sync we would need to issue a move command the MOMENT a delay is up, and if delays are not cleanly divisible by our tick rate (0.5 deciseconds) this is impossible. +This is because mob move rate is a function of the initial move delay and a bunch of slowdown/speedup modifiers. +In order to maintain sync we would need to issue a move command the MOMENT a delay is up, and if delays are not cleanly divisible by our tick rate (0.5 deciseconds) this is impossible. This is why you'll sometime see a stutter in your step when slowed Just so you know, client movement works off `/client/var/move_delay` which sets the next time an input will be accepted. It's typically glide rate, but is in some cases just 1 tick. ## Sight - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/sight) +- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/sight) `/mob/var/sight` is a set of bitflags that *mostly* set what HAS to render on your screen. Be that mobs, turfs, etc. That said, there is some nuance here so I'ma get into that. -- `SEE_INFRA`: I'll get into this later, but infrared is essentially a copy of BYOND darkness, it's not something we currently use. -- `SEE_BLACKNESS`: This relates heavily to [planes](#planes), essentially typically the "blackness" (that darkness that masks things that you can't see) -is rendered separately, out of our control as "users". +- `SEE_INFRA`: I'll get into this later, but infrared is essentially a copy of BYOND darkness, it's not something we currently use. +- `SEE_BLACKNESS`: This relates heavily to [planes](#planes), essentially typically the "blackness" (that darkness that masks things that you can't see) +is rendered separately, out of our control as "users". However, if the `SEE_BLACKNESS` flag is set, it will instead render on plane 0, the default BYOND plane. -This allows us to capture it, and say, blur it, or redraw it elsewhere. This is in theory very powerful, but not possible with the 'side_map' [map format](https://www.byond.com/docs/ref/#/world/var/map_format) +This allows us to capture it, and say, blur it, or redraw it elsewhere. This is in theory very powerful, but not possible with the 'side_map' [map format](https://www.byond.com/docs/ref/#/world/var/map_format) ## BYOND Lighting @@ -346,14 +348,14 @@ Alongside OUR lighting implementation, which is discussed in with color matrixes It's very basic. Essentially, a tile is either "lit" or it's not. -If a tile is not lit, and it matches some other preconditions, it and all its contents will be hidden from the user, +If a tile is not lit, and it matches some other preconditions, it and all its contents will be hidden from the user, sort of like if there was a wall between them. This hiding uses BYOND darkness, and is thus controllable. I'll use this section to discuss all the little bits that contribute to this behavior ### Luminosity - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/luminosity) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/luminosity) `/atom/var/luminosity` is a variable that lets us inject light into BYOND's lighting system. It's real simple, just a range of tiles that will be lit, respecting sight-lines and such of course. @@ -363,16 +365,16 @@ You can actually force it to use a particular mob's sight to avoid aspects of th ### See in Dark - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/see_in_dark) +- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/see_in_dark) This is why when you stand in darkness you can see yourself, and why you can see a line of objects appear when you use mesons (horrible effect btw). It's quite simple, but worth describing. ### Infrared - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/see_infrared) +- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/see_infrared) -Infrared vision can be thought of as a hidden copy of standard BYOND darkness. +Infrared vision can be thought of as a hidden copy of standard BYOND darkness. It's not something we actually use, but I think you should know about it, because the whole thing is real confusing without context. ## Invisibility @@ -388,16 +390,16 @@ It's also used to hide some more then ghost invisible things, like some timers a ## Layers - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/layer) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/layer) -`/atom/var/layer` is the first bit of logic that decides the order in which things on the map render. -Rendering order depends a LOT on the [map format](https://www.byond.com/docs/ref/#/world/var/map_format), -which I will not get into in this document because it is not yet relevant. -All you really need to know is for our current format, -the objects that appear first in something's contents will draw first, and render lowest. -Think of it like stacking little paper cutouts. +`/atom/var/layer` is the first bit of logic that decides the order in which things on the map render. +Rendering order depends a LOT on the [map format](https://www.byond.com/docs/ref/#/world/var/map_format), +which I will not get into in this document because it is not yet relevant. +All you really need to know is for our current format, +the objects that appear first in something's contents will draw first, and render lowest. +Think of it like stacking little paper cutouts. -Layer has a bit more nuance then just being lowest to highest, tho it's not a lot. +Layer has a bit more nuance then just being lowest to highest, tho it's not a lot. There are a few snowflake layers that can be used to accomplish niche goals, alongside floating layers, which are essentially just any layer that is negative. Floating layers will float "up" the chain of things they're being drawn onto, until they find a real layer. They'll then offset off of that. @@ -406,7 +408,7 @@ This allows us to keep relative layer differences while not needing to make all ## Planes - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/plane) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/plane) Allllright `/atom/var/plane`s. Let's talk about em. @@ -415,16 +417,16 @@ Higher planes will (**normally**) render over lower ones. Very clearcut. Similarly to [layers](#layers), planes also support "floating" with `FLOAT_PLANE`. See above for an explanation of that. -However, they can be used for more complex and... fun things too! +However, they can be used for more complex and... fun things too! If a client has an atom with the `PLANE_MASTER` [appearance flag](#appearance-flags) in their [screen](#client-screen), then rather then being all rendered normally, anything in the client's view is instead first rendered onto the plane master. -This is VERY powerful, because it lets us [hide](https://www.byond.com/docs/ref/#/atom/var/alpha), [color](#color), +This is VERY powerful, because it lets us [hide](https://www.byond.com/docs/ref/#/atom/var/alpha), [color](#color), and [distort](#filters) whole classes of objects, among other things. I cannot emphasize enough how useful this is. It does have some downsides however. Because planes are tied to both grouping and rendering order, there are some effects that require splitting a plane into bits. -It's also possible for some effects, especially things relating to [map format](https://www.byond.com/docs/ref/#/world/var/map_format), +It's also possible for some effects, especially things relating to [map format](https://www.byond.com/docs/ref/#/world/var/map_format), to just be straight up impossible, or conflict with each other. It's dumb, but it's what we've got brother so we're gonna use it like it's a free ticket to the bahamas. @@ -432,15 +434,15 @@ We have a system that allows for arbitrary grouping of plane masters for the pur called `/atom/movable/plane_master_controller`. This is somewhat outmoded by our use of [render relays](#render-targetsource), but it's still valid and occasionally useful. -> Something you should know: Plane masters effect ONLY the map their screen_loc is on. +> Something you should know: Plane masters effect ONLY the map their screen_loc is on. For this reason, we are forced to generate whole copies of the set of plane masters with the proper screen_loc to make subviews look right -> Warning: Planes have some restrictions on valid values. They NEED to be whole integers, and they NEED to have an absolute value of `10000`. +> Warning: Planes have some restrictions on valid values. They NEED to be whole integers, and they NEED to have an absolute value of `10000`. This is to support `FLOAT_PLANE`, which lives out at the very edge of the 32 bit int range. ## Render Target/Source - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/render_target) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/render_target) Render targets are a way of rendering one thing onto another. Not like vis_contents but in a literal sense ONTO. The target object is given a `/atom/var/render_target` value, and anything that wishes to "take" it sets its `/atom/var/render_source` var to match. @@ -475,8 +477,8 @@ This meant the turf below looked as if it was offset, and everything was good. Except not, for 2 reasons. One more annoying then the other. - 1: It looked like dog doo-doo. This pattern destroyed the old planes of everything vis_contents'd, so effects/lighting/dropshadows broke bad. -- 2: I alluded to this earlier, but it totally breaks the `side_map` [map format](https://www.byond.com/docs/ref/#/world/var/map_format) -which I need for a massive resprite I'm helping with. This is because `side_map` changes how rendering order works, +- 2: I alluded to this earlier, but it totally breaks the `side_map` [map format](https://www.byond.com/docs/ref/#/world/var/map_format) +which I need for a massive resprite I'm helping with. This is because `side_map` changes how rendering order works, going off "distance" from the front of the frame. The issue here is it of course needs a way to group things that are even allowed to overlap, so it uses plane. So when you squish everything down onto one plane, this of course breaks horribly and fucks you. @@ -491,7 +493,7 @@ to the openspace plane master one level up. More then doable. SECOND problem. How do we get everything below to "land" on the right plane? The answer to this is depressing but still true. We manually offset every single object on the map's plane based off its "z layer". -This includes any `overlays` or `vis_contents` with a unique plane value. +This includes any `overlays` or `vis_contents` with a unique plane value. Mostly we require anything that sets the plane var to pass in a source of context, like a turf or something that can be used to derive a turf. There are a few edge cases where we need to work in explicitly offsets, but those are much rarer. @@ -500,18 +502,18 @@ This is stupid, but it's makable, and what we do. ## Mouse Opacity - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/mouse_opacity) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/mouse_opacity) `/atom/var/mouse_opacity` tells clients how to treat mousing over the atom in question. A value of 0 means it is completely ignored, no matter what. A value of 1 means it is transparent/opaque based off the alpha of the icon at any particular part. -A value of 2 means it will count as opaque across ALL of the icon-state. All 32x32 (or whatever) of it. +A value of 2 means it will count as opaque across ALL of the icon-state. All 32x32 (or whatever) of it. -We will on occasion use mouse opacity to expand hitboxes, but more often this is done with [vis_contents](#visual-contents), +We will on occasion use mouse opacity to expand hitboxes, but more often this is done with [vis_contents](#visual-contents), or just low alpha pixels on the sprite. -> Note: Mouse opacity will only matter if the atom is being rendered on its own. [Overlays](#overlays)(and [images](#images)) +> Note: Mouse opacity will only matter if the atom is being rendered on its own. [Overlays](#overlays)(and [images](#images)) will NOT work as expected with this. However, you can still have totally transparent overlays. If you render them onto a [plane master](#planes) with the desired mouse opacity value it will work as expected. This is because as a step of the rendering pipeline the overlay is rendered ONTO the plane master, and then the plane @@ -519,10 +521,10 @@ master's effects are applied. ## Filters - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/filters) +- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/filters) Filters are a general purpose system for applying a limited set of shaders to a render. -These shaders run on the client's machine. This has upsides and downsides. +These shaders run on the client's machine. This has upsides and downsides. Upside: Very cheap for the server. Downside: Potentially quite laggy for the client. Take care with these @@ -544,7 +546,7 @@ It'll let you add and tweak *most* of the filters in BYOND. ## Particles - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/particles) +- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/particles) Particles are a system that allows you to attach "generators" to atoms on the world, and have them spit out little visual effects. This is done by creating a subtype of the `/particles` type, and giving it the values you want. @@ -558,7 +560,7 @@ It'll let you add and tweak the particles attached to that atom. ## Pixel Offsets - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/pixel_x) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/pixel_x) This is a real simple idea and I normally wouldn't mention it, but I have something else I wanna discuss related to it, so I'ma take this chance. @@ -571,7 +573,7 @@ There are two "types" of each direction offset. There's the "real" offset (x/y) Real offsets will change both the visual position (IE: where it renders) and also the positional position (IE: where the renderer thinks they are). Fake offsets only effect visual position. -This doesn't really matter for our current map format, but for anything that takes position into account when layering, like `side_map` or `isometric_map` +This doesn't really matter for our current map format, but for anything that takes position into account when layering, like `side_map` or `isometric_map` it matters a whole ton. It's kinda a hard idea to get across, but I hope you have at least some idea. ## Map Formats @@ -585,10 +587,10 @@ There are 4 types currently. Only 2 that are interesting to us, and one that's n Most of them involve changing how layering works, from the standard [layers](#layers) and [planes](#planes) method. There's a bit more detail here, not gonna go into it, stuff like [underlays](https://www.byond.com/docs/ref/#/atom/var/underlays) drawing under things. See [Understanding The Renderer](https://www.byond.com/docs/ref/#/{notes}/renderer) -> There is very technically more nuance here. +> There is very technically more nuance here. > In default rendering modes, byond will conflict break by using the thing that is highest in the contents list of its location. Or lowest. Don't remember. -### [`TOPDOWN_MAP`](https://www.byond.com/docs/ref/#/{notes}/topdown) +### [`TOPDOWN_MAP`](https://www.byond.com/docs/ref/#/{notes}/topdown) This is the default rendering format. What we used to use. It tells byond to render going off [plane](#planes) first, then [layer](#layers). There's a few edgecases involving big icons, but it's small peanuts. @@ -604,7 +606,7 @@ The idea is the closer to the front of the screen something is, the higher its l `pixel_y` + `y` tell the engine where something "is". `/atom/var/bound_width`, `/atom/var/bound_height` and `/atom/var/bound_x/y` describe how big it is, which lets us in theory control what it tries to layer "against". -I'm not bothering with reference links because they are entirely unrelated. +I'm not bothering with reference links because they are entirely unrelated. An issue that will crop up with this map format is needing to manage the "visual" (how/where it renders) and physical (where it is in game) aspects of position and size. Physical position tells the renderer how to layer things. Visual position and a combination of physical bounds (manually set) and visual bounds (inferred from other aspects of it. Sprite width/height, physical bounds, transforms, filters, etc) tell it what the sprite might be rendering OVER. @@ -645,28 +647,28 @@ One more thing. Big icons are fucked From the byond reference >If you use an icon wider than one tile, the "footprint" of the isometric icon (the actual map tiles it takes up) will always be a square. That is, if your normal tile size is 64 and you want to show a 128x128 icon, the icon is two tiles wide and so it will take up a 2×2-tile area on the map. The height of a big icon is irrelevant--any excess height beyond width/2 is used to show vertical features. To draw this icon properly, other tiles on that same ground will be moved behind it in the drawing order. -> One important warning about using big icons in isometric mode is that you should only do this with dense atoms. If part of a big mob icon covers the same tile as a tall building for instance, the tall building is moved back and it could be partially covered by other turfs that are actually behind it. A mob walking onto a very large non-dense turf icon would experience similar irregularities. +> One important warning about using big icons in isometric mode is that you should only do this with dense atoms. If part of a big mob icon covers the same tile as a tall building for instance, the tall building is moved back and it could be partially covered by other turfs that are actually behind it. A mob walking onto a very large non-dense turf icon would experience similar irregularities. These can cause very annoying flickering. In fact, MUCH of how rendering works causes flickering. This is because we don't decide on a pixel by pixel case, the engine groups sprites up into a sort of rendering stack, unable to split them up. -This combined with us being unable to modify bounds means that if one bit of the view is conflicting. +This combined with us being unable to modify bounds means that if one bit of the view is conflicting. If A wants to be above B and below C, but B wants to be below A and above C, we'll be unable to resolve the rendering properly, leading to flickering depending on other aspects of the layering. This can just sort of spread. Very hard to debug. -### [`ISOMETRIC_MAP`](https://www.byond.com/docs/ref/#/{notes}/isometric) - +### [`ISOMETRIC_MAP`](https://www.byond.com/docs/ref/#/{notes}/isometric) + Isometric mode, renders everything well, isometrically, biased to the north east. This gives the possibility for fake 3d, assuming you get things drawn properly. It will render things in the foreground "last", after things in the background. This is the right way of thinking about it, it's not rendering things above or below, but in a layering order. This is interesting mostly in the context of understanding [side map](#side_map-check-the-main-page-too), but we did actually run an isometric station for april fools once. It was really cursed and flickered like crazy (which causes client lag). Fun as hell though. -The mode essentially overrides the layer/plane layering discussed before, and inserts new rules. -I wish I knew what those rules EXACTLY are, but I'm betting they're similar to [side map's](#side_map-check-the-main-page-too), and lummy's actually told me those. +The mode essentially overrides the layer/plane layering discussed before, and inserts new rules. +I wish I knew what those rules EXACTLY are, but I'm betting they're similar to [side map's](#side_map-check-the-main-page-too), and lummy's actually told me those. Yes this is all rather poorly documented. Similar to sidemap, we take physical position into account when deciding layering. In addition to its height positioning, we also account for width. -So both `pixel_y` and `pixel_x` can effect layering. `pixel_z` handles strictly visual y, and `pixel_w` handles x. +So both `pixel_y` and `pixel_x` can effect layering. `pixel_z` handles strictly visual y, and `pixel_w` handles x. This has similar big icon problems to [sidemap](#side_map-check-the-main-page-too). @@ -677,14 +679,14 @@ it would be automatically broken down into smaller icon states, which you would ## Color - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/color) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/color) `/atom/var/color` is another one like [pixel offsets](#pixel-offsets) where its most common use is really uninteresting, but it has an interesting edge case I think is fun to discuss/important to know. So let's get the base case out of the way shall we? -At base, you can set an atom's color to some `rrggbbaa` string (see [here](https://www.byond.com/docs/ref/#/{{appendix}}/html-colors)). This will shade every pixel on that atom to said color, and override its [`/atom/var/alpha`](https://www.byond.com/docs/ref/#/atom/var/alpha) value. +At base, you can set an atom's color to some `rrggbbaa` string (see [here](https://www.byond.com/docs/ref/#/{{appendix}}/html-colors)). This will shade every pixel on that atom to said color, and override its [`/atom/var/alpha`](https://www.byond.com/docs/ref/#/atom/var/alpha) value. See [appearance flags](#appearance-flags) for how this effect can carry into overlays and such. That's the boring stuff, now the fun shit. @@ -703,7 +705,7 @@ It'll help visualize this process quite well. Play around with it, it's fun. ## Transform - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/transform) +- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/transform) `/atom/var/transform` allows you to shift, contort, rotate and scale atoms visually. This is done using a matrix, similarly to color matrixes. You will likely never need to use it manually however, since there are @@ -730,17 +732,17 @@ and forget to update this file. ## Animate() - [Table of Contents](#table-of-contents) -- [Reference Entry](https://www.byond.com/docs/ref/#/proc/animate) +- [Reference Entry](https://www.byond.com/docs/ref/#/proc/animate) The animate proc allows us to VISUALLY transition between different values on an appearance on clients, while in actuality setting the values instantly on the servers. This is quite powerful, and lets us do many things, like slow fades, shakes, hell even parallax using matrixes. -It doesn't support everything, and it can be quite temperamental especially if you use things like the flag that makes it work in +It doesn't support everything, and it can be quite temperamental especially if you use things like the flag that makes it work in parallel. It's got a lot of nuance to it, but it's real useful. Works on filters and their variables too, which is AGGRESSIVELY useful. -Lets you give radiation glow a warm pulse, that sort of thing. +Lets you give radiation glow a warm pulse, that sort of thing. ## GAGS - [Table of Contents](#table-of-contents) diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 90bd77c0423..a83ef71ddc6 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -54,8 +54,6 @@ //Helpers for vv_get_dropdown() #define VV_DROPDOWN_OPTION(href_key, name) . += "" -//Same with VV_DROPDOWN_OPTION, but global proc doesn't have src -#define VV_DROPDOWN_OPTION_APPEARANCE(thing, href_key, name) . += "" // VV HREF KEYS #define VV_HK_TARGET "target" diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 958cb116548..403fb0f15d0 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -343,6 +343,19 @@ DEFINE_BITFIELD(vis_flags, list( "VIS_UNDERLAY" = VIS_UNDERLAY, )) +// I am so sorry. Required because vis_flags is both undefinable and unreadable on mutable_appearance +// But we need to display them anyway. See /mutable_appearance/appearance_mirror +DEFINE_BITFIELD(_vis_flags, list( + "VIS_HIDE" = VIS_HIDE, + "VIS_INHERIT_DIR" = VIS_INHERIT_DIR, + "VIS_INHERIT_ICON" = VIS_INHERIT_ICON, + "VIS_INHERIT_ICON_STATE" = VIS_INHERIT_ICON_STATE, + "VIS_INHERIT_ID" = VIS_INHERIT_ID, + "VIS_INHERIT_LAYER" = VIS_INHERIT_LAYER, + "VIS_INHERIT_PLANE" = VIS_INHERIT_PLANE, + "VIS_UNDERLAY" = VIS_UNDERLAY, +)) + DEFINE_BITFIELD(zap_flags, list( "ZAP_ALLOW_DUPLICATES" = ZAP_ALLOW_DUPLICATES, "ZAP_MACHINE_EXPLOSIVE" = ZAP_MACHINE_EXPLOSIVE, diff --git a/code/modules/admin/view_variables/debug_variable_appearance.dm b/code/modules/admin/view_variables/debug_variable_appearance.dm index 0d7bbb4b61c..9e92eba4605 100644 --- a/code/modules/admin/view_variables/debug_variable_appearance.dm +++ b/code/modules/admin/view_variables/debug_variable_appearance.dm @@ -1,236 +1,101 @@ -/* < OH MY GOD. Can't you just make "/image/proc/foo()" instead of making these? > - * /appearance is a hardcoded byond type, and it is very internal type. - * Its type is actually /image, but it isn't truly /image. We defined it as "/appearance" - * new procs to /image will only work to actual /image references, but... - * /appearance references are not capable of executing procs, because these are not real /image - * This is why these global procs exist. Welcome to the curse. - */ -#define ADD_UNUSED_VAR(varlist, thing, varname) if(NAMEOF(##thing, ##varname)) ##varlist += #varname -#define RESULT_VARIABLE_NOT_FOUND "_switch_result_variable_not_found" - -/// An alias datum that allows us to access and view the variables of an appearance by keeping certain known, yet undocumented, variables that we can access and read in a datum for debugging purposes. -/// Kindly do not use this outside of a debugging context. -/image/appearance - parent_type = /atom/movable // This is necessary to access the variables on compile-time. - - // var/override // Sad point. We can't steal byond internal variable name -#ifdef OPENDREAM - // opendream doens't support mouse_drop_zone yet. Remove this once OD supports it. - var/mouse_drop_zone -#endif - -/image/appearance/New(loc, ...) - . = ..() - CRASH("something tried to use '/image/appearance', but this isn't actual type we use. Do not fucking do this.") - -/// Makes a var list of /appearance type actually uses. This will be only called once. -/proc/build_virtual_appearance_vars() - var/list/used_variables = list("vis_flags") // manual listing. - . = used_variables - var/list/unused_var_names = list() - - var/image/appearance/nameof_reference // We don't copy vars from this. - pass(nameof_reference) // compiler complains unused variable - ADD_UNUSED_VAR(unused_var_names, nameof_reference, appearance) // it only does self-reference - ADD_UNUSED_VAR(unused_var_names, nameof_reference, x) // xyz are always 0 - ADD_UNUSED_VAR(unused_var_names, nameof_reference, y) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, z) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, weak_reference) // it's not a good idea to make a weak_ref on this, and this won't have it - ADD_UNUSED_VAR(unused_var_names, nameof_reference, vars) // inherited from /image, but /appearance hasn't this - - // Even if these vars are essential for image, these only exists in an actual type - ADD_UNUSED_VAR(unused_var_names, nameof_reference, filter_data) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, realized_overlays) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, realized_underlays) - - // we have no reason to show these, right? - ADD_UNUSED_VAR(unused_var_names, nameof_reference, _active_timers) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, _datum_components) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, _listen_lookup) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, _signal_procs) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, __auxtools_weakref_id) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, _status_traits) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, cooldowns) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, datum_flags) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, verbs) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, gc_destroyed) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, harddel_deets_dumped) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, open_uis) - ADD_UNUSED_VAR(unused_var_names, nameof_reference, tgui_shared_states) - - var/image/dummy_image = image(null, null) // actual type we'll copy variable names - for(var/each in dummy_image.vars) // try to inherit var list from /image - if(each in unused_var_names) - continue - used_variables += each - del(dummy_image) - dummy_image = null - - return used_variables - -/// debug_variable() proc but made for /appearance type specifically -/proc/debug_variable_appearance(var_name, appearance) - var/value - try - value = locate_appearance_variable(var_name, appearance) - catch - return "