[MIRROR] Multiz Rework: Human Suffering Edition (Contains PLANE CUBE) [MDB IGNORE] (#16472)

* Multiz Rework: Human Suffering Edition (Contains PLANE CUBE)

* skyrat changes

* bodyparts merge

* unres door floorlight fix

* Future upstream fix for blindness

* upcoming upstream airlock fix

* fix button emissive

* Fix FOV markings?

Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: tastyfish <crazychris32@gmail.com>
This commit is contained in:
SkyratBot
2022-10-03 05:30:09 +02:00
committed by GitHub
parent ddf91791f1
commit 9bf006d189
298 changed files with 6165 additions and 1700 deletions

650
.github/guides/VISUALS.md vendored Normal file
View File

@@ -0,0 +1,650 @@
# Visuals in /tg/station 13
Welcome to a breakdown of visuals and visual effects in our codebase, and in BYOND.
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.
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.
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).
We do a LOT, so this document might run on for a bit. Forgive me.
You'll find links to the relevant reference entries at the heading of each entry, alongside a hook back to the head of this document.
### Table of Contents
- [Appearances](#appearances-in-byond)
- [Overlays](#overlays)
- [Visual contents](#visual-contents)
- [Images](#images)
- [Client images](#client-images)
- [View](#view)
- [Eye](#eye)
- [Client screen](#client-screen)
- [Blend mode](#client-screen)
- [Appearance flags](#appearance-flags)
- [Gliding](#gliding)
- [Sight](#sight)
- [BYOND lighting](#byond-lighting)
- [Luminosity](#luminosity)
- [See in dark](#see-in-dark)
- [Infrared](#infrared)
- [Invisibility](#invisibility)
- [Layers](#layers)
- [Planes](#planes)
- [Render target/source](#render-targetsource)
- [Multiz](#multiz)
- [Mouse opacity](#mouse-opacity)
- [Filters](#filters)
- [Particles](#particles)
- [Pixel offsets](#pixel-offsets)
- [Color](#color)
- [Transform](#transform)
- [Lighting](#lighting)
- [Animate()](#animate())
- [GAGS](#gags)
## Appearances in BYOND
- [Table of Contents](#table-of-contents)
- [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.
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.
To start off with, appearances are static. You can't directly edit an appearance "datum", it will throw a runtime or just yell at you.
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,
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),
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
// 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)
var/mutable_appearance/temporary_lad = new()
temporary_lad.appearance = thing
temporary_lad.icon_state += "haha_owned"
return temporary_lad.appearance
```
> **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
- [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.
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.
While overlays are stored as static appearances they can be created using icon states to draw from the overlay'd thing icon, or using `/icon` objects.
Also of note: overlays have a cost on addition, which is why as we will discuss we cache modifications to the list.
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.
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),
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()`,
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 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.
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()`,
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)
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).
Some flags of interest:
- `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.
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.
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.
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.
Careful how much you use it.
## Images
- [Table of Contents](#table-of-contents)
- [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.
See [/client/var/images](#client-images)
> Note: the inheritance between the two is essentially for engine convenience. Don't rely on it.
## Client Images
- [Table of Contents](#table-of-contents)
- [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.
The image objects are displayed at their loc variable, and can be shown to more then one user at once.
### 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.
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.
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.
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.
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)
`/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.
### Client Rendering Modes
- [Zoom Ref](https://www.byond.com/docs/ref/#/{skin}/param/zoom) / [Zoom Mode Ref](https://www.byond.com/docs/ref/#/{skin}/param/zoom-mode)
Clients get some choice in literally how they want the game to be rendered to them.
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.
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,
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)
`/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.
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)
Similar to client images but not *quite* the same, we can also insert objects onto our client's literal screen
This is done by giving it an appropriate `screen_loc` value, and inserting it into the client's `screen` list.
Note: we use screen for other things too, I'll get to that eventually.
`screen` is actually rather uninteresting, but `screen_loc` has a LOT more nuance.
To start with, the format.
The classic `screen_loc` format looks something like this (keeping in mind it counts from the top left):
`x:px,y:py`
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,
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.
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)
`/atom/var/blend_mode` defines how an atom well, renders onto the map.
There's a whole bunch of options but really the only one you need to know offhand is `BLEND_MULTIPLY`, which multiplies the thing being drawn "on" by us.
This is how we do lighting effects, since the lighting [plane](#planes) can be used to multiply just normal coloring. If it's all black, the full screen goes black.
## Appearance Flags
- [Table of Contents](#table-of-contents)
- [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.
Flags of interest:
- `LONG_GLIDE`: without this, diagonal movements will automatically take sqrt(2) more time, to account for the greater distance. We do this calculus automatically, and so want this flipped to disable the behavior.
- `KEEP_TOGETHER`: this allows us to force overlays to render in the same manner as the thing they're overlaid on. Most useful for humans to make alpha changes effect all overlays.
- `PLANE_MASTER`: I will get into this later, but this allows us to use the [plane](#planes) var to relay renders onto screen objects, so we can apply visual effects and masks and such.
- `TILE_BOUND`: By default if something is part in one tile and part in another it will display if either is visible. With this set it'll go off its loc value only.
## Gliding
- [Table of Contents](#table-of-contents)
- [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.
This is done using two patterns. One is how we handle input, the other is BYOND's gliding.
We can edit `/atom/movable/var/glide_size` to set the amount of pixels our mob should move per SERVER tick (Our server tick rate is 20 times a second, or 0.5 deciseconds).
This is done using `/atom/movable/proc/set_glide_size`, which will inform anything we are "carrying" to match our rate.
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.
There's extra cruft here.
> 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.
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 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)
`/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".
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. Very very powerful, we always have this flag set.
## BYOND Lighting
- [Table of Contents](#table-of-contents)
Alongside OUR lighting implementation, which is discussed in with color matrixes, BYOND has its own lighting system.
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,
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)
`/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.
> This "light" is how `/proc/view()` knows if something is in view or not. Oh by the by `view()` respects lighting.
You can actually force it to use a particular mob's sight to avoid aspects of this, this is what `dview()` is
### See in Dark
- [Table of Contents](#table-of-contents)
- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/see_in_dark)
`/mob/var/see_in_dark` sets the radius of a square around the mob that cuts out BYOND darkness.
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)
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
- [Table of Contents](#table-of-contents)
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/invisibility)
`/atom/var/invisibility` is a rudimentary way of hiding things from select groups of users. Think of it like [planes](#planes), or [client images](#client-images) but more limited.
We use this to hide ghosts, ghost visible things, and in the past we used it to hide/show backdrops for the lighting plane, which is semi redundant now.
It's also used to hide some more then ghost invisible things, like some timers and countdowns. It scales from 0 to 101.
`/mob/var/see_invisible` is the catcher of invisibility. If a mob's see_invisible is higher then a target/s invisibility, it'll be shown. Really basic stuff.
## Layers
- [Table of Contents](#table-of-contents)
- [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.
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.
This allows us to keep relative layer differences while not needing to make all sources static. Often very useful.
## Planes
- [Table of Contents](#table-of-contents)
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/plane)
Allllright `/atom/var/plane`s. Let's talk about em.
They serve two purposes. The first is really simple, and basically just a copy of [layers](#layers).
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!
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),
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),
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.
We have a system that allows for arbitrary grouping of plane masters for the purposes of [filter effects](#filters)
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.
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`.
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)
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.
When I say render onto, I mean it literally. It is like adding a second step in the rendering process.
You can even prepend * to the render target value to disable the initial render, and JUST render via the render source.
### Our Implementation
We use render targets to create "render relays" which can be used to link [plane masters](#planes) together and accomplish more advanced effects.
See [the renderer documentation](../../code/_onclick/hud/rendering/_render_readme.md) for visualizations for this.
> Of note: this linking behavior is accomplished by adding a screen object to link onto with a plane value of the desired PM we want to relay onto.
Layer is VERY important here, and will be set based off the layer of the last plane master.
This means plane order is not always the absolute order in which different plane masters render. Be careful of this.
> To edit and display planes and plane connections in game, run the `Edit/Debug Planes` command.
It will open a ui that allows you to view relay connections, plane master descriptions, and edit their values and effects.
## Multiz
- [Table of Contents](#table-of-contents)
- Reference: Hell of our own creation
I'm gonna explain how our multiz system works. But first I need to explain how it used to work.
What we used to do was take an openspace turf above, insert the turf below into its [vis_contents](#visual-contents), and call it a day.
This worked because everything on the map had the `VIS_INHERIT_PLANE` flag, and openspace had a plane master below most everything.
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,
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.
Ok then, old way's not workable. What will we do instead?
There's two problems here. The first is that all our plane masters come pre-ordered. We need a way to have lower and upper plane masters.
This is well... not trivial but not hard either. We essentially duplicate all our plane masters out like a tree, and link the head of the master rendering plate
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.
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.
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)
`/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.
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))
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
master's effects are applied.
## Filters
- [Table of Contents](#table-of-contents)
- [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.
Upside: Very cheap for the server. Downside: Potentially quite laggy for the client.
Take care with these
Like I said, they're quite general purpose. There's a LOT of different effects, and many things you can do with them.
There's two things I want you to know about them, partly to put across their usefulness, and partially so you know their limitations.
On Usefulness. There are filters for alpha masking. They accept render sources as params, which means we can use say, one plane master
to mask out another. This + some fucking bullshit is how emissive lighting works.
Similarly there are filters for distortions. This is how we accomplish the grav anomaly effect, as it too accepts a render source as a param.
On limitations: Filters, like many things in BYOND, are stored in a snowflake list on `/atom`. This means if we want to manage them,
we will need our own management system. This is why we, unlike byond, use a wrapper around filters to set priorities and manage addition/removal.
This system has the potential to break animations and other such things. Take care.
> We have a debug tool for filters, called filterrific. You can access it in-game by vving an atom, going to the dropdown, and hitting `Edit Filters`
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)
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.
At base BYOND only allows you to attach one particle emitter to any one `/atom`. We get around this using an atom inserted into the loc of some parent atom to follow.
The type is `/obj/effect/abstract/particle_holder`. Interacting with it's real simple, you just pass in the location to mirror, and the type to use.
It'll do the rest.
## Pixel Offsets
- [Table of Contents](#table-of-contents)
- [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.
`/atom/var/pixel_x/y/w/z` are variables that allow us to offset the DISPLAY position of an atom. This doesn't effect its position on the map mind,
just where it APPEARS to be. This is useful for many little effects, and some larger ones.
Anyway, onto why I'm mentioning this.
There are two "types" of each direction offset. There's the "real" offset (x/y) and the "fake" offset (w,z).
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`
it matters a whole ton. It's kinda a hard idea to get across, but I hope you have at least some idea.
## Color
- [Table of Contents](#table-of-contents)
- [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.
See [appearance flags](#appearance-flags) for how this effect can carry into overlays and such.
That's the boring stuff, now the fun shit.
> Before we get into this. `rr` is read as "red to red". `ag` is read as "alpha to green", etc. `c` is read as constant, and always has a value of 255
You can use the color variable to not just shade, but shift the colors of the atom.
It accepts a list (functionally a matrix if you know those) in the format `list(rr,br,gr,ar, rb,bb,gb,ab, rg,bg,gg,ag, ra,ba,ga,aa, cr,cb,cg,ca)`
This allows us to essentially multiply the color of each pixel by some other other. The values inserted in each multiple are not really bounded.
You can accomplish some really fun effects with this trick, it gives you a LOT of control over the color of a sprite or say, a [plane master](#planes)
and leads to some fun vfx.
> We have a debug tool for color matrixes. Just VV an atom, go to the VV dropdown and look for the `Edit Color as Matrix` entry.
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)
`/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
helper procs for pretty much everything it can do.
> Note: the transform var is COPIED whenever you read it. So if you want to modify it, you will need to reset the atom var back to your changes.
It's not totally without explanation, and I figured you might wanna know about it. Not a whole lot more to say tho. Neat tool.
## Lighting
- [Table of Contents](#table-of-contents)
- Reference: Hell of our own creation
I wanted to take this chance to briefly explain the essentials of how our lighting system works.
Essentially, each tile has a lighting [overlay](#overlays) (technically an [underlay](https://www.byond.com/docs/ref/#/atom/var/underlays)
which is just overlays but drawn under).
Anyway, each underlay is a color gradient, with red green and blue and alpha in each corner.
Every "corner" (we call them lighting corners) on the map impacts the 4 colors that touch it.
This is done with color matrixes. This allows us to apply color and lighting in a smooth way, while only needing 1 overlay per tile.
There's a lot of nuance here, like how color is calculated and stored, and our overlay lighting system which is a whole other beast.
But it covers the core idea, the rest should be derivable, and you're more qualified to do so then me, assuming some bastard will come along to change it
and forget to update this file.
## Animate()
- [Table of Contents](#table-of-contents)
- [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
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.
## GAGS
- [Table of Contents](#table-of-contents)
- Reference: Hell of our own creation
GAGS is a system of our own design built to support runtime creation of icons from split components.
This means recoloring is trivial, and bits of sprites can be combined and split easily. Very useful.
I won't go into much detail here, check out the [starter guide](https://hackmd.io/@tgstation/GAGS-Walkthrough) for more info if you're interested.

View File

@@ -47,16 +47,20 @@
GLOBAL_LIST_INIT(nonoverlaying_gases, typecache_of_gases_with_no_overlays())
///Returns a list of overlays of every gas in the mixture
#define GAS_OVERLAYS(gases, out_var)\
out_var = list();\
for(var/_ID in gases){\
if(GLOB.nonoverlaying_gases[_ID]) continue;\
var/_GAS = gases[_ID];\
var/_GAS_META = _GAS[GAS_META];\
if(_GAS[MOLES] <= _GAS_META[META_GAS_MOLES_VISIBLE]) continue;\
var/_GAS_OVERLAY = _GAS_META[META_GAS_OVERLAY];\
out_var += _GAS_OVERLAY[min(TOTAL_VISIBLE_STATES, CEILING(_GAS[MOLES] / MOLES_GAS_VISIBLE_STEP, 1))];\
}
#define GAS_OVERLAYS(gases, out_var, z_layer_turf)\
do { \
out_var = list();\
var/offset = GET_TURF_PLANE_OFFSET(z_layer_turf);\
for(var/_ID in gases){\
if(GLOB.nonoverlaying_gases[_ID]) continue;\
var/_GAS = gases[_ID];\
var/_GAS_META = _GAS[GAS_META];\
if(_GAS[MOLES] <= _GAS_META[META_GAS_MOLES_VISIBLE]) continue;\
var/_GAS_OVERLAY = _GAS_META[META_GAS_OVERLAY][offset + 1];\
out_var += _GAS_OVERLAY[min(TOTAL_VISIBLE_STATES, CEILING(_GAS[MOLES] / MOLES_GAS_VISIBLE_STEP, 1))];\
} \
}\
while (FALSE)
#ifdef TESTING
GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
@@ -79,7 +83,7 @@ GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
*
* To equalize two gas mixtures, we simply pool the energy and divide it by the pooled heat capacity.
* T' = (W1+W2) / (C1+C2)
* But if we want to moderate this conduction, maybe we can calculate the energy transferred
* But if we want to moderate this conduction, maybe we can calculate the energy transferred
* and multiply a coefficient to it instead.
* This is the energy transferred:
* W = T' * C1 - W1
@@ -91,20 +95,20 @@ GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
* W = (W2C1 - W1C2) / (C1+C2)
* W = (T2*C2*C1 - T1*C1*C2) / (C1+C2)
* W = (C1*C2) * (T2-T1) / (C1+C2)
*
*
* W: Energy involved in the operation
* T': Combined temperature
* T1, C1, W1: Temp, heat cap, and thermal energy of the first gas mixture
* T2, C2, W2: Temp, heat cap, and thermal energy of the second gas mixture
*
* Not immediately obvious, but saves us operation time.
*
* We put a lot of parentheses here because the numbers get really really big.
*
* We put a lot of parentheses here because the numbers get really really big.
* By prioritizing the division we try to tone the number down so we dont get overflows.
*
*
* Arguments:
* * temperature_delta: T2 - T1. [/datum/gas_mixture/var/temperature]
* If you have any moderating (less than 1) coefficients and are dealing with very big numbers
* If you have any moderating (less than 1) coefficients and are dealing with very big numbers
* multiply the temperature_delta by it first before passing so we get even more breathing room.
* * heat_capacity_one: gasmix one's [/datum/gas_mixture/proc/heat_capacity]
* * heat_capacity_two: gasmix two's [/datum/gas_mixture/proc/heat_capacity]

View File

@@ -0,0 +1,36 @@
// Taken from https://www.byond.com/docs/ref/#/atom/var/blend_mode
// I want you to be able to get these values without using global.vars manually yourself.
// The suggestions here are from the ref, and therefore are NOT ALWAYS ACCURATE TO SS13
// Controls the way the atom's icon is blended onto the icons behind it.
// The blend mode used by an atom is inherited by any attached overlays, unless they override it.
// BLEND_DEFAULT will use the main atom's blend mode; for the atom itself, it's the same as BLEND_OVERLAY.
// #define BLEND_DEFAULT 0
// BLEND_OVERLAY will draw an icon the normal way.
// #define BLEND_OVERLAY 1
// BLEND_ADD will do additive blending, so that the colors in the icon are added to whatever is behind it.
// Light effects like explosions will tend to look better in this mode.
// #define BLEND_ADD 2
// BLEND_SUBTRACT is for subtractive blending. This may be useful for special effects.
// #define BLEND_SUBTRACT 3
// BLEND_MULTIPLY will multiply the icon's colors by whatever is behind it.
// This is typically only useful for applying a colored light effect; for simply darkening, using a translucent black icon with normal overlay blending is a better option.
// #define BLEND_MULTIPLY 4
// BLEND_INSET_OVERLAY overlays the icon, but masks it by the image being drawn on.
// This is pretty much not at all useful directly on the map, but can be very useful for an overlay for an atom that uses KEEP_TOGETHER (see appearance_flags), or for the layering filter.
// #define BLEND_INSET_OVERLAY 5
GLOBAL_LIST_INIT(blend_names, list(
"0" = "BLEND_DEFAULT",
"1" = "BLEND_OVERLAY",
"2" = "BLEND_ADD",
"3" = "BLEND_SUBTRACT",
"4" = "BLEND_MULTIPLY",
"5" = "BLEND_INSET_OVERLAY",
))

View File

@@ -0,0 +1,2 @@
// Sent when the max plane offset changes : (old_max_offset, new_max_offset)
#define COMSIG_PLANE_OFFSET_INCREASE "plane_offset_increase"

View File

@@ -45,7 +45,7 @@
#define COMSIG_MOVABLE_POST_THROW "movable_post_throw"
///from base of datum/thrownthing/finalize(): (obj/thrown_object, datum/thrownthing) used for when a throw is finished
#define COMSIG_MOVABLE_THROW_LANDED "movable_throw_landed"
///from base of atom/movable/on_changed_z_level(): (turf/old_turf, turf/new_turf)
///from base of atom/movable/on_changed_z_level(): (turf/old_turf, turf/new_turf, same_z_layer)
#define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit"
///called when the movable is placed in an unaccessible area, used for stationloving: ()
#define COMSIG_MOVABLE_SECLUDED_LOCATION "movable_secluded"

View File

@@ -2,3 +2,6 @@
// from /client/proc/change_view() : (new_size)
#define COMSIG_VIEW_SET "view_set"
// from /client/proc/handle_popup_close() : (window_id)
#define COMSIG_POPUP_CLEARED "popup_cleared"

View File

@@ -0,0 +1,2 @@
/// Sent from /datum/hud/proc/eye_z_changed() : (old_offset, new_offset)
#define COMSIG_HUD_OFFSET_CHANGED "hud_offset_changed"

View File

@@ -42,8 +42,11 @@
#define COMSIG_MOB_CLIENT_MOVED "mob_client_moved"
/// From base of /client/proc/change_view() (mob/source, new_size)
#define COMSIG_MOB_CLIENT_CHANGE_VIEW "mob_client_change_view"
/// From base of /mob/proc/reset_perspective() (mob/source)
/// From base of /mob/proc/reset_perspective() : ()
#define COMSIG_MOB_RESET_PERSPECTIVE "mob_reset_perspective"
/// from base of /client/proc/set_eye() : (atom/old_eye, atom/new_eye)
#define COMSIG_CLIENT_SET_EYE "client_set_eye"
///from mind/transfer_to. Sent to the receiving mob.
#define COMSIG_MOB_MIND_TRANSFERRED_INTO "mob_mind_transferred_into"
@@ -62,6 +65,16 @@
///from base of mob/create_mob_hud(): ()
#define COMSIG_MOB_HUD_CREATED "mob_hud_created"
///from base of hud/show_to(): (datum/hud/hud_source)
#define COMSIG_MOB_HUD_REFRESHED "mob_hud_refreshed"
///from base of mob/set_sight(): (new_sight, old_sight)
#define COMSIG_MOB_SIGHT_CHANGE "mob_sight_changed"
///from base of mob/set_invis_see(): (new_invis, old_invis)
#define COMSIG_MOB_SEE_INVIS_CHANGE "mob_see_invis_change"
///from base of mob/set_see_in_dark(): (new_range, old_range)
#define COMSIG_MOB_SEE_IN_DARK_CHANGE "mob_see_in_dark_change"
///from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone)
#define COMSIG_MOB_APPLY_DAMAGE "mob_apply_damage"

View File

@@ -211,9 +211,14 @@
#define SCRN_OBJ_IN_LIST "list"
/// In the collapseable palette
#define SCRN_OBJ_IN_PALETTE "palette"
///Inserted first in the list
#define SCRN_OBJ_INSERT_FIRST "first"
// Plane group keys, used to group swaths of plane masters that need to appear in subwindows
/// The primary group, holds everything on the main window
#define PLANE_GROUP_MAIN "main"
/// A secondary group, used when a client views a generic window
#define PLANE_GROUP_POPUP_WINDOW(screen) "popup-[REF(screen)]"
/// The filter name for the hover outline
#define HOVER_OUTLINE_FILTER "hover_outline"

View File

@@ -2,23 +2,20 @@
//KEEP THESE IN A NICE ACSCENDING ORDER, PLEASE
//NEVER HAVE ANYTHING BELOW THIS PLANE ADJUST IF YOU NEED MORE SPACE
#define LOWEST_EVER_PLANE -200
#define LOWEST_EVER_PLANE -100
#define FIELD_OF_VISION_BLOCKER_PLANE -199
#define FIELD_OF_VISION_BLOCKER_PLANE -90
#define FIELD_OF_VISION_BLOCKER_RENDER_TARGET "*FIELD_OF_VISION_BLOCKER_RENDER_TARGET"
#define CLICKCATCHER_PLANE -99
#define CLICKCATCHER_PLANE -80
#define PLANE_SPACE -95
#define PLANE_SPACE_PARALLAX -90
#define PLANE_SPACE -25
#define PLANE_SPACE_PARALLAX -20
#define GRAVITY_PULSE_PLANE -12
#define GRAVITY_PULSE_RENDER_TARGET "*GRAVPULSE_RENDER_TARGET"
#define TRANSPARENT_FLOOR_PLANE -11 //Transparent plane that shows openspace underneath the floor
#define OPENSPACE_PLANE -10 //Openspace plane below all turfs
#define OPENSPACE_BACKDROP_PLANE -9 //Black square just over openspace plane to guaranteed cover all in openspace turf
#define RENDER_PLANE_TRANSPARENT -9 //Transparent plane that shows openspace underneath the floor
#define FLOOR_PLANE -8
@@ -28,15 +25,82 @@
#define GAME_PLANE_UPPER_FOV_HIDDEN -4
///Slightly above the game plane but does not catch mouse clicks. Useful for certain visuals that should be clicked through, like seethrough trees
#define ABOVE_GAME_NO_MOUSE_PLANE -3
#define SEETHROUGH_PLANE -3
#define ABOVE_GAME_PLANE -2
#define MOUSE_TRANSPARENT_PLANE -1 //SKYRAT EDIT ADDITION - Pollution port
#define RENDER_PLANE_GAME_WORLD -1
#define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals
#define AREA_PLANE 2
#define MASSIVE_OBJ_PLANE 3
#define GHOST_PLANE 4
#define POINT_PLANE 5
//---------- LIGHTING -------------
///Normal 1 per turf dynamic lighting underlays
#define LIGHTING_PLANE 10
///Lighting objects that are "free floating"
#define O_LIGHTING_VISUAL_PLANE 11
#define O_LIGHTING_VISUAL_RENDER_TARGET "O_LIGHT_VISUAL_PLANE"
///Things that should render ignoring lighting
#define ABOVE_LIGHTING_PLANE 12
/// This plane masks out lighting to create an "emissive" effect, ie for glowing lights in otherwise dark areas.
#define EMISSIVE_PLANE 14
#define RENDER_PLANE_LIGHTING 15
///---------------- MISC -----------------------
///Pipecrawling images
#define PIPECRAWL_IMAGES_PLANE 20
///AI Camera Static
#define CAMERA_STATIC_PLANE 21
///Anything that wants to be part of the game plane, but also wants to draw above literally everything else
#define HIGH_GAME_PLANE 22
#define FULLSCREEN_PLANE 23
///Visuals that represent sounds happening, and can be seen while blind.
#define SOUND_EFFECT_VISUAL_PLANE 25
///--------------- FULLSCREEN RUNECHAT BUBBLES ------------
///Popup Chat Messages
#define RUNECHAT_PLANE 30
/// Plane for balloon text (text that fades up)
#define BALLOON_CHAT_PLANE 31
//-------------------- HUD ---------------------
//HUD layer defines
#define HUD_PLANE 40
#define ABOVE_HUD_PLANE 41
///Plane of the "splash" icon used that shows on the lobby screen. only render plate planes should be above this
#define SPLASHSCREEN_PLANE 50
//-------------------- Rendering ---------------------
#define RENDER_PLANE_GAME 100
#define RENDER_PLANE_NON_GAME 101
#define RENDER_PLANE_MASTER 102
// Lummox I swear to god I will find you
// NOTE! You can only ever have planes greater then -10000, if you add too many with large offsets you will brick multiz
// Same can be said for large multiz maps. Tread carefully mappers
#define HIGHEST_EVER_PLANE RENDER_PLANE_MASTER
/// The range unique planes can be in
#define PLANE_RANGE (HIGHEST_EVER_PLANE - LOWEST_EVER_PLANE)
// PLANE_SPACE layer(s)
#define SPACE_LAYER 1.8
//#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define. Most floors (FLOOR_PLANE) and walls (GAME_PLANE) use this.
#define OPENSPACE_LAYER 600 //Openspace layer over all turfs
// GAME_PLANE layers
#define CULT_OVERLAY_LAYER 2.01
@@ -60,7 +124,7 @@
#define GAS_PUMP_LAYER 2.49
#define PLUMBING_PIPE_VISIBILE_LAYER 2.495//layer = initial(layer) + ducting_layer / 3333 in atmospherics/handle_layer() to determine order of duct overlap
#define LOW_OBJ_LAYER 2.5
///catwalk overlay of /turf/open/floor/catwalk_floor
///catwalk overlay of /turf/open/floor/plating/catwalk_floor
#define CATWALK_LAYER 2.51
#define LOW_SIGIL_LAYER 2.52
#define SIGIL_LAYER 2.53
@@ -122,81 +186,37 @@
#define GASFIRE_LAYER 5.05
#define RIPPLE_LAYER 5.1
#define OPENSPACE_LAYER 600 //Openspace layer over all
#define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals
#define AREA_PLANE 60
#define MASSIVE_OBJ_PLANE 70
#define GHOST_PLANE 80
#define POINT_PLANE 90
//---------- LIGHTING -------------
///Normal 1 per turf dynamic lighting underlays
#define LIGHTING_PLANE 100
///Lighting objects that are "free floating"
#define O_LIGHTING_VISUAL_PLANE 110
#define O_LIGHTING_VISUAL_RENDER_TARGET "O_LIGHT_VISUAL_PLANE"
///Things that should render ignoring lighting
#define ABOVE_LIGHTING_PLANE 120
#define LIGHTING_PRIMARY_LAYER 15 //The layer for the main lights of the station
#define LIGHTING_PRIMARY_DIMMER_LAYER 15.1 //The layer that dims the main lights of the station
#define LIGHTING_SECONDARY_LAYER 16 //The colourful, usually small lights that go on top
///visibility + hiding of things outside of light source range
#define BYOND_LIGHTING_PLANE 130
//---------- EMISSIVES -------------
//Layering order of these is not particularly meaningful.
//Important part is the seperation of the planes for control via plane_master
/// This plane masks out lighting to create an "emissive" effect, ie for glowing lights in otherwise dark areas.
#define EMISSIVE_PLANE 150
/// The render target used by the emissive layer.
#define EMISSIVE_RENDER_TARGET "*EMISSIVE_PLANE"
/// The layer you should use if you _really_ don't want an emissive overlay to be blocked.
#define EMISSIVE_LAYER_UNBLOCKABLE 9999
///---------------- MISC -----------------------
///Pipecrawling images
#define PIPECRAWL_IMAGES_PLANE 180
///AI Camera Static
#define CAMERA_STATIC_PLANE 200
///Debug Atmos Overlays
#define ATMOS_GROUP_PLANE 450
///--------------- FULLSCREEN IMAGES ------------
#define FULLSCREEN_PLANE 500
#define FLASH_LAYER 1
#define FULLSCREEN_LAYER 2
#define UI_DAMAGE_LAYER 3
#define BLIND_LAYER 4
#define CRIT_LAYER 5
#define CURSE_LAYER 6
#define FOV_EFFECTS_LAYER 10000 //Blindness effects are not layer 4, they lie to you
///--------------- FULLSCREEN RUNECHAT BUBBLES ------------
///Popup Chat Messages
#define RUNECHAT_PLANE 501
/// Plane for balloon text (text that fades up)
#define BALLOON_CHAT_PLANE 502
///--------------- SOUND EFFECT VISUALS ------------
/// Bubble for typing indicators
#define TYPING_LAYER 500
//-------------------- HUD ---------------------
//HUD layer defines
#define HUD_PLANE 1000
#define ABOVE_HUD_PLANE 1100
#define TYPING_LAYER 1
#define FOV_EFFECTS_LAYER 2 //Blindness effects are not layer 4, they lie to you
#define RADIAL_BACKGROUND_LAYER 0
///1000 is an unimportant number, it's just to normalize copied layers
@@ -207,14 +227,6 @@
///Layer for screentips
#define SCREENTIP_LAYER 4
///Plane of the "splash" icon used that shows on the lobby screen. only render plate planes should be above this
#define SPLASHSCREEN_PLANE 9900
//-------------------- Rendering ---------------------
#define RENDER_PLANE_GAME 9990
#define RENDER_PLANE_NON_GAME 9995
#define RENDER_PLANE_MASTER 9999
//----------------------------------------------------
#define LOBBY_BACKGROUND_LAYER 3
#define LOBBY_BUTTON_LAYER 4

View File

@@ -4,7 +4,7 @@
/// This shouldn't be higher than the icon size, and generally you shouldn't be changing this, but it's here just in case.
#define MAX_GLIDE_SIZE 32
/// Compensating for time dialation
/// Compensating for time dilation
GLOBAL_VAR_INIT(glide_size_multiplier, 1.0)
///Broken down, here's what this does:

View File

@@ -30,3 +30,30 @@
#define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses
#define VISOR_DARKNESSVIEW (1<<3)
#define VISOR_INVISVIEW (1<<4)
// BYOND internal values for the sight flags
// See [https://www.byond.com/docs/ref/#/mob/var/sight]
/// can't see anything
//#define BLIND (1<<0)
/// can see all mobs, no matter what
//#define SEE_MOBS (1<<2)
/// can see all objs, no matter what
//#define SEE_OBJS (1<<3)
// can see all turfs (and areas), no matter what
//#define SEE_TURFS (1<<4)
/// can see self, no matter what
//#define SEE_SELF (1<<5)
/// can see infra-red objects (different sort of luminosity, essentially a copy of it, one we do not use)
//#define SEE_INFRA (1<<6)
/// if an object is located on an unlit area, but some of its pixels are
/// in a lit area (via pixel_x,y or smooth movement), can see those pixels
//#define SEE_PIXELS (1<<8)
/// can see through opaque objects
//#define SEE_THRU (1<<9)
/// render dark tiles as blackness (Note, this basically means we draw dark tiles to plane 0)
/// we can then hijack that plane with a plane master, and start drawing it anywhere we want
//#define SEE_BLACKNESS (1<<10)
/// Bitfield of sight flags that show things "inside" the blackness plane
/// We've gotta alpha it down if we get this, cause otherwise the sight flag won't work
#define BLACKNESS_CUTTING (SEE_MOBS|SEE_OBJS|SEE_TURFS|SEE_TURFS|SEE_TURFS)

View File

@@ -121,6 +121,7 @@
#define VV_HK_DIRECT_CONTROL "direct_control"
#define VV_HK_GIVE_DIRECT_CONTROL "give_direct_control"
#define VV_HK_OFFER_GHOSTS "offer_ghosts"
#define VV_HK_VIEW_PLANES "view_planes"
// /mob/living
#define VV_HK_GIVE_SPEECH_IMPEDIMENT "impede_speech"

View File

@@ -1,6 +1,6 @@
/// Creates a mutable appearance glued to the EMISSIVE_PLAN, using the values from a mutable appearance
/proc/emissive_appearance_copy(mutable_appearance/to_use, appearance_flags = (RESET_COLOR|KEEP_APART))
var/mutable_appearance/appearance = mutable_appearance(to_use.icon, to_use.icon_state, to_use.layer, EMISSIVE_PLANE, to_use.alpha, to_use.appearance_flags | appearance_flags)
/proc/emissive_appearance_copy(mutable_appearance/to_use, atom/offset_spokesman, appearance_flags = (RESET_COLOR|KEEP_APART))
var/mutable_appearance/appearance = mutable_appearance(to_use.icon, to_use.icon_state, to_use.layer, offset_spokesman, EMISSIVE_PLANE, to_use.alpha, to_use.appearance_flags | appearance_flags)
appearance.color = GLOB.emissive_color
appearance.pixel_x = to_use.pixel_x
appearance.pixel_y = to_use.pixel_y

78
code/__HELPERS/_planes.dm Normal file
View File

@@ -0,0 +1,78 @@
// This file contains helper macros for plane operations
// See the planes section of Visuals.md for more detail, but essentially
// When we render multiz, we do it by placing all atoms on lower levels on well, lower planes
// This is done with stacks of plane masters (things we use to apply effects to planes)
// These macros exist to facilitate working with this system, and other associated small bits
/// Takes an atom to change the plane of, a new plane value, and something that can be used as a reference to a z level as input
/// Modifies the new value to match the plane we actually want. Note, if you pass in an already offset plane the offsets will add up
/// Use PLANE_TO_TRUE() to avoid this
#define SET_PLANE(thing, new_value, z_reference) (thing.plane = MUTATE_PLANE(new_value, z_reference))
/// Takes a plane and a z reference, and offsets the plane by the mutation
/// The SSmapping.max_plane_offset bit here is technically redundant, but saves a bit of work in the base case
/// And the base case is important to me. Non multiz shouldn't get hit too bad by this code
#define MUTATE_PLANE(new_value, z_reference) ((SSmapping.max_plane_offset) ? GET_NEW_PLANE(new_value, GET_TURF_PLANE_OFFSET(z_reference)) : (new_value))
/// Takes a z reference that we are unsure of, sanity checks it
/// Returns either its offset, or 0 if it's not a valid ref
#define GET_TURF_PLANE_OFFSET(z_reference) ((SSmapping.max_plane_offset && isatom(z_reference)) ? GET_Z_PLANE_OFFSET(z_reference.z) : 0)
/// Essentially just an unsafe version of GET_TURF_PLANE_OFFSET()
/// Takes a z value we returns its offset with a list lookup
/// Will runtime during parts of init. Be careful :)
#define GET_Z_PLANE_OFFSET(z) (SSmapping.z_level_to_plane_offset[z])
/// Takes a plane to offset, and the multiplier to use, and well, does the offsetting
/// Respects a blacklist we use to remove redundant plane masters, such as hud objects
#define GET_NEW_PLANE(new_value, multiplier) (SSmapping.plane_offset_blacklist?["[new_value]"] ? new_value : (new_value) - (PLANE_RANGE * (multiplier)))
// Now for the more niche things
/// Takes an object, new plane, and multipler, and offsets the plane
/// This is for cases where you have a multipler precalculated, and just want to use it
/// Often an optimization, sometimes a necessity
#define SET_PLANE_W_SCALAR(thing, new_value, multiplier) (thing.plane = GET_NEW_PLANE(new_value, multiplier))
/// Implicit plane set. We take the turf from the object we're changing the plane of, and use ITS z as a spokesperson for our plane value
#define SET_PLANE_IMPLICIT(thing, new_value) SET_PLANE_EXPLICIT(thing, new_value, thing)
// This is an unrolled and optimized version of SET_PLANE, for use anywhere where you are unsure of a source's "turfness"
// The plane is cached to allow for fancy stuff to be eval'd once, rather then often
#define SET_PLANE_EXPLICIT(thing, new_value, source) \
do {\
if(SSmapping.max_plane_offset) {\
var/_cached_plane = new_value;\
var/turf/_our_turf = get_turf(source);\
if(_our_turf){\
thing.plane = GET_NEW_PLANE(_cached_plane, GET_Z_PLANE_OFFSET(_our_turf.z));\
}\
}\
else {\
thing.plane = new_value;\
}\
}\
while (FALSE)
// Now for macros that exist to get info from SSmapping
// Mostly about details of planes, or z levels
/// Takes a z level, gets the lowest plane offset in its "stack"
#define GET_LOWEST_STACK_OFFSET(z) ((SSmapping.max_plane_offset) ? SSmapping.z_level_to_lowest_plane_offset[z] : 0)
/// Takes a plane, returns the canonical, unoffset plane it represents
#define PLANE_TO_TRUE(plane) ((SSmapping.plane_offset_to_true) ? SSmapping.plane_offset_to_true["[plane]"] : plane)
/// Takes a plane, returns the offset it uses
#define PLANE_TO_OFFSET(plane) ((SSmapping.plane_to_offset) ? SSmapping.plane_to_offset["[plane]"] : plane)
/// Takes a true plane, returns the offset planes that would canonically represent it
#define TRUE_PLANE_TO_OFFSETS(plane) ((SSmapping.true_to_offset_planes) ? SSmapping.true_to_offset_planes["[plane]"] : list(plane))
/// Takes a render target and an offset, returns a canonical render target string for it
#define OFFSET_RENDER_TARGET(render_target, offset) (_OFFSET_RENDER_TARGET(render_target, SSmapping.render_offset_blacklist?["[render_target]"] ? 0 : offset))
/// Helper macro for the above
/// Honestly just exists to make the pattern of render target strings more readable
#define _OFFSET_RENDER_TARGET(render_target, offset) ("[(render_target)] #[(offset)]")
// Known issues:
// Potentially too much client load? Hard to tell due to not having a potato pc to hand.
// This is solvable with lowspec preferences, which would not be hard to implement
// Player popups will now render their effects, like overlay lights. this is fixable, but I've not gotten to it
// I think overlay lights can render on the wrong z layer. s fucked

View File

@@ -379,7 +379,7 @@ DEFINE_BITFIELD(smoothing_junction, list(
var/junction_dir = reverse_ndir(smoothing_junction)
var/turned_adjacency = REVERSE_DIR(junction_dir)
var/turf/neighbor_turf = get_step(src, turned_adjacency & (NORTH|SOUTH))
var/mutable_appearance/underlay_appearance = mutable_appearance(layer = TURF_LAYER, plane = FLOOR_PLANE)
var/mutable_appearance/underlay_appearance = mutable_appearance(layer = TURF_LAYER, offset_spokesman = src, plane = FLOOR_PLANE)
if(!neighbor_turf.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency))
neighbor_turf = get_step(src, turned_adjacency & (EAST|WEST))

View File

@@ -1312,9 +1312,8 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
* result_appearance - End result appearance/atom/image
* time - Animation duration
* transform_overlay - Appearance/atom/image of effect that moves along the animation - should be horizonatally centered
* reset_after - If FALSE, filters won't be reset and helper vis_objects will not be removed after animation duration expires. Cleanup must be handled by the caller!
*/
/atom/movable/proc/transformation_animation(result_appearance,time = 3 SECONDS,transform_overlay,reset_after=TRUE)
/atom/movable/proc/transformation_animation(result_appearance,time = 3 SECONDS,transform_overlay)
var/list/transformation_objects = GLOB.transformation_animation_objects[src] || list()
//Disappearing part
var/top_part_filter = filter(type="alpha",icon=icon('icons/effects/alphacolors.dmi',"white"),y=0)
@@ -1343,8 +1342,7 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
GLOB.transformation_animation_objects[src] = transformation_objects
for(var/A in transformation_objects)
vis_contents += A
if(reset_after)
addtimer(CALLBACK(src,.proc/_reset_transformation_animation,filter_index),time)
addtimer(CALLBACK(src,.proc/_reset_transformation_animation,filter_index),time)
/*
* Resets filters and removes transformation animations helper objects from vis contents.

View File

@@ -1,14 +1,14 @@
/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EMISSIVE_COLOR].
/proc/emissive_appearance(icon, icon_state = "", layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE)
var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS)
/proc/emissive_appearance(icon, icon_state = "", atom/offset_spokesman, layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE, offset_const)
var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const)
appearance.color = GLOB.emissive_color
return appearance
/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EM_BLOCK_COLOR].
/proc/emissive_blocker(icon, icon_state = "", layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE)
/proc/emissive_blocker(icon, icon_state = "", atom/offset_spokesman, layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE, offset_const)
// Note: alpha doesn't "do" anything, since it's overriden by the color set shortly after
// Consider removing it someday?
var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS)
var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const)
appearance.color = GLOB.em_block_color
return appearance

View File

@@ -891,7 +891,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
GLOB.dview_mob.loc = center
GLOB.dview_mob.see_invisible = invis_flags
GLOB.dview_mob.set_invis_see(invis_flags)
. = view(range, GLOB.dview_mob)
GLOB.dview_mob.loc = null
@@ -925,7 +925,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
#define FOR_DVIEW(type, range, center, invis_flags) \
GLOB.dview_mob.loc = center; \
GLOB.dview_mob.see_invisible = invis_flags; \
GLOB.dview_mob.set_invis_see(invis_flags); \
for(type in view(range, GLOB.dview_mob))
#define FOR_DVIEW_END GLOB.dview_mob.loc = null

View File

@@ -530,6 +530,16 @@
M.Scale(px/sx, py/sy)
transform = M
/atom/movable/screen/click_catcher/Initialize(mapload)
. = ..()
RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, .proc/offset_increased)
offset_increased(SSmapping, 0, SSmapping.max_plane_offset)
// Draw to the lowest plane level offered
/atom/movable/screen/click_catcher/proc/offset_increased(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
SET_PLANE_W_SCALAR(src, initial(plane), new_offset)
/atom/movable/screen/click_catcher/Click(location, control, params)
var/list/modifiers = params2list(params)
if(LAZYACCESS(modifiers, MIDDLE_CLICK) && iscarbon(usr))

View File

@@ -163,7 +163,7 @@
blobpwrdisplay.icon_state = "block"
blobpwrdisplay.screen_loc = ui_health
blobpwrdisplay.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
blobpwrdisplay.plane = ABOVE_HUD_PLANE
SET_PLANE_EXPLICIT(blobpwrdisplay, ABOVE_HUD_PLANE, owner)
blobpwrdisplay.hud = src
infodisplay += blobpwrdisplay

View File

@@ -56,6 +56,19 @@
else
client.screen -= screen
/mob/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
. = ..()
if(!same_z_layer)
relayer_fullscreens()
/mob/proc/relayer_fullscreens()
var/turf/our_lad = get_turf(src)
var/offset = GET_TURF_PLANE_OFFSET(our_lad)
var/atom/movable/screen/fullscreen/screen
for(var/category in screens)
screen = screens[category]
screen.plane = GET_NEW_PLANE(initial(screen.plane), offset)
/atom/movable/screen/fullscreen
icon = 'icons/hud/screen_full.dmi'
icon_state = "default"

View File

@@ -70,10 +70,18 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
var/list/screenoverlays = list() //the screen objects used as whole screen overlays (flash, damageoverlay, etc...)
var/list/inv_slots[SLOTS_AMT] // /atom/movable/screen/inventory objects, ordered by their slot ID.
var/list/hand_slots // /atom/movable/screen/inventory/hand objects, assoc list of "[held_index]" = object
var/list/atom/movable/screen/plane_master/plane_masters = list() // see "appearance_flags" in the ref, assoc list of "[plane]" = object
/// Assoc list of key => "plane master groups"
/// This is normally just the main window, but it'll occasionally contain things like spyglasses windows
var/list/datum/plane_master_group/master_groups = list()
///Assoc list of controller groups, associated with key string group name with value of the plane master controller ref
var/list/atom/movable/plane_master_controller/plane_master_controllers = list()
/// Think of multiz as a stack of z levels. Each index in that stack has its own group of plane masters
/// This variable is the plane offset our mob/client is currently "on"
/// We use it to track what we should show/not show
/// Goes from 0 to the max (z level stack size - 1)
var/current_plane_offset = 0
///UI for screentips that appear when you mouse over things
var/atom/movable/screen/screentip/screentip_text
@@ -83,6 +91,8 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
/// had with a proc call, especially on one of the hottest procs in the
/// game (MouseEntered).
var/screentips_enabled = SCREENTIP_PREFERENCE_ENABLED
/// If this client is being shown atmos debug overlays or not
var/atmos_debug_overlays = FALSE
/// The color to use for the screentips.
/// This is updated by the preference for cheaper reads than would be
@@ -123,10 +133,8 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
hand_slots = list()
for(var/mytype in subtypesof(/atom/movable/screen/plane_master)- /atom/movable/screen/plane_master/rendering_plate)
var/atom/movable/screen/plane_master/instance = new mytype()
plane_masters["[instance.plane]"] = instance
instance.backdrop(mymob)
var/datum/plane_master_group/main/main_group = new(PLANE_GROUP_MAIN)
main_group.attach_to(src)
var/datum/preferences/preferences = owner?.client?.prefs
screentip_color = preferences?.read_preference(/datum/preference/color/screentip_color)
@@ -141,6 +149,59 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
owner.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/see_through_darkness)
AddComponent(/datum/component/zparallax, owner.client)
RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, .proc/on_plane_increase)
RegisterSignal(mymob, COMSIG_MOB_LOGIN, .proc/client_refresh)
RegisterSignal(mymob, COMSIG_MOB_LOGOUT, .proc/clear_client)
RegisterSignal(mymob, COMSIG_MOB_SIGHT_CHANGE, .proc/update_sightflags)
update_sightflags(mymob, mymob.sight, NONE)
/datum/hud/proc/client_refresh(datum/source)
RegisterSignal(mymob.client, COMSIG_CLIENT_SET_EYE, .proc/on_eye_change)
on_eye_change(null, null, mymob.client.eye)
/datum/hud/proc/clear_client(datum/source)
if(mymob.canon_client)
UnregisterSignal(mymob.canon_client, COMSIG_CLIENT_SET_EYE)
/datum/hud/proc/on_eye_change(datum/source, atom/old_eye, atom/new_eye)
SIGNAL_HANDLER
if(old_eye)
UnregisterSignal(old_eye, COMSIG_MOVABLE_Z_CHANGED)
if(new_eye)
// By the time logout runs, the client's eye has already changed
// There's just no log of the old eye, so we need to override
// :sadkirby:
RegisterSignal(new_eye, COMSIG_MOVABLE_Z_CHANGED, .proc/eye_z_changed, override = TRUE)
eye_z_changed(new_eye)
/datum/hud/proc/update_sightflags(datum/source, new_sight, old_sight)
// If neither the old and new flags can see turfs but not objects, don't transform the turfs
// This is to ensure parallax works when you can't see holder objects
if(should_sight_scale(new_sight) == should_sight_scale(old_sight))
return
var/datum/plane_master_group/group = get_plane_group(PLANE_GROUP_MAIN)
group.transform_lower_turfs(src, current_plane_offset)
/datum/hud/proc/should_use_scale()
return should_sight_scale(mymob.sight)
/datum/hud/proc/should_sight_scale(sight_flags)
return (sight_flags & (SEE_TURFS | SEE_OBJS)) != SEE_TURFS
/datum/hud/proc/eye_z_changed(atom/eye)
SIGNAL_HANDLER
var/turf/eye_turf = get_turf(eye)
var/new_offset = GET_TURF_PLANE_OFFSET(eye_turf)
if(current_plane_offset == new_offset)
return
var/old_offset = current_plane_offset
current_plane_offset = new_offset
SEND_SIGNAL(src, COMSIG_HUD_OFFSET_CHANGED, old_offset, new_offset)
var/datum/plane_master_group/group = get_plane_group(PLANE_GROUP_MAIN)
if(group && should_use_scale())
group.transform_lower_turfs(src, new_offset)
/datum/hud/Destroy()
if(mymob.hud_used == src)
@@ -182,7 +243,7 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
wanted_lvl = null
// SKYRAT EDIT END - SKYRAT HUD
QDEL_LIST_ASSOC_VAL(plane_masters)
QDEL_LIST_ASSOC_VAL(master_groups)
QDEL_LIST_ASSOC_VAL(plane_master_controllers)
QDEL_LIST(screenoverlays)
mymob = null
@@ -191,6 +252,38 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
return ..()
/datum/hud/proc/on_plane_increase(datum/source, old_max_offset, new_max_offset)
SIGNAL_HANDLER
build_plane_groups(old_max_offset + 1, new_max_offset)
/// Creates the required plane masters to fill out new z layers (because each "level" of multiz gets its own plane master set)
/datum/hud/proc/build_plane_groups(starting_offset, ending_offset)
for(var/group_key in master_groups)
var/datum/plane_master_group/group = master_groups[group_key]
group.build_plane_masters(starting_offset, ending_offset)
/// Returns the plane master that matches the input plane from the passed in group
/datum/hud/proc/get_plane_master(plane, group_key = PLANE_GROUP_MAIN)
var/plane_key = "[plane]"
var/datum/plane_master_group/group = master_groups[group_key]
return group.plane_masters[plane_key]
/// Returns a list of all plane masters that match the input true plane, drawn from the passed in group (ignores z layer offsets)
/datum/hud/proc/get_true_plane_masters(true_plane, group_key = PLANE_GROUP_MAIN)
var/list/atom/movable/screen/plane_master/masters = list()
for(var/plane in TRUE_PLANE_TO_OFFSETS(true_plane))
masters += get_plane_master(plane, group_key)
return masters
/// Returns all the planes belonging to the passed in group key
/datum/hud/proc/get_planes_from(group_key)
var/datum/plane_master_group/group = master_groups[group_key]
return group.plane_masters
/// Returns the corresponding plane group datum if one exists
/datum/hud/proc/get_plane_group(key)
return master_groups[key]
/mob/proc/create_mob_hud()
if(!client || hud_used)
return
@@ -296,14 +389,14 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
else if (viewmob.hud_used)
viewmob.hud_used.plane_masters_update()
SEND_SIGNAL(screenmob, COMSIG_MOB_HUD_REFRESHED, src)
return TRUE
/datum/hud/proc/plane_masters_update()
// Plane masters are always shown to OUR mob, never to observers
for(var/thing in plane_masters)
var/atom/movable/screen/plane_master/PM = plane_masters[thing]
PM.backdrop(mymob)
mymob.client.screen += PM
for(var/group_key in master_groups)
var/datum/plane_master_group/group = master_groups[group_key]
// Plane masters are always shown to OUR mob, never to observers
group.refresh_hud()
/datum/hud/human/show_hud(version = 0,mob/viewmob)
. = ..()

View File

@@ -1,12 +1,3 @@
/**
* A screen object, which acts as a container for turfs and other things
* you want to show on the map, which you usually attach to "vis_contents".
*/
/atom/movable/screen/map_view
// Map view has to be on the lowest plane to enable proper lighting
layer = GAME_PLANE
plane = GAME_PLANE
/**
* A generic background object.
* It is also implicitly used to allocate a rectangle on the map, which will
@@ -84,9 +75,10 @@
*
* Returns a map name.
*/
/client/proc/create_popup(name, ratiox = 100, ratioy = 100)
/client/proc/create_popup(name, title, ratiox = 100, ratioy = 100)
winclone(src, "popupwindow", name)
var/list/winparams = list()
winparams["title"] = title
winparams["size"] = "[ratiox]x[ratioy]"
winparams["on-close"] = "handle-popup-close [name]"
winset(src, "[name]", list2params(winparams))
@@ -109,13 +101,13 @@
* Width and height are multiplied by 64 by default.
*/
/client/proc/setup_popup(popup_name, width = 9, height = 9, \
tilesize = 2, bg_icon)
tilesize = 2, title, bg_icon)
if(!popup_name)
return
clear_map("[popup_name]_map")
var/x_value = world.icon_size * tilesize * width
var/y_value = world.icon_size * tilesize * height
var/map_name = create_popup(popup_name, x_value, y_value)
var/map_name = create_popup(popup_name, title, x_value, y_value)
var/atom/movable/screen/background/background = new
background.assigned_map = map_name
@@ -139,3 +131,4 @@
/client/verb/handle_popup_close(window_id as text)
set hidden = TRUE
clear_map("[window_id]_map")
SEND_SIGNAL(src, COMSIG_POPUP_CLEARED, window_id)

View File

@@ -0,0 +1,69 @@
/**
* A screen object, which acts as a container for turfs and other things
* you want to show on the map, which you usually attach to "vis_contents".
* Additionally manages the plane masters required to display said container contents
*/
INITIALIZE_IMMEDIATE(/atom/movable/screen/map_view)
/atom/movable/screen/map_view
name = "screen"
// Map view has to be on the lowest plane to enable proper lighting
layer = GAME_PLANE
plane = GAME_PLANE
del_on_map_removal = FALSE
// Weakrefs of all our hud viewers -> a weakref to the hud datum they last used
var/list/datum/weakref/viewers_to_huds = list()
/atom/movable/screen/map_view/Destroy()
for(var/datum/weakref/client_ref in viewers_to_huds)
var/client/our_client = client_ref.resolve()
if(!our_client)
continue
hide_from(our_client.mob)
return ..()
/atom/movable/screen/map_view/proc/generate_view(map_key)
// Map keys have to start and end with an A-Z character,
// and definitely NOT with a square bracket or even a number.
// I wasted 6 hours on this. :agony:
// -- Stylemistake
assigned_map = map_key
set_position(1, 1)
/atom/movable/screen/map_view/proc/display_to(mob/show_to)
show_to.client.register_map_obj(src)
// We need to add planesmasters to the popup, otherwise
// blending fucks up massively. Any planesmaster on the main screen does
// NOT apply to map popups. If there's ever a way to make planesmasters
// omnipresent, then this wouldn't be needed.
// We lazy load this because there's no point creating all these if none's gonna see em
// Store this info in a client -> hud pattern, so ghosts closing the window nukes the right group
var/datum/weakref/client_ref = WEAKREF(show_to.client)
var/datum/weakref/hud_ref = viewers_to_huds[client_ref]
var/datum/hud/our_hud = hud_ref?.resolve()
if(our_hud)
return our_hud.get_plane_group(PLANE_GROUP_POPUP_WINDOW(src))
// Generate a new plane group for this case
var/datum/plane_master_group/popup/pop_planes = new(PLANE_GROUP_POPUP_WINDOW(src), assigned_map)
viewers_to_huds[client_ref] = WEAKREF(show_to.hud_used)
pop_planes.attach_to(show_to.hud_used)
return pop_planes
/atom/movable/screen/map_view/proc/hide_from(mob/hide_from)
hide_from?.canon_client.clear_map(assigned_map)
var/client_ref = WEAKREF(hide_from?.canon_client)
// Make sure we clear the *right* hud
var/datum/weakref/hud_ref = viewers_to_huds[client_ref]
viewers_to_huds -= client_ref
var/datum/hud/clear_from = hud_ref?.resolve()
if(!clear_from)
return
var/datum/plane_master_group/popup/pop_planes = clear_from.get_plane_group(PLANE_GROUP_POPUP_WINDOW(src))
qdel(pop_planes)

View File

@@ -20,28 +20,30 @@
C.parallax_layers.len = C.parallax_layers_max
C.screen |= (C.parallax_layers)
var/atom/movable/screen/plane_master/PM = screenmob.hud_used.plane_masters["[PLANE_SPACE]"]
if(screenmob != mymob)
C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
C.screen += PM
PM.color = list(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0
)
// We could do not do parallax for anything except the main plane group
// This could be changed, but it would require refactoring this whole thing
// And adding non client particular hooks for all the inputs, and I do not have the time I'm sorry :(
for(var/atom/movable/screen/plane_master/plane_master in screenmob.hud_used.get_true_plane_masters(PLANE_SPACE))
if(screenmob != mymob)
C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
C.screen += plane_master
plane_master.color = list(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0
)
/datum/hud/proc/remove_parallax(mob/viewmob)
var/mob/screenmob = viewmob || mymob
var/client/C = screenmob.client
C.screen -= (C.parallax_layers_cached)
var/atom/movable/screen/plane_master/PM = screenmob.hud_used.plane_masters["[PLANE_SPACE]"]
if(screenmob != mymob)
C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
C.screen += PM
PM.color = initial(PM.color)
for(var/atom/movable/screen/plane_master/plane_master in screenmob.hud_used.get_true_plane_masters(PLANE_SPACE))
if(screenmob != mymob)
C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
C.screen += plane_master
plane_master.color = initial(plane_master.color)
C.parallax_layers = null
/datum/hud/proc/apply_parallax_pref(mob/viewmob)
@@ -247,7 +249,6 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
var/speed = 1
var/offset_x = 0
var/offset_y = 0
var/view_sized
var/absolute = FALSE
blend_mode = BLEND_ADD
plane = PLANE_SPACE_PARALLAX
@@ -289,7 +290,6 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
new_overlays += texture_overlay
cut_overlays()
add_overlay(new_overlays)
view_sized = view
/atom/movable/screen/parallax_layer/layer_1
icon_state = "layer1"

View File

@@ -16,6 +16,12 @@
/atom/movable/screen/movable/pic_in_pic/Initialize(mapload)
. = ..()
make_backgrounds()
RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, .proc/multiz_offset_increase)
multiz_offset_increase(SSmapping)
/atom/movable/screen/movable/pic_in_pic/proc/multiz_offset_increase(datum/source)
SIGNAL_HANDLER
SET_PLANE_W_SCALAR(src, initial(plane), SSmapping.max_plane_offset)
/atom/movable/screen/movable/pic_in_pic/Destroy()
for(var/C in shown_to)

View File

@@ -6,6 +6,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
/atom/movable/screen/radial
icon = 'icons/hud/radial.dmi'
plane = ABOVE_HUD_PLANE
vis_flags = VIS_INHERIT_PLANE
var/datum/radial_menu/parent
/atom/movable/screen/radial/proc/set_parent(new_value)
@@ -251,7 +252,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
E.add_overlay(choices_icons[choice_id])
if (choice_datum?.info)
var/obj/effect/abstract/info/info_button = new(E, choice_datum.info)
info_button.plane = ABOVE_HUD_PLANE
SET_PLANE_EXPLICIT(info_button, ABOVE_HUD_PLANE, anchor)
info_button.layer = RADIAL_CONTENT_LAYER
E.vis_contents += info_button
@@ -295,7 +296,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
var/mutable_appearance/MA = new /mutable_appearance(to_extract_from)
if(MA)
MA.plane = ABOVE_HUD_PLANE
SET_PLANE_EXPLICIT(MA, ABOVE_HUD_PLANE, anchor)
MA.layer = RADIAL_CONTENT_LAYER
MA.appearance_flags |= RESET_TRANSFORM
return MA
@@ -314,7 +315,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
current_user = M.client
//Blank
menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing", layer = RADIAL_BACKGROUND_LAYER)
menu_holder.plane = ABOVE_HUD_PLANE
SET_PLANE_EXPLICIT(menu_holder, ABOVE_HUD_PLANE, M)
menu_holder.appearance_flags |= KEEP_APART|RESET_ALPHA|RESET_COLOR|RESET_TRANSFORM
menu_holder.vis_contents += elements + close_button
current_user.images += menu_holder

View File

@@ -1,218 +1,460 @@
// 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|NO_CLIENT_COLOR
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY
plane = LOWEST_EVER_PLANE
var/show_alpha = 255
var/hide_alpha = 0
/// 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--
///integer: what plane we will relay this planes render to
var/render_relay_plane = RENDER_PLANE_GAME
///bool: Whether this plane should get a render target automatically generated
var/generate_render_target = TRUE
///integer: blend mode to apply to the render relay in case you dont want to use the plane_masters blend_mode
/// 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
///reference: current relay this plane is utilizing to render
var/atom/movable/render_plane_relay/relay
/// 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
/atom/movable/screen/plane_master/proc/Show(override)
alpha = override || show_alpha
/// 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
/atom/movable/screen/plane_master/proc/Hide(override)
alpha = override || hide_alpha
/// 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
//Why do plane masters need a backdrop sometimes? Read https://secure.byond.com/forum/?post=2141928
//Trust me, you need one. Period. If you don't think you do, you're doing something extremely wrong.
/atom/movable/screen/plane_master/proc/backdrop(mob/mymob)
SHOULD_CALL_PARENT(TRUE)
if(!isnull(render_relay_plane))
relay_render_to_plane(mymob, render_relay_plane)
///Things rendered on "openspace"; holes in multi-z
/atom/movable/screen/plane_master/openspace_backdrop
name = "open space backdrop plane master"
plane = OPENSPACE_BACKDROP_PLANE
appearance_flags = PLANE_MASTER
blend_mode = BLEND_MULTIPLY
alpha = 255
/atom/movable/screen/plane_master/openspace
name = "open space plane master"
plane = OPENSPACE_PLANE
appearance_flags = PLANE_MASTER
/atom/movable/screen/plane_master/openspace/Initialize(mapload)
/atom/movable/screen/plane_master/Initialize(mapload, datum/plane_master_group/home, offset = 0)
. = ..()
add_filter("first_stage_openspace", 1, drop_shadow_filter(color = "#04080FAA", size = -10))
add_filter("second_stage_openspace", 2, drop_shadow_filter(color = "#04080FAA", size = -15))
add_filter("third_stage_openspace", 3, drop_shadow_filter(color = "#04080FAA", size = -20))
src.offset = offset
true_alpha = alpha
real_plane = plane
///For any transparent multi-z tiles we want to render
/atom/movable/screen/plane_master/transparent
name = "transparent plane master"
plane = TRANSPARENT_FLOOR_PLANE
appearance_flags = PLANE_MASTER
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
if(!our_client)
return TRUE
our_client.screen += src
our_client.screen += relays
return TRUE
/// 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/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
/atom/movable/screen/plane_master/clickcatcher/Initialize(mapload, datum/plane_master_group/home, offset)
. = ..()
RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, .proc/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
///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/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)
/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 plane master"
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
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY
///Contains most things in the game world
/atom/movable/screen/plane_master/game_world
name = "game world plane master"
/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
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
/atom/movable/screen/plane_master/game_world/backdrop(mob/mymob)
. = ..()
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"))
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
/atom/movable/screen/plane_master/game_world_fov_hidden
name = "game world fov hidden plane master"
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_plane = GAME_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
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 = FIELD_OF_VISION_BLOCKER_RENDER_TARGET, flags = MASK_INVERSE))
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 plane master"
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_plane = GAME_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
/atom/movable/screen/plane_master/game_world_upper_fov_hidden
name = "upper game world fov hidden plane master"
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_plane = GAME_PLANE_FOV_HIDDEN
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
/atom/movable/screen/plane_master/game_world_upper_fov_hidden/Initialize()
. = ..()
// 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(GAME_PLANE)
start_hidden = TRUE
/atom/movable/screen/plane_master/game_world_above
name = "above game world plane master"
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_plane = GAME_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
/atom/movable/screen/plane_master/game_world_above_no_mouse
name = "above game world no mouse plane master"
plane = ABOVE_GAME_NO_MOUSE_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
render_relay_plane = GAME_PLANE
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY
/atom/movable/screen/plane_master/massive_obj
name = "massive object plane master"
plane = MASSIVE_OBJ_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
/atom/movable/screen/plane_master/ghost
name = "ghost plane master"
plane = GHOST_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
render_relay_plane = RENDER_PLANE_NON_GAME
/atom/movable/screen/plane_master/point
name = "point plane master"
plane = POINT_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
/**
* Plane master handling byond internal blackness
* vars are set as to replicate behavior when rendering to other planes
* do not touch this unless you know what you are doing
*/
// Blackness renders weird when you view down openspace, because of transforms and borders and such
// This is a consequence of not using lummy's grouped transparency, but I couldn't get that to work without totally fucking up
// Sight flags, and shooting vis_contents usage to the moon. So we're doin it different.
// Look into lessening this, maybe mirror down all the time? idk
// Part of the issue is it isn't actually the blackness plane, it's just normal blackness
// (If image vis contents worked (it should in 515), and we were ok with a maptick cost (wait for threaded maptick)) this could be fixed
/atom/movable/screen/plane_master/blackness
name = "darkness plane master"
name = "Darkness"
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 we're doing here is using plane 0 to capture \"Blackness\", or the mask that hides tiles. Note, this only works if our mob has the SEE_PIXELS or SEE_BLACKNESS sight flags.\
<br>We relay this plane master (on plane 0) down to other copies of itself, depending on the layer your mob is on at the moment.\
<br>Of note: plane master blackness, and the blackness that comes from having nothing to display look similar, but are not the same thing,\
mind yourself when you're working with this plane, you might have accidentially been trying to work with the wrong thing."
plane = BLACKNESS_PLANE
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
// Note: we don't set this to blend multiply because it just dies when its alpha is modified, because of fun byond bugs
// Marked as multiz_scaled = FALSE because it should not scale, scaling lets you see "through" the floor
multiz_scaled = FALSE
/atom/movable/screen/plane_master/blackness/show_to(mob/mymob)
. = ..()
if(!.)
return
if(offset != 0)
// You aren't the source? don't change yourself
return
RegisterSignal(mymob, COMSIG_MOB_SIGHT_CHANGE, .proc/handle_sight_value)
handle_sight_value(mymob, mymob.sight, 0)
var/datum/hud/hud = home.our_hud
if(hud)
RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, .proc/on_offset_change)
offset_change(0, hud.current_plane_offset)
/atom/movable/screen/plane_master/blackness/hide_from(mob/oldmob)
. = ..()
if(offset != 0)
return
UnregisterSignal(oldmob, COMSIG_MOB_SIGHT_CHANGE)
var/datum/hud/hud = home.our_hud
if(hud)
UnregisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, .proc/on_offset_change)
/// Reacts to some new plane master value
/atom/movable/screen/plane_master/blackness/proc/handle_sight_value(datum/source, new_sight, old_sight)
SIGNAL_HANDLER
// Tryin to set a sight flag that cuts blackness eh?
if(new_sight & BLACKNESS_CUTTING)
// Better set alpha then, so it'll actually work
// We just get the one because there is only one blackness PM, it's just mirrored around
disable_alpha()
else
enable_alpha()
/atom/movable/screen/plane_master/blackness/proc/on_offset_change(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
offset_change(old_offset, new_offset)
/atom/movable/screen/plane_master/blackness/proc/offset_change(old_offset, new_offset)
// Basically, the rule here is the blackness we harvest from the mob using the SEE_BLACKNESS flag will be relayed to the darkness
// Plane that we're actually on
if(old_offset != 0) // If our old target wasn't just ourselves
remove_relay_from(GET_NEW_PLANE(plane, old_offset))
if(new_offset != 0)
add_relay_to(GET_NEW_PLANE(plane, new_offset))
/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
/// 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
appearance_flags = PLANE_MASTER | NO_CLIENT_COLOR | PIXEL_SCALE
//byond internal end
///Contains all lighting objects
/atom/movable/screen/plane_master/lighting
name = "lighting plane master"
plane = LIGHTING_PLANE
blend_mode_override = BLEND_MULTIPLY
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/atom/movable/screen/plane_master/lighting/backdrop(mob/mymob)
. = ..()
mymob.overlay_fullscreen("lighting_backdrop_lit", /atom/movable/screen/fullscreen/lighting_backdrop/lit)
mymob.overlay_fullscreen("lighting_backdrop_unlit", /atom/movable/screen/fullscreen/lighting_backdrop/unlit)
/*!
* This system works by exploiting BYONDs color matrix filter to use layers to handle emissive blockers.
*
* Emissive overlays are pasted with an atom color that converts them to be entirely some specific color.
* Emissive blockers are pasted with an atom color that converts them to be entirely some different color.
* Emissive overlays and emissive blockers are put onto the same plane.
* The layers for the emissive overlays and emissive blockers cause them to mask eachother similar to normal BYOND objects.
* A color matrix filter is applied to the emissive plane to mask out anything that isn't whatever the emissive color is.
* This is then used to alpha mask the lighting plane.
*/
/atom/movable/screen/plane_master/lighting/Initialize(mapload)
. = ..()
add_filter("emissives", 1, alpha_mask_filter(render_source = EMISSIVE_RENDER_TARGET, flags = MASK_INVERSE))
add_filter("object_lighting", 2, alpha_mask_filter(render_source = O_LIGHTING_VISUAL_RENDER_TARGET, flags = MASK_INVERSE))
/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 plane master"
name = "Emissive"
documentation = "This system works by exploiting BYONDs color matrix filter to use layers to handle emissive blockers.\
<br>Emissive overlays are pasted with an atom color that converts them to be entirely some specific color.\
<br>Emissive blockers are pasted with an atom color that converts them to be entirely some different color.\
<br>Emissive overlays and emissive blockers are put onto the same plane (This one).\
<br>The layers for the emissive overlays and emissive blockers cause them to mask eachother similar to normal BYOND objects.\
<br>A color matrix filter is applied to the emissive plane to mask out anything that isn't whatever the emissive color is.\
<br>This is then used to alpha mask the lighting plane."
plane = EMISSIVE_PLANE
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
render_target = EMISSIVE_RENDER_TARGET
render_relay_plane = null
render_relay_planes = list()
/atom/movable/screen/plane_master/emissive/Initialize(mapload)
. = ..()
add_filter("em_block_masking", 1, color_matrix_filter(GLOB.em_mask_matrix))
/atom/movable/screen/plane_master/above_lighting
name = "above lighting plane master"
plane = ABOVE_LIGHTING_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
///Contains space parallax
/atom/movable/screen/plane_master/parallax
name = "parallax plane master"
plane = PLANE_SPACE_PARALLAX
blend_mode = BLEND_MULTIPLY
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/atom/movable/screen/plane_master/parallax_white
name = "parallax whitifier plane master"
plane = PLANE_SPACE
/atom/movable/screen/plane_master/pipecrawl
name = "pipecrawl plane master"
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
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY
start_hidden = TRUE
/atom/movable/screen/plane_master/pipecrawl/Initialize(mapload)
. = ..()
@@ -221,85 +463,99 @@
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 plane master"
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
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY
start_hidden = TRUE
/atom/movable/screen/plane_master/excited_turfs
name = "atmos excited turfs"
plane = ATMOS_GROUP_PLANE
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY
alpha = 0
/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/o_light_visual
name = "overlight light visual plane master"
plane = O_LIGHTING_VISUAL_PLANE
render_target = O_LIGHTING_VISUAL_RENDER_TARGET
/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/sound_effect_visual
name = "Sound Effect Visuals"
documentation = "Holds anything that is a game visual, but is displayed over fullscreen effects. \
<br>Displayed over fullscreen effects, but still under runechat and the HUD."
plane = SOUND_EFFECT_VISUAL_PLANE
render_relay_planes = list(RENDER_PLANE_NON_GAME)
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
blend_mode = BLEND_MULTIPLY
blend_mode_override = BLEND_MULTIPLY
/atom/movable/screen/plane_master/runechat
name = "runechat plane master"
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
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY
render_relay_plane = RENDER_PLANE_NON_GAME
render_relay_planes = list(RENDER_PLANE_NON_GAME)
/atom/movable/screen/plane_master/runechat/backdrop(mob/mymob)
/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/gravpulse
name = "gravpulse plane"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
plane = GRAVITY_PULSE_PLANE
blend_mode = BLEND_ADD
blend_mode_override = BLEND_ADD
render_target = GRAVITY_PULSE_RENDER_TARGET
render_relay_plane = null
/atom/movable/screen/plane_master/area
name = "area plane"
plane = AREA_PLANE
/atom/movable/screen/plane_master/balloon_chat
name = "balloon alert plane"
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
render_relay_plane = RENDER_PLANE_NON_GAME
/atom/movable/screen/plane_master/fullscreen
name = "fullscreen alert plane"
plane = FULLSCREEN_PLANE
render_relay_plane = RENDER_PLANE_NON_GAME
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/atom/movable/screen/plane_master/field_of_vision_blocker
name = "field of vision blocker plane master"
plane = FIELD_OF_VISION_BLOCKER_PLANE
render_target = FIELD_OF_VISION_BLOCKER_RENDER_TARGET
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
render_relay_plane = null
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
render_relay_planes = list(RENDER_PLANE_NON_GAME)
/atom/movable/screen/plane_master/hud
name = "HUD plane"
name = "HUD"
documentation = "Contains anything that want to be rendered on the hud. Typically is just screen elements."
plane = HUD_PLANE
render_relay_plane = RENDER_PLANE_NON_GAME
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 plane"
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
render_relay_plane = RENDER_PLANE_NON_GAME
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 plane"
name = "Splashscreen"
documentation = "Anything that's drawn above LITERALLY everything else. Think cinimatics and the well, spashscreen."
plane = SPLASHSCREEN_PLANE
render_relay_plane = RENDER_PLANE_NON_GAME
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
render_relay_planes = list(RENDER_PLANE_NON_GAME)
allows_offsetting = FALSE

View File

@@ -1,6 +1,6 @@
///Atom that manages and controls multiple planes. It's an atom so we can hook into add_filter etc. Multiple controllers can control one plane.
/atom/movable/plane_master_controller
///List of planes in this controllers control. Initially this is a normal list, but becomes an assoc list of plane numbers as strings | plane instance
///List of planes as defines in this controllers control
var/list/controlled_planes = list()
///hud that owns this controller
var/datum/hud/owner_hud
@@ -12,71 +12,68 @@ INITIALIZE_IMMEDIATE(/atom/movable/plane_master_controller)
. = ..()
if(!istype(hud))
return
owner_hud = hud
var/assoc_controlled_planes = list()
for(var/i in controlled_planes)
var/atom/movable/screen/plane_master/instance = owner_hud.plane_masters["[i]"]
if(!instance) //If we looked for a hud that isn't instanced, just keep going
stack_trace("[i] isn't a valid plane master layer for [owner_hud.type], are you sure it exists in the first place?")
continue
assoc_controlled_planes["[i]"] = instance
controlled_planes = assoc_controlled_planes
/atom/movable/plane_master_controller/proc/get_planes()
var/returned_planes = list()
for(var/true_plane in controlled_planes)
returned_planes += get_true_plane(true_plane)
return returned_planes
/atom/movable/plane_master_controller/proc/get_true_plane(true_plane)
var/list/returned_planes = owner_hud.get_true_plane_masters(true_plane)
if(!length(returned_planes)) //If we looked for a hud that isn't instanced, just keep going
stack_trace("[plane] isn't a valid plane master layer for [owner_hud.type], are you sure it exists in the first place?")
return
return returned_planes
///Full override so we can just use filterrific
/atom/movable/plane_master_controller/add_filter(name, priority, list/params)
. = ..()
for(var/i in controlled_planes)
var/atom/movable/screen/plane_master/pm_iterator = controlled_planes[i]
for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes())
pm_iterator.add_filter(name, priority, params)
///Full override so we can just use filterrific
/atom/movable/plane_master_controller/remove_filter(name_or_names)
. = ..()
for(var/i in controlled_planes)
var/atom/movable/screen/plane_master/pm_iterator = controlled_planes[i]
for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes())
pm_iterator.remove_filter(name_or_names)
/atom/movable/plane_master_controller/update_filters()
. = ..()
for(var/i in controlled_planes)
var/atom/movable/screen/plane_master/pm_iterator = controlled_planes[i]
for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes())
pm_iterator.update_filters()
///Gets all filters for this controllers plane masters
/atom/movable/plane_master_controller/proc/get_filters(name)
. = list()
for(var/i in controlled_planes)
var/atom/movable/screen/plane_master/pm_iterator = controlled_planes[i]
for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes())
. += pm_iterator.get_filter(name)
///Transitions all filters owned by this plane master controller
/atom/movable/plane_master_controller/transition_filter(name, time, list/new_params, easing, loop)
. = ..()
for(var/i in controlled_planes)
var/atom/movable/screen/plane_master/pm_iterator = controlled_planes[i]
for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes())
pm_iterator.transition_filter(name, time, new_params, easing, loop)
///Full override so we can just use filterrific
/atom/movable/plane_master_controller/add_atom_colour(coloration, colour_priority)
. = ..()
for(var/i in controlled_planes)
var/atom/movable/screen/plane_master/pm_iterator = controlled_planes[i]
for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes())
pm_iterator.add_atom_colour(coloration, colour_priority)
///Removes an instance of colour_type from the atom's atom_colours list
/atom/movable/plane_master_controller/remove_atom_colour(colour_priority, coloration)
. = ..()
for(var/i in controlled_planes)
var/atom/movable/screen/plane_master/pm_iterator = controlled_planes[i]
for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes())
pm_iterator.remove_atom_colour(colour_priority, coloration)
///Resets the atom's color to null, and then sets it to the highest priority colour available
/atom/movable/plane_master_controller/update_atom_colour()
for(var/i in controlled_planes)
var/atom/movable/screen/plane_master/pm_iterator = controlled_planes[i]
for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes())
pm_iterator.update_atom_colour()
@@ -84,7 +81,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/plane_master_controller)
name = PLANE_MASTERS_GAME
controlled_planes = list(
FLOOR_PLANE,
TRANSPARENT_FLOOR_PLANE,
RENDER_PLANE_TRANSPARENT,
GAME_PLANE,
GAME_PLANE_FOV_HIDDEN,
GAME_PLANE_UPPER,
@@ -108,7 +105,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/plane_master_controller)
GAME_PLANE_FOV_HIDDEN,
GAME_PLANE_UPPER,
GAME_PLANE_UPPER_FOV_HIDDEN,
ABOVE_GAME_NO_MOUSE_PLANE,
SEETHROUGH_PLANE,
ABOVE_GAME_PLANE,
MASSIVE_OBJ_PLANE,
GHOST_PLANE,
@@ -118,8 +115,9 @@ INITIALIZE_IMMEDIATE(/atom/movable/plane_master_controller)
ABOVE_LIGHTING_PLANE,
CAMERA_STATIC_PLANE,
PIPECRAWL_IMAGES_PLANE,
ATMOS_GROUP_PLANE,
HIGH_GAME_PLANE,
FULLSCREEN_PLANE,
SOUND_EFFECT_VISUAL_PLANE,
RUNECHAT_PLANE,
HUD_PLANE,
ABOVE_HUD_PLANE,

View File

@@ -0,0 +1,153 @@
/// Datum that represents one "group" of plane masters
/// So all the main window planes would be in one, all the spyglass planes in another
/// Etc
/datum/plane_master_group
/// Our key in the group list on /datum/hud
/// Should be unique for any group of plane masters in the world
var/key
/// Our parent hud
var/datum/hud/our_hud
/// List in the form "[plane]" = object, the plane masters we own
var/list/atom/movable/screen/plane_master/plane_masters = list()
/// The visual offset we are currently using
var/active_offset = 0
/// What, if any, submap we render onto
var/map = ""
/datum/plane_master_group/New(key, map = "")
. = ..()
src.key = key
src.map = map
build_plane_masters(0, SSmapping.max_plane_offset)
/datum/plane_master_group/Destroy()
orphan_hud()
QDEL_LIST_ASSOC_VAL(plane_masters)
return ..()
/// Display a plane master group to some viewer, so show all our planes to it
/datum/plane_master_group/proc/attach_to(datum/hud/viewing_hud)
if(viewing_hud.master_groups[key])
stack_trace("Hey brother, our key [key] is already in use by a plane master group on the passed in hud, belonging to [viewing_hud.mymob]. Ya fucked up, why are there dupes")
return
our_hud = viewing_hud
our_hud.master_groups[key] = src
show_hud()
transform_lower_turfs(our_hud, active_offset)
/// Hide the plane master from its current hud, fully clear it out
/datum/plane_master_group/proc/orphan_hud()
if(our_hud)
our_hud.master_groups -= key
hide_hud()
our_hud = null
/// Well, refresh our group, mostly useful for plane specific updates
/datum/plane_master_group/proc/refresh_hud()
hide_hud()
show_hud()
/// Fully regenerate our group, resetting our planes to their compile time values
/datum/plane_master_group/proc/rebuild_hud()
hide_hud()
QDEL_LIST_ASSOC_VAL(plane_masters)
build_plane_masters(0, SSmapping.max_plane_offset)
show_hud()
transform_lower_turfs(our_hud, active_offset)
/datum/plane_master_group/proc/hide_hud()
for(var/thing in plane_masters)
var/atom/movable/screen/plane_master/plane = plane_masters[thing]
plane.hide_from(our_hud.mymob)
/datum/plane_master_group/proc/show_hud()
for(var/thing in plane_masters)
var/atom/movable/screen/plane_master/plane = plane_masters[thing]
show_plane(plane)
/// This is mostly a proc so it can be overriden by popups, since they have unique behavior they want to do
/datum/plane_master_group/proc/show_plane(atom/movable/screen/plane_master/plane)
plane.show_to(our_hud.mymob)
/// Returns a list of all the plane master types we want to create
/datum/plane_master_group/proc/get_plane_types()
return subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/rendering_plate
/// Actually generate our plane masters, in some offset range (where offset is the z layers to render to, because each "layer" in a multiz stack gets its own plane master cube)
/datum/plane_master_group/proc/build_plane_masters(starting_offset, ending_offset)
for(var/atom/movable/screen/plane_master/mytype as anything in get_plane_types())
for(var/plane_offset in starting_offset to ending_offset)
if(plane_offset != 0 && !initial(mytype.allows_offsetting))
continue
var/atom/movable/screen/plane_master/instance = new mytype(null, src, plane_offset)
plane_masters["[instance.plane]"] = instance
prep_plane_instance(instance)
/// Similarly, exists so subtypes can do unique behavior to planes on creation
/datum/plane_master_group/proc/prep_plane_instance(atom/movable/screen/plane_master/instance)
return
// It would be nice to setup parallaxing for stairs and things when doing this
// So they look nicer. if you can't it's all good, if you think you can sanely look at monster's work
// It's hard, and potentially expensive. be careful
/datum/plane_master_group/proc/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE)
// No offset? piss off
if(!SSmapping.max_plane_offset)
return
active_offset = new_offset
// Each time we go "down" a visual z level, we'll reduce the scale by this amount
// Chosen because mothblocks liked it, didn't cause motion sickness while also giving a sense of height
var/scale_by = 0.965
// If our mob can see through walls
if(!use_scale)
// This is a workaround for two things
// First of all, if a mob can see objects but not turfs, they will not be shown the holder objects we use for
// What I'd like to do is revert to images if this case throws, but image vis_contents is broken
// https://www.byond.com/forum/post/2821969
// If that's ever fixed, please just use that. thanks :)
scale_by = 1
var/list/offsets = list()
// We accept negatives so going down "zooms" away the drop above as it goes
for(var/offset in -SSmapping.max_plane_offset to SSmapping.max_plane_offset)
// No transformations if we're landing ON you
if(offset == 0)
offsets += null
continue
var/scale = scale_by ** (offset)
var/matrix/multiz_shrink = matrix()
multiz_shrink.Scale(scale)
offsets += multiz_shrink
// So we can talk in 1 -> max_offset * 2 + 1, rather then -max_offset -> max_offset
var/offset_offset = SSmapping.max_plane_offset + 1
for(var/plane_key in plane_masters)
var/atom/movable/screen/plane_master/plane = plane_masters[plane_key]
if(!plane.multiz_scaled || !plane.allows_offsetting)
continue
var/visual_offset = plane.offset - new_offset
if(plane.force_hidden || visual_offset < 0)
// We don't animate here because it should be invisble, but we do mark because it'll look nice
plane.transform = offsets[visual_offset + offset_offset]
continue
animate(plane, transform = offsets[visual_offset + offset_offset], 0.05 SECONDS, easing = LINEAR_EASING)
/// Holds plane masters for popups, like camera windows
/// Note: We do not scale this plane, even though we could
/// This is because it's annoying to get turfs to position inside it correctly
/// If you wanna try someday feel free, but I can't manage it
/datum/plane_master_group/popup
/datum/plane_master_group/popup/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE)
return ..(source, new_offset, FALSE)
/// Holds the main plane master
/datum/plane_master_group/main
/datum/plane_master_group/main/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE)
if(use_scale)
return ..(source, new_offset, source.should_use_scale())
return ..()

View File

@@ -24,61 +24,264 @@
* remember that once planes are unified on a render plate you cant change the layering of them!
*/
/atom/movable/screen/plane_master/rendering_plate
name = "default rendering plate"
name = "Default rendering plate"
multiz_scaled = FALSE
///this plate renders the final screen to show to the player
/atom/movable/screen/plane_master/rendering_plate/master
name = "master rendering plate"
name = "Master rendering plate"
documentation = "The endpoint of all plane masters, you can think of this as the final \"view\" we draw.\
<br>If offset is not 0 this will be drawn to the transparent plane of the floor above, but otherwise this is drawn to nothing, or shown to the player."
plane = RENDER_PLANE_MASTER
render_relay_plane = null
generate_render_target = FALSE
render_relay_planes = list()
/atom/movable/screen/plane_master/rendering_plate/master/show_to(mob/mymob)
. = ..()
if(!.)
return
if(offset == 0)
return
// Non 0 offset render plates will relay up to the transparent plane above them, assuming they're not on the same z level as their target of course
var/datum/hud/hud = home.our_hud
if(hud)
RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, .proc/on_offset_change)
offset_change(hud.current_plane_offset)
/atom/movable/screen/plane_master/rendering_plate/master/hide_from(mob/oldmob)
. = ..()
if(offset == 0)
return
var/datum/hud/hud = home.our_hud
if(hud)
UnregisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, .proc/on_offset_change)
/atom/movable/screen/plane_master/rendering_plate/master/proc/on_offset_change(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
offset_change(new_offset)
/atom/movable/screen/plane_master/rendering_plate/master/proc/offset_change(new_offset)
if(new_offset == offset) // If we're on our own z layer, relay to nothing, just draw
remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_TRANSPARENT, offset - 1))
else // Otherwise, regenerate the relay
add_relay_to(GET_NEW_PLANE(RENDER_PLANE_TRANSPARENT, offset - 1))
///renders general in charachter game objects
/atom/movable/screen/plane_master/rendering_plate/game_world
name = "game rendering plate"
/atom/movable/screen/plane_master/rendering_plate/game_plate
name = "Game rendering plate"
documentation = "Holds all objects that are ahhh, in character? is maybe the best way to describe it.\
<br>We apply a displacement effect from the gravity pulse plane too, so we can warp the game world."
plane = RENDER_PLANE_GAME
render_relay_plane = RENDER_PLANE_MASTER
render_relay_planes = list(RENDER_PLANE_MASTER)
/atom/movable/screen/plane_master/rendering_plate/game_world/Initialize(mapload)
/atom/movable/screen/plane_master/rendering_plate/game_plate/Initialize(mapload)
. = ..()
add_filter("displacer", 1, displacement_map_filter(render_source = GRAVITY_PULSE_RENDER_TARGET, size = 10))
add_filter("displacer", 1, displacement_map_filter(render_source = OFFSET_RENDER_TARGET(GRAVITY_PULSE_RENDER_TARGET, offset), size = 10))
/atom/movable/screen/plane_master/rendering_plate/transparent
name = "Transparent plate"
documentation = "The master rendering plate from the offset below ours will be mirrored onto this plane. That way we achive a \"stack\" effect.\
<br>This plane exists to uplayer the master rendering plate to the correct spot in our z layer's rendering order"
plane = RENDER_PLANE_TRANSPARENT
appearance_flags = PLANE_MASTER
/atom/movable/screen/plane_master/rendering_plate/transparent/Initialize(mapload, datum/plane_master_group/home, offset)
. = ..()
// Don't display us if we're below everything else yeah?
AddComponent(/datum/component/plane_hide_highest_offset)
color = list(0.9,0,0,0, 0,0.9,0,0, 0,0,0.9,0, 0,0,0,1, 0,0,0,0)
///Contains most things in the game world
/atom/movable/screen/plane_master/rendering_plate/game_world
name = "Game world plate"
documentation = "Contains most of the objects in the world. Mobs, machines, etc. Note the drop shadow, it gives a very nice depth effect."
plane = RENDER_PLANE_GAME_WORLD
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
/atom/movable/screen/plane_master/rendering_plate/game_world/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"))
///Contains all lighting objects
/atom/movable/screen/plane_master/rendering_plate/lighting
name = "Lighting plate"
documentation = "Anything on this plane will be <b>multiplied</b> with the plane it's rendered onto (typically the game plane).\
<br>That's how lighting functions at base. Because it uses BLEND_MULTIPLY and occasionally color matrixes, it needs a backdrop of blackness.\
<br>See <a href=\"https://secure.byond.com/forum/?post=2141928\">This byond post</a>\
<br>Lemme see uh, we're masked by the emissive plane so it can actually function (IE: make things glow in the dark).\
<br>We're also masked by the overlay lighting plane, which contains all the movable lights in the game. It draws to us and also the game plane.\
<br>Masks us out so it has the breathing room to apply its effect.\
<br>Oh and we quite often have our alpha changed to achive night vision effects, or things of that sort."
plane = RENDER_PLANE_LIGHTING
blend_mode_override = BLEND_MULTIPLY
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/atom/movable/screen/plane_master/rendering_plate/lighting/show_to(mob/mymob)
. = ..()
if(!.)
return
// This applies a backdrop to our lighting plane
// Why do plane masters need a backdrop sometimes? Read https://secure.byond.com/forum/?post=2141928
// Basically, we need something to brighten
// unlit is perhaps less needed rn, it exists to provide a fullbright for things that can't see the lighting plane
// but we don't actually use invisibility to hide the lighting plane anymore, so it's pointless
mymob.overlay_fullscreen("lighting_backdrop_lit", /atom/movable/screen/fullscreen/lighting_backdrop/lit)
mymob.overlay_fullscreen("lighting_backdrop_unlit", /atom/movable/screen/fullscreen/lighting_backdrop/unlit)
// Sorry, this is a bit annoying
// Basically, we only want the lighting plane we can actually see to attempt to render
// If we don't our lower plane gets totally overriden by the black void of the upper plane
var/datum/hud/hud = home.our_hud
if(hud)
RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, .proc/on_offset_change)
offset_change(hud.current_plane_offset)
set_alpha(mymob.lighting_alpha)
/atom/movable/screen/plane_master/rendering_plate/lighting/hide_from(mob/oldmob)
. = ..()
oldmob.clear_fullscreen("lighting_backdrop_lit")
oldmob.clear_fullscreen("lighting_backdrop_unlit")
var/datum/hud/hud = home.our_hud
if(hud)
UnregisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, .proc/on_offset_change)
/atom/movable/screen/plane_master/rendering_plate/lighting/proc/on_offset_change(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
offset_change(new_offset)
/atom/movable/screen/plane_master/rendering_plate/lighting/proc/offset_change(mob_offset)
// Offsets stack down remember. This implies that we're above the mob's view plane, and shouldn't render
if(offset < mob_offset)
disable_alpha()
else
enable_alpha()
/*!
* This system works by exploiting BYONDs color matrix filter to use layers to handle emissive blockers.
*
* Emissive overlays are pasted with an atom color that converts them to be entirely some specific color.
* Emissive blockers are pasted with an atom color that converts them to be entirely some different color.
* Emissive overlays and emissive blockers are put onto the same plane.
* The layers for the emissive overlays and emissive blockers cause them to mask eachother similar to normal BYOND objects.
* A color matrix filter is applied to the emissive plane to mask out anything that isn't whatever the emissive color is.
* This is then used to alpha mask the lighting plane.
*/
/atom/movable/screen/plane_master/rendering_plate/lighting/Initialize(mapload)
. = ..()
add_filter("emissives", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(EMISSIVE_RENDER_TARGET, offset), flags = MASK_INVERSE))
add_filter("object_lighting", 2, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(O_LIGHTING_VISUAL_RENDER_TARGET, offset), flags = MASK_INVERSE))
///render plate for OOC stuff like ghosts, hud-screen effects, etc
/atom/movable/screen/plane_master/rendering_plate/non_game
name = "non-game rendering plate"
name = "Non-Game rendering plate"
documentation = "Renders anything that's out of character. Mostly useful as a converse to the game rendering plate."
plane = RENDER_PLANE_NON_GAME
render_relay_plane = RENDER_PLANE_MASTER
render_relay_planes = list(RENDER_PLANE_MASTER)
/**
* Plane master proc called in backdrop() that creates a relay object, sets it as needed and then adds it to the clients screen
* Plane master proc called in Initialize() that creates relay objects, and sets them uo as needed
* Sets:
* * layer from plane to avoid z-fighting
* * plane to relay the render to
* * render_source so that the plane will render on this object
* * planes to relay the render to
* * render_source so that the plane will render on these objects
* * mouse opacity to ensure proper mouse hit tracking
* * name for debugging purposes
* Other vars such as alpha will automatically be applied with the render source
* Arguments:
* * mymob: mob whose plane is being backdropped
* * relay_plane: plane we are relaying this plane master to
*/
/atom/movable/screen/plane_master/proc/relay_render_to_plane(mob/mymob, relay_plane)
if(relay in mymob.client.screen) //backdrop can be called multiple times
return
if(!render_target && generate_render_target)
render_target = "*[name]: AUTOGENERATED RENDER TGT"
relay = new()
relay.render_source = render_target
relay.plane = relay_plane
relay.layer = (plane + abs(LOWEST_EVER_PLANE))*0.5 //layer must be positive but can be a decimal
if(blend_mode_override)
relay.blend_mode = blend_mode_override
else
relay.blend_mode = blend_mode
relay.mouse_opacity = mouse_opacity
relay.name = render_target
mymob.client.screen += relay
/atom/movable/screen/plane_master/proc/generate_render_relays()
var/relay_loc = "CENTER"
// If we're using a submap (say for a popup window) make sure we draw onto it
if(home?.map)
relay_loc = "[home.map]:[relay_loc]"
var/list/generated_planes = list()
for(var/atom/movable/render_plane_relay/relay as anything in relays)
generated_planes += relay.plane
for(var/relay_plane in (render_relay_planes - generated_planes))
generate_relay_to(relay_plane, relay_loc)
if(blend_mode != BLEND_MULTIPLY)
blend_mode = BLEND_DEFAULT
relays_generated = TRUE
/// Creates a connection between this plane master and the passed in plane
/// Helper for out of system code, shouldn't be used in this file
/// Build system to differenchiate between generated and non generated render relays
/atom/movable/screen/plane_master/proc/add_relay_to(target_plane, blend_override)
if(get_relay_to(target_plane))
return
render_relay_planes += target_plane
if(!relays_generated && isnull(blend_override))
return
var/client/display_lad = home?.our_hud?.mymob?.client
generate_relay_to(target_plane, show_to = display_lad, blend_override = blend_override)
/proc/get_plane_master_render_base(name)
return "*[name]: AUTOGENERATED RENDER TGT"
/atom/movable/screen/plane_master/proc/generate_relay_to(target_plane, relay_loc, client/show_to, blend_override)
if(!length(relays) && !initial(render_target))
render_target = OFFSET_RENDER_TARGET(get_plane_master_render_base(name), offset)
if(!relay_loc)
relay_loc = "CENTER"
// If we're using a submap (say for a popup window) make sure we draw onto it
if(home?.map)
relay_loc = "[home.map]:[relay_loc]"
var/blend_to_use = blend_override
if(isnull(blend_to_use))
blend_to_use = blend_mode_override || initial(blend_mode)
var/atom/movable/render_plane_relay/relay = new()
relay.render_source = render_target
relay.plane = target_plane
relay.screen_loc = relay_loc
// There are two rules here
// 1: layer needs to be positive (negative layers are treated as float layers)
// 2: lower planes (including offset ones) need to be layered below higher ones (because otherwise they'll render fucky)
// By multiplying LOWEST_EVER_PLANE by 30, we give 30 offsets worth of room to planes before they start going negative
// Bet
relay.layer = (plane + abs(LOWEST_EVER_PLANE * 30)) //layer must be positive but can be a decimal
relay.blend_mode = blend_to_use
relay.mouse_opacity = mouse_opacity
relay.name = render_target
relays += relay
// Relays are sometimes generated early, before huds have a mob to display stuff to
// That's what this is for
if(show_to)
show_to.screen += relay
return relay
/// Breaks a connection between this plane master, and the passed in place
/atom/movable/screen/plane_master/proc/remove_relay_from(target_plane)
render_relay_planes -= target_plane
var/atom/movable/render_plane_relay/existing_relay = get_relay_to(target_plane)
if(!existing_relay)
return
relays -= existing_relay
if(!length(relays) && !initial(render_target))
render_target = null
var/client/lad = home?.our_hud?.mymob?.client
if(lad)
lad.screen -= existing_relay
/// Gets the relay atom we're using to connect to the target plane, if one exists
/atom/movable/screen/plane_master/proc/get_relay_to(target_plane)
for(var/atom/movable/render_plane_relay/relay in relays)
if(relay.plane == target_plane)
return relay
return null
/// Basically, trigger a full hud rebuild so our relays will be added to the screen
/// I hate hud code
/atom/movable/screen/plane_master/proc/rebuild_relays()
relays = list()
var/datum/hud/hud = home.our_hud
hud.show_hud(hud.hud_version)

View File

@@ -136,6 +136,8 @@
static_inventory += using
robit.interfaceButton = using
if(robit.modularInterface)
// Just trust me
robit.modularInterface.vis_flags |= VIS_INHERIT_PLANE
using.vis_contents += robit.modularInterface
var/atom/movable/screen/robot/modpc/tabletbutton = using
tabletbutton.robot = robit
@@ -230,7 +232,7 @@
A.screen_loc = "CENTER[x]:16,SOUTH+[y]:7"
else
A.screen_loc = "CENTER+[x]:16,SOUTH+[y]:7"
A.plane = ABOVE_HUD_PLANE
SET_PLANE_IMPLICIT(A, ABOVE_HUD_PLANE)
x++
if(x == 4)

View File

@@ -9,10 +9,11 @@
/atom/movable/screen
name = ""
icon = 'icons/hud/screen_gen.dmi'
// NOTE: screen objects do NOT change their plane to match the z layer of their owner
// You shouldn't need this, but if you ever do and it's widespread, reconsider what you're doing.
plane = HUD_PLANE
animate_movement = SLIDE_STEPS
speech_span = SPAN_ROBOT
vis_flags = VIS_INHERIT_PLANE
appearance_flags = APPEARANCE_UI
/// A reference to the object in the slot. Grabs or items, generally.
var/obj/master = null
@@ -455,6 +456,7 @@
vis_contents -= hover_overlays_cache[hovering]
hovering = choice
// Don't need to account for turf cause we're on the hud babyyy
var/obj/effect/overlay/zone_sel/overlay_object = hover_overlays_cache[choice]
if(!overlay_object)
overlay_object = new

View File

@@ -282,7 +282,7 @@
var/mutable_appearance/focus_overlay = new(focus)
focus_overlay.layer = layer + 0.01
focus_overlay.plane = ABOVE_HUD_PLANE
SET_PLANE_EXPLICIT(focus_overlay, ABOVE_HUD_PLANE, focus)
. += focus_overlay
/obj/item/tk_grab/suicide_act(mob/user)

View File

@@ -627,12 +627,16 @@ GLOBAL_LIST_EMPTY(colored_turfs)
GLOBAL_LIST_EMPTY(colored_images)
/datum/controller/subsystem/air/proc/setup_turf_visuals()
for(var/sharp_color in GLOB.contrast_colors)
var/obj/effect/overlay/atmos_excited/suger_high = new()
GLOB.colored_turfs += suger_high
var/image/shiny = new('icons/effects/effects.dmi', suger_high, "atmos_top")
shiny.plane = ATMOS_GROUP_PLANE
shiny.color = sharp_color
GLOB.colored_images += shiny
var/list/add_to = list()
GLOB.colored_turfs += list(add_to)
for(var/offset in 0 to SSmapping.max_plane_offset)
var/obj/effect/overlay/atmos_excited/suger_high = new()
SET_PLANE_W_SCALAR(suger_high, HIGH_GAME_PLANE, offset)
add_to += suger_high
var/image/shiny = new('icons/effects/effects.dmi', suger_high, "atmos_top")
SET_PLANE_W_SCALAR(shiny, HIGH_GAME_PLANE, offset)
shiny.color = sharp_color
GLOB.colored_images += shiny
/datum/controller/subsystem/air/proc/setup_template_machinery(list/atmos_machines)
var/obj/machinery/atmospherics/AM
@@ -788,8 +792,7 @@ GLOBAL_LIST_EMPTY(colored_images)
#else
data["display_max"] = FALSE
#endif
var/atom/movable/screen/plane_master/plane = user.hud_used.plane_masters["[ATMOS_GROUP_PLANE]"]
data["showing_user"] = (plane.alpha == 255)
data["showing_user"] = user.hud_used.atmos_debug_overlays
return data
/datum/controller/subsystem/air/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
@@ -827,13 +830,10 @@ GLOBAL_LIST_EMPTY(colored_images)
group.hide_turfs()
return TRUE
if("toggle_user_display")
var/atom/movable/screen/plane_master/plane = ui.user.hud_used.plane_masters["[ATMOS_GROUP_PLANE]"]
if(!plane.alpha)
if(ui.user.client)
ui.user.client.images += GLOB.colored_images
plane.alpha = 255
var/mob/user = ui.user
user.hud_used.atmos_debug_overlays = !user.hud_used.atmos_debug_overlays
if(user.hud_used.atmos_debug_overlays)
user.client.images += GLOB.colored_images
else
if(ui.user.client)
ui.user.client.images -= GLOB.colored_images
plane.alpha = 0
user.client.images -= GLOB.colored_images
return TRUE

View File

@@ -30,6 +30,25 @@ SUBSYSTEM_DEF(mapping)
var/list/holodeck_templates = list()
var/list/areas_in_z = list()
/// List of z level (as number) -> plane offset of that z level
/// Used to maintain the plane cube
var/list/z_level_to_plane_offset = list()
/// List of z level (as number) -> The lowest plane offset in that z stack
var/list/z_level_to_lowest_plane_offset = list()
// This pair allows for easy conversion between an offset plane, and its true representation
// Both are in the form "input plane" -> output plane(s)
/// Assoc list of string plane values to their true, non offset representation
var/list/plane_offset_to_true
/// Assoc list of true string plane values to a list of all potential offset planess
var/list/true_to_offset_planes
/// Assoc list of string plane to the plane's offset value
var/list/plane_to_offset
/// List of planes that do not allow for offsetting
var/list/plane_offset_blacklist
/// List of render targets that do not allow for offsetting
var/list/render_offset_blacklist
/// The largest plane offset we've generated so far
var/max_plane_offset = 0
var/loading_ruins = FALSE
var/list/turf/unused_turfs = list() //Not actually unused turfs they're unused but reserved for use for whatever requests them. "[zlevel_of_turf]" = list(turfs)
@@ -80,6 +99,17 @@ SUBSYSTEM_DEF(mapping)
if(!config || config.defaulted)
to_chat(world, span_boldannounce("Unable to load next or default map config, defaulting to Meta Station."))
config = old_config
plane_offset_to_true = list()
true_to_offset_planes = list()
plane_to_offset = list()
// VERY special cases for FLOAT_PLANE, so it will be treated as expected by plane management logic
// Sorry :(
plane_offset_to_true["[FLOAT_PLANE]"] = FLOAT_PLANE
true_to_offset_planes["[FLOAT_PLANE]"] = list(FLOAT_PLANE)
plane_to_offset["[FLOAT_PLANE]"] = 0
plane_offset_blacklist = list()
render_offset_blacklist = list()
create_plane_offsets(0, 0)
initialize_biomes()
loadWorld()
determine_fake_sale()
@@ -678,3 +708,108 @@ GLOBAL_LIST_EMPTY(the_station_areas)
isolated_ruins_z = add_new_zlevel("Isolated Ruins/Reserved", list(ZTRAIT_RESERVED = TRUE, ZTRAIT_ISOLATED_RUINS = TRUE))
initialize_reserved_level(isolated_ruins_z.z_value)
return isolated_ruins_z.z_value
/// Takes a z level datum, and tells the mapping subsystem to manage it
/// Also handles things like plane offset generation, and other things that happen on a z level to z level basis
/datum/controller/subsystem/mapping/proc/manage_z_level(datum/space_level/new_z)
// First, add the z
z_list += new_z
// Then we build our lookup lists
var/z_value = new_z.z_value
// We are guarenteed that we'll always grow bottom up
// Suck it jannies
z_level_to_plane_offset.len += 1
z_level_to_lowest_plane_offset += 1
// 0's the default value, we'll update it later if required
z_level_to_plane_offset[z_value] = 0
z_level_to_lowest_plane_offset[z_value] = 0
// Now we check if this plane is offset or not
var/below_offset = new_z.traits[ZTRAIT_DOWN]
if(below_offset)
update_plane_tracking(new_z)
// And finally, misc global generation
// We'll have to update this if offsets change, because we load lowest z to highest z
generate_lighting_appearance_by_z(z_value)
/datum/controller/subsystem/mapping/proc/update_plane_tracking(datum/space_level/update_with)
// We're essentially going to walk down the stack of connected z levels, and set their plane offset as we go
// Yes this will cause infinite loops if our templating is fucked. Fuck off
var/below_offset = 0
// I'm sorry, it needs to start at 0
var/current_level = -1
var/current_z = update_with.z_value
var/list/datum/space_level/levels_checked = list()
do
current_level += 1
current_z += below_offset
z_level_to_plane_offset[current_z] = current_level
var/datum/space_level/next_level = z_list[current_z]
below_offset = next_level.traits[ZTRAIT_DOWN]
levels_checked += next_level
while(below_offset)
/// Updates the lowest offset value
for(var/datum/space_level/level_to_update in levels_checked)
z_level_to_lowest_plane_offset[level_to_update.z_value] = current_level
// This can be affected by offsets, so we need to update it
// PAIN
for(var/i in 1 to length(z_list))
generate_lighting_appearance_by_z(i)
var/old_max = max_plane_offset
max_plane_offset = max(max_plane_offset, current_level)
if(max_plane_offset == old_max)
return
generate_offset_lists(old_max + 1, max_plane_offset)
SEND_SIGNAL(src, COMSIG_PLANE_OFFSET_INCREASE, old_max, max_plane_offset)
/// Takes an offset to generate misc lists to, and a base to start from
/// Use this to react globally to maintain parity with plane offsets
/datum/controller/subsystem/mapping/proc/generate_offset_lists(gen_from, new_offset)
create_plane_offsets(gen_from, new_offset)
for(var/offset in gen_from to new_offset)
GLOB.fullbright_overlays += create_fullbright_overlay(offset)
GLOB.cryo_overlays_cover_on += create_cryo_overlay(offset, "cover-on")
GLOB.cryo_overlays_cover_off += create_cryo_overlay(offset, "cover-off")
for(var/datum/gas/gas_type as anything in GLOB.meta_gas_info)
var/list/gas_info = GLOB.meta_gas_info[gas_type]
if(initial(gas_type.moles_visible) != null)
gas_info[META_GAS_OVERLAY] += generate_gas_overlays(gen_from, new_offset, gas_type)
/datum/controller/subsystem/mapping/proc/create_plane_offsets(gen_from, new_offset)
for(var/plane_offset in gen_from to new_offset)
for(var/atom/movable/screen/plane_master/master_type as anything in subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/rendering_plate)
var/plane_to_use = initial(master_type.plane)
var/string_real = "[plane_to_use]"
var/offset_plane = GET_NEW_PLANE(plane_to_use, plane_offset)
var/string_plane = "[offset_plane]"
if(!initial(master_type.allows_offsetting))
plane_offset_blacklist[string_plane] = TRUE
var/render_target = initial(master_type.render_target)
if(!render_target)
render_target = get_plane_master_render_base(initial(master_type.name))
render_offset_blacklist[render_target] = TRUE
if(plane_offset != 0)
continue
plane_offset_to_true[string_plane] = plane_to_use
plane_to_offset[string_plane] = plane_offset
if(!true_to_offset_planes[string_real])
true_to_offset_planes[string_real] = list()
true_to_offset_planes[string_real] |= offset_plane
/proc/generate_lighting_appearance_by_z(z_level)
if(length(GLOB.default_lighting_underlays_by_z) < z_level)
GLOB.default_lighting_underlays_by_z.len = z_level
GLOB.default_lighting_underlays_by_z[z_level] = mutable_appearance(LIGHTING_ICON, "transparent", z_level, null, LIGHTING_PLANE, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM, offset_const = GET_Z_PLANE_OFFSET(z_level))

View File

@@ -131,3 +131,104 @@ SUBSYSTEM_DEF(overlays)
overlays |= cached_other
else if(cut_old)
cut_overlays()
// Debug procs
/atom
/// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances
/// Drawn from the overlays list
var/list/realized_overlays
/image
/// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances
/// Drawn from the overlays list
var/list/realized_overlays
/// Takes the atoms's existing overlays, and makes them mutable so they can be properly vv'd in the realized_overlays list
/atom/proc/realize_overlays()
realized_overlays = list()
var/list/queue = overlays.Copy()
var/queue_index = 0
while(queue_index < length(queue))
queue_index++
// If it's not a command, we assert that it's an appearance
var/mutable_appearance/appearance = queue[queue_index]
if(!appearance) // Who fucking adds nulls to their sublists god you people are the worst
continue
var/mutable_appearance/new_appearance = new /mutable_appearance()
new_appearance.appearance = appearance
var/key = "[appearance.icon]-[appearance.icon_state]-[appearance.plane]-[appearance.layer]-[appearance.dir]-[appearance.color]"
var/tmp_key = key
var/overlay_indx = 1
while(realized_overlays[tmp_key])
tmp_key = "[key]-[overlay_indx]"
overlay_indx++
realized_overlays[tmp_key] = new_appearance
// Now check its children
for(var/mutable_appearance/child_appearance as anything in appearance.overlays)
queue += child_appearance
/// Takes the image's existing overlays, and makes them mutable so they can be properly vv'd in the realized_overlays list
/image/proc/realize_overlays()
realized_overlays = list()
var/list/queue = overlays.Copy()
var/queue_index = 0
while(queue_index < length(queue))
queue_index++
// If it's not a command, we assert that it's an appearance
var/mutable_appearance/appearance = queue[queue_index]
if(!appearance) // Who fucking adds nulls to their sublists god you people are the worst
continue
var/mutable_appearance/new_appearance = new /mutable_appearance()
new_appearance.appearance = appearance
var/key = "[appearance.icon]-[appearance.icon_state]-[appearance.plane]-[appearance.layer]-[appearance.dir]-[appearance.color]"
var/tmp_key = key
var/overlay_indx = 1
while(realized_overlays[tmp_key])
tmp_key = "[key]-[overlay_indx]"
overlay_indx++
realized_overlays[tmp_key] = new_appearance
// Now check its children
for(var/mutable_appearance/child_appearance as anything in appearance.overlays)
queue += child_appearance
/// Takes two appearances as args, prints out, logs, and returns a text representation of their differences
/// Including suboverlays
/proc/diff_appearances(mutable_appearance/first, mutable_appearance/second, iter = 0)
var/list/diffs = list()
var/list/firstdeet = first.vars
var/list/seconddeet = second.vars
var/diff_found = FALSE
for(var/name in first.vars)
var/firstv = firstdeet[name]
var/secondv = seconddeet[name]
if(firstv ~= secondv)
continue
if((islist(firstv) || islist(secondv)) && length(firstv) == 0 && length(secondv) == 0)
continue
if(name == "vars") // Go away
continue
if(name == "comp_lookup") // This is just gonna happen with marked datums, don't care
continue
if(name == "overlays")
first.realize_overlays()
second.realize_overlays()
var/overlays_differ = FALSE
for(var/i in 1 to length(first.realized_overlays))
if(diff_appearances(first.realized_overlays[i], second.realized_overlays[i], iter + 1))
overlays_differ = TRUE
if(!overlays_differ)
continue
diff_found = TRUE
diffs += "Diffs detected at [name]: First ([firstv]), Second ([secondv])"
var/text = "Depth of: [iter]\n\t[diffs.Join("\n\t")]"
message_admins(text)
log_world(text)
return diff_found

View File

@@ -63,6 +63,7 @@
visuals.icon_state = icon_state
visuals.color = beam_color
visuals.layer = ABOVE_ALL_MOB_LAYER
visuals.vis_flags = VIS_INHERIT_PLANE
visuals.update_appearance()
Draw()
RegisterSignal(origin, COMSIG_MOVABLE_MOVED, .proc/redrawing)
@@ -170,7 +171,7 @@
/obj/effect/ebeam/update_overlays()
. = ..()
var/mutable_appearance/emmisive = emissive_appearance(icon, icon_state)
var/mutable_appearance/emmisive = emissive_appearance(icon, icon_state, src)
emmisive.transform = transform
. += emmisive

View File

@@ -62,7 +62,7 @@
desc = "A wonderful yet fake friend."
see_in_dark = 0
lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
sight = NONE
sight = SEE_BLACKNESS
mouse_opacity = MOUSE_OPACITY_ICON
see_invisible = SEE_INVISIBLE_LIVING
invisibility = INVISIBILITY_MAXIMUM
@@ -215,14 +215,19 @@
//speech bubble
if(owner.client)
var/mutable_appearance/MA = mutable_appearance('icons/mob/effects/talk.dmi', src, "default[say_test(message)]", FLY_LAYER)
MA.plane = ABOVE_GAME_PLANE
SET_PLANE_EXPLICIT(MA, ABOVE_GAME_PLANE, src)
MA.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
INVOKE_ASYNC(GLOBAL_PROC, /proc/flick_overlay, MA, list(owner.client), 30)
LAZYADD(update_on_z, MA)
addtimer(CALLBACK(src, .proc/clear_saypopup, MA), 3.5 SECONDS)
for(var/mob/M in GLOB.dead_mob_list)
var/link = FOLLOW_LINK(M, owner)
to_chat(M, "[link] [dead_rendered]")
/mob/camera/imaginary_friend/proc/clear_saypopup(image/say_popup)
LAZYREMOVE(update_on_z, say_popup)
/mob/camera/imaginary_friend/Move(NewLoc, Dir = 0)
if(world.time < move_delay)
return FALSE

View File

@@ -198,7 +198,7 @@
// Build message image
message = image(loc = message_loc, layer = CHAT_LAYER + CHAT_LAYER_Z_STEP * current_z_idx++)
message.plane = RUNECHAT_PLANE
SET_PLANE(message, RUNECHAT_PLANE, message_loc)
message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART
message.alpha = 0
message.pixel_y = target.maptext_height
@@ -212,6 +212,7 @@
LAZYADDASSOCLIST(owned_by.seen_messages, message_loc, src)
owned_by.images |= message
animate(message, alpha = 255, time = CHAT_MESSAGE_SPAWN_TIME)
RegisterSignal(message_loc, COMSIG_MOVABLE_Z_CHANGED, .proc/loc_z_changed)
// Register with the runechat SS to handle EOL and destruction
var/duration = lifespan - CHAT_MESSAGE_EOL_FADE
@@ -229,6 +230,10 @@
animate(message, alpha = 0, time = fadetime, flags = ANIMATION_PARALLEL)
addtimer(CALLBACK(GLOBAL_PROC, /proc/qdel, src), fadetime, TIMER_DELETE_ME, SSrunechat)
/datum/chatmessage/proc/loc_z_changed(datum/source, turf/old_turf, turf/new_turf, same_z_layer)
SIGNAL_HANDLER
SET_PLANE(message, RUNECHAT_PLANE, new_turf)
/**
* Creates a message overlay at a defined location for a given speaker
*

View File

@@ -22,6 +22,9 @@
qdel(src) //no QDEL hint for components, and we dont want this to print a warning regarding bad component application
return
for(var/atom/movable/screen/plane_master/plane_master in mob_parent.hud_used.get_true_plane_masters(FIELD_OF_VISION_BLOCKER_PLANE))
plane_master.unhide_plane(mob_parent)
blocker_mask = new
visual_shadow = new
//visual_shadow.alpha = parent_client?.prefs.read_preference(/datum/preference/numeric/fov_darkness) //SKYRAT EDIT REMOVAL
@@ -32,6 +35,10 @@
update_mask()
/datum/component/fov_handler/Destroy()
var/mob/living/mob_parent = parent
for(var/atom/movable/screen/plane_master/plane_master in mob_parent.hud_used.get_true_plane_masters(FIELD_OF_VISION_BLOCKER_PLANE))
plane_master.hide_plane(mob_parent)
if(applied_mask)
remove_mask()
if(blocker_mask) // In a case of early deletion due to volatile client

View File

@@ -0,0 +1,24 @@
/// Component that takes a plane master, and will hide it if it's the highest offset of its kind
/// This allows us to not show PMs to clients if they're not actively doing anything
/datum/component/plane_hide_highest_offset
/datum/component/plane_hide_highest_offset/Initialize()
if(!istype(parent, /atom/movable/screen/plane_master))
return
RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, .proc/on_offset_increase)
offset_increase(SSmapping.max_plane_offset)
/datum/component/plane_hide_highest_offset/proc/on_offset_increase(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
offset_increase(new_offset)
/datum/component/plane_hide_highest_offset/proc/offset_increase(new_offset)
var/atom/movable/screen/plane_master/plane_parent = parent
var/mob/our_mob = plane_parent.home?.our_hud?.mymob
var/our_offset = plane_parent.offset
if(!our_mob)
return
if(our_offset == new_offset)
plane_parent.hide_plane(our_mob)
else if(plane_parent.force_hidden)
plane_parent.unhide_plane(our_mob)

View File

@@ -36,7 +36,9 @@
return COMPONENT_INCOMPATIBLE
holder.forceMove(parent)
INITIALIZE_IMMEDIATE(/obj/effect/abstract/mirage_holder)
/obj/effect/abstract/mirage_holder
name = "Mirage holder"
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT

View File

@@ -83,13 +83,13 @@
. = ..()
visible_mask = image('icons/effects/light_overlays/light_32.dmi', icon_state = "light")
visible_mask.plane = O_LIGHTING_VISUAL_PLANE
SET_PLANE_EXPLICIT(visible_mask, O_LIGHTING_VISUAL_PLANE, movable_parent)
visible_mask.appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM
visible_mask.alpha = 0
if(is_directional)
directional = TRUE
cone = image('icons/effects/light_overlays/light_cone.dmi', icon_state = "light")
cone.plane = O_LIGHTING_VISUAL_PLANE
SET_PLANE_EXPLICIT(cone, O_LIGHTING_VISUAL_PLANE, movable_parent)
cone.appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM
cone.alpha = 110
cone.transform = cone.transform.Translate(-32, -32)
@@ -119,6 +119,7 @@
RegisterSignal(parent, COMSIG_ATOM_USED_IN_CRAFT, .proc/on_parent_crafted)
RegisterSignal(parent, COMSIG_LIGHT_EATER_QUEUE, .proc/on_light_eater)
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_parent_moved)
RegisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED, .proc/on_z_move)
var/atom/movable/movable_parent = parent
if(movable_parent.light_flags & LIGHT_ATTACHED)
overlay_lighting_flags |= LIGHTING_ATTACHED
@@ -300,6 +301,17 @@
return
make_luminosity_update()
/datum/component/overlay_lighting/proc/on_z_move(atom/source)
SIGNAL_HANDLER
if(current_holder && overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays -= visible_mask
current_holder.underlays -= cone
SET_PLANE_EXPLICIT(visible_mask, O_LIGHTING_VISUAL_PLANE, source)
if(cone)
SET_PLANE_EXPLICIT(cone, O_LIGHTING_VISUAL_PLANE, source)
if(current_holder && overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays += visible_mask
current_holder.underlays += cone
///Called when the current_holder is qdeleted, to remove the light effect.
/datum/component/overlay_lighting/proc/on_parent_attached_to_qdel(atom/movable/source, force)

View File

@@ -95,11 +95,15 @@
///Apply the trickery image and animation
/datum/component/seethrough/proc/trick_mob(mob/fool)
var/datum/hud/our_hud = fool.hud_used
var/atom/movable/screen/plane_master/seethrough = our_hud.get_plane_master(SEETHROUGH_PLANE)
seethrough.unhide_plane(fool)
var/image/user_overlay = new(parent)
user_overlay.loc = parent
user_overlay.override = TRUE
//Special plane so we can click through the overlay
user_overlay.plane = ABOVE_GAME_NO_MOUSE_PLANE
SET_PLANE_EXPLICIT(user_overlay, SEETHROUGH_PLANE, parent)
//These are inherited, but we already use the atom's loc so we end up at double the pixel offset
user_overlay.pixel_x = 0
@@ -135,6 +139,9 @@
var/image/trickery_image = tricked_mobs[fool]
fool.client?.images -= trickery_image
UnregisterSignal(fool, COMSIG_MOB_LOGOUT)
var/datum/hud/our_hud = fool.hud_used
var/atom/movable/screen/plane_master/seethrough = our_hud.get_plane_master(SEETHROUGH_PLANE)
seethrough.hide_plane(fool)
tricked_mobs.Cut()
@@ -145,3 +152,6 @@
tricked_mobs.Remove(fool)
UnregisterSignal(fool, COMSIG_MOB_LOGOUT)
RegisterSignal(fool, COMSIG_MOB_LOGIN, .proc/trick_mob)
var/datum/hud/our_hud = fool.hud_used
var/atom/movable/screen/plane_master/seethrough = our_hud.get_plane_master(SEETHROUGH_PLANE)
seethrough.hide_plane(fool)

View File

@@ -1,5 +1,6 @@
/datum/component/tactical
var/allowed_slot
var/current_slot
/datum/component/tactical/Initialize(allowed_slot)
if(!isitem(parent))
@@ -19,6 +20,15 @@
unmodify()
return ..()
/datum/component/tactical/proc/on_z_move(datum/source)
SIGNAL_HANDLER
var/obj/item/master = parent
if(!ismob(master.loc))
return
var/old_slot = current_slot
unmodify(master, master.loc)
modify(master, master.loc, old_slot)
/datum/component/tactical/proc/modify(obj/item/source, mob/user, slot)
SIGNAL_HANDLER
@@ -26,13 +36,16 @@
unmodify()
return
current_slot = slot
var/obj/item/master = parent
var/image/I = image(icon = master.icon, icon_state = master.icon_state, loc = user)
I.plane = GAME_PLANE_FOV_HIDDEN
SET_PLANE_EXPLICIT(I, GAME_PLANE_FOV_HIDDEN, master)
I.copy_overlays(master)
I.override = TRUE
source.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "sneaking_mission", I)
I.layer = ABOVE_MOB_LAYER
RegisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED, .proc/on_z_move)
/datum/component/tactical/proc/unmodify(obj/item/source, mob/user)
SIGNAL_HANDLER
@@ -44,3 +57,5 @@
user = master.loc
user.remove_alt_appearance("sneaking_mission")
current_slot = null
UnregisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED)

View File

@@ -1,9 +1,180 @@
/// List of z pillars (datums placed in the bottom left of XbyX squares that control transparency in that space)
/// The pillars are stored in triple depth lists indexed by (world_size % pillar_size) + 1
/// They are created at transparent turf request, and deleted when no turfs remain
GLOBAL_LIST_EMPTY(pillars_by_z)
#define Z_PILLAR_RADIUS 20
// Takes a position, transforms it into a z pillar key
#define Z_PILLAR_TRANSFORM(pos) (ROUND_UP(pos / Z_PILLAR_RADIUS))
// Takes a z pillar key, hands back the actual posiiton it represents
// A key of 1 becomes 1, a key of 2 becomes Z_PILLAR_RADIUS + 1, etc.
#define Z_KEY_TO_POSITION(key) (((key - 1) * Z_PILLAR_RADIUS) + 1)
/// Returns a z pillar to insert turfs into
/proc/request_z_pillar(x, y, z)
var/list/pillars_by_z = GLOB.pillars_by_z
if(length(pillars_by_z) < z)
pillars_by_z.len = z
var/list/our_z = pillars_by_z[z]
if(!our_z)
our_z = list()
pillars_by_z[z] = our_z
//Now that we've got the z layer sorted, we're gonna check the X line
var/x_key = Z_PILLAR_TRANSFORM(x)
if(length(our_z) < x_key)
our_z.len = x_key
var/list/our_x = our_z[x_key]
if(!our_x)
our_x = list()
our_z[x_key] = our_x
//And now the y layer
var/y_key = Z_PILLAR_TRANSFORM(y)
if(length(our_x) < y_key)
our_x.len = y_key
var/datum/z_pillar/our_lad = our_x[y_key]
if(!our_lad)
our_lad = new(x_key, y_key, z)
our_x[y_key] = our_lad
return our_lad
/// Exists to be placed on the turf of walls and such to hold the vis_contents of the tile below
/// Otherwise the lower turf might get shifted around, which is dumb. do this instead.
/obj/effect/abstract/z_holder
var/datum/z_pillar/pillar
var/turf/show_for
appearance_flags = PIXEL_SCALE
plane = HUD_PLANE
anchored = TRUE
move_resist = INFINITY
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/obj/effect/abstract/z_holder/Destroy()
if(pillar)
pillar.drawing_object -= show_for
pillar = null
show_for = null
return ..()
/obj/effect/abstract/z_holder/proc/display(turf/display, datum/z_pillar/behalf_of)
if(pillar)
CRASH("We attempted to use a z holder to display when it was already in use, what'd you do")
pillar = behalf_of
show_for = display
vis_contents += display
behalf_of.drawing_object[display] = src
/// Grouping datum that manages transparency for a block of space
/// Setup to ease debugging, and to make add/remove operations cheaper
/datum/z_pillar
var/x_pos
var/y_pos
var/z_pos
/// Assoc list in the form displayed turf -> list of sources
var/list/turf_sources = list()
/// Assoc list of turfs using z holders in the form displayed turf -> z holder
var/list/drawing_object = list()
/datum/z_pillar/New(x_pos, y_pos, z_pos)
. = ..()
src.x_pos = x_pos
src.y_pos = y_pos
src.z_pos = z_pos
/datum/z_pillar/Destroy()
GLOB.pillars_by_z[z_pos][x_pos][y_pos] = null
// Just to be totally clear, this is code that exists to
// A: make sure cleanup is actually possible for this datum, just in case someone goes insane
// B: allow for easier debugging and making sure everything behaves as expected when fully removed
// It is not meant to be relied on, please don't actually it's not very fast
for(var/turf/displaying in turf_sources)
for(var/turf/displaying_for in turf_sources[displaying])
hide_turf(displaying, displaying_for)
return ..()
/// Displays a turf from the z level below us on our level
/datum/z_pillar/proc/display_turf(turf/to_display, turf/source)
var/list/sources = turf_sources[to_display]
if(!sources)
sources = list()
turf_sources[to_display] = sources
sources |= source
if(length(sources) != 1) // If we aren't the first to request this turf, return
var/obj/effect/abstract/z_holder/holding = drawing_object[to_display]
if(!holding)
return
var/turf/visual_target = to_display.above()
/// Basically, if we used to be under a non transparent turf, but are no longer in that position
/// Then we add to the transparent turf we're now under, and nuke the old object
if(!istransparentturf(visual_target))
return
holding.vis_contents -= to_display
qdel(holding)
drawing_object -= to_display
visual_target.vis_contents += to_display
return
var/turf/visual_target = to_display.above()
if(istransparentturf(visual_target) || isopenspaceturf(visual_target))
visual_target.vis_contents += to_display
else
var/obj/effect/abstract/z_holder/hold_this = new(visual_target)
hold_this.display(to_display, src)
/// Hides an existing turf from our vis_contents, or the vis_contents of the source if applicable
/datum/z_pillar/proc/hide_turf(turf/to_hide, turf/source)
var/list/sources = turf_sources[to_hide]
if(!sources)
return
sources -= source
// More sources remain
if(length(sources))
return
turf_sources -= to_hide
var/obj/effect/abstract/z_holder/holding = drawing_object[to_hide]
if(holding)
qdel(holding)
else
var/turf/visual_target = to_hide.above()
visual_target.vis_contents -= to_hide
if(!length(turf_sources) && !QDELETED(src))
qdel(src)
/// Called when a transparent turf is cleared. We wait a tick, then check to see what
/// Kind of turf replaced our former holder, and resetup our visuals as desired
/// We do not need to do this for non transparent holders, because they will have their abstract object cleared
/// When a transparent holder comes back.
/datum/z_pillar/proc/parent_cleared(turf/visual, turf/current_holder)
addtimer(CALLBACK(src, .proc/refresh_orphan, visual, current_holder))
/// Runs the actual refresh of some formerly orphaned via vis_loc deletiong turf
/// We'll only reup if we either have no souece, or if the source is a transparent turf
/datum/z_pillar/proc/refresh_orphan(turf/orphan, turf/parent)
var/list/sources = turf_sources[orphan]
if(!length(sources))
return
var/obj/effect/abstract/z_holder/holding = drawing_object[orphan]
if(holding)
return
if(istransparentturf(parent) || isopenspaceturf(parent))
parent.vis_contents += orphan
else
var/obj/effect/abstract/z_holder/hold_this = new(parent)
hold_this.display(orphan, src)
/datum/element/turf_z_transparency
element_flags = ELEMENT_DETACH
///This proc sets up the signals to handle updating viscontents when turfs above/below update. Handle plane and layer here too so that they don't cover other obs/turfs in Dream Maker
/datum/element/turf_z_transparency/Attach(datum/target, is_openspace = FALSE)
/datum/element/turf_z_transparency/Attach(datum/target, mapload)
. = ..()
if(!isturf(target))
return ELEMENT_INCOMPATIBLE
@@ -11,43 +182,71 @@
var/turf/our_turf = target
our_turf.layer = OPENSPACE_LAYER
if(is_openspace) // openspace and windows have different visual effects but both share this component.
our_turf.plane = OPENSPACE_PLANE
else
our_turf.plane = TRANSPARENT_FLOOR_PLANE
RegisterSignal(target, COMSIG_TURF_MULTIZ_DEL, .proc/on_multiz_turf_del)
RegisterSignal(target, COMSIG_TURF_MULTIZ_NEW, .proc/on_multiz_turf_new)
ADD_TRAIT(our_turf, TURF_Z_TRANSPARENT_TRAIT, ELEMENT_TRAIT(type))
update_multi_z(our_turf)
if(!mapload)
update_multi_z(our_turf)
/datum/element/turf_z_transparency/Detach(datum/source)
. = ..()
var/turf/our_turf = source
our_turf.vis_contents.len = 0
clear_multiz(our_turf)
UnregisterSignal(our_turf, list(COMSIG_TURF_MULTIZ_NEW, COMSIG_TURF_MULTIZ_DEL))
REMOVE_TRAIT(our_turf, TURF_Z_TRANSPARENT_TRAIT, ELEMENT_TRAIT(type))
///Updates the viscontents or underlays below this tile.
/datum/element/turf_z_transparency/proc/update_multi_z(turf/our_turf)
var/turf/below_turf = our_turf.below()
if(below_turf) // If we actually have somethign below us, display it.
our_turf.vis_contents += below_turf
if(below_turf) // If we actually have something below us, display it.
for(var/turf/partner in range(1, below_turf))
// We use our z here to ensure the pillar is actually on our level
var/datum/z_pillar/z_boss = request_z_pillar(partner.x, partner.y, our_turf.z)
z_boss.display_turf(partner, our_turf)
else
our_turf.vis_contents.len = 0 // Nuke the list
add_baseturf_underlay(our_turf)
our_turf.underlays += get_baseturf_underlay(our_turf)
// This shit is stupid
// z transparency is for making something SHOW WHAT'S BENEATH it, or if nothing is, show
// the appropriate underlay
// IT IS NOT FOR MAKING YOUR CLOSED TURF SEETHROUGH
// these are different concerns, and should not be HANDLED TOGETHER
// similarly, if you rip this out, rework diagonal closed turfs to work with this system
// it will make them look significantly nicer, and should let you tie into their logic more easily
// Just please don't break behavior yeah? thanks, I love you <3
if(isclosedturf(our_turf)) //Show girders below closed turfs
var/mutable_appearance/girder_underlay = mutable_appearance('icons/obj/structures.dmi', "girder", layer = TURF_LAYER-0.01)
girder_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR
our_turf.underlays += girder_underlay
var/mutable_appearance/plating_underlay = mutable_appearance('icons/turf/floors.dmi', "plating", layer = TURF_LAYER-0.02)
plating_underlay = RESET_ALPHA | RESET_COLOR
plating_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR
our_turf.underlays += plating_underlay
return TRUE
/datum/element/turf_z_transparency/proc/clear_multiz(turf/our_turf)
var/turf/below_turf = our_turf.below()
if(below_turf) // If we actually have something below us, we need to clear ourselves from it
for(var/turf/partner in range(1, below_turf))
// We use our z here to ensure the pillar is actually on our level
var/datum/z_pillar/z_boss = request_z_pillar(partner.x, partner.y, our_turf.z)
z_boss.hide_turf(partner, our_turf)
if(partner == below_turf)
z_boss.parent_cleared(below_turf, our_turf)
else
our_turf.underlays -= get_baseturf_underlay(our_turf)
if(isclosedturf(our_turf)) //Show girders below closed turfs
var/mutable_appearance/girder_underlay = mutable_appearance('icons/obj/structures.dmi', "girder", layer = TURF_LAYER-0.01)
girder_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR
our_turf.underlays -= girder_underlay
var/mutable_appearance/plating_underlay = mutable_appearance('icons/turf/floors.dmi', "plating", layer = TURF_LAYER-0.02)
plating_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR
our_turf.underlays -= plating_underlay
/datum/element/turf_z_transparency/proc/on_multiz_turf_del(turf/our_turf, turf/below_turf, dir)
SIGNAL_HANDLER
@@ -65,13 +264,13 @@
update_multi_z(our_turf)
///Called when there is no real turf below this turf
/datum/element/turf_z_transparency/proc/add_baseturf_underlay(turf/our_turf)
/datum/element/turf_z_transparency/proc/get_baseturf_underlay(turf/our_turf)
var/turf/path = SSmapping.level_trait(our_turf.z, ZTRAIT_BASETURF) || /turf/open/space
if(!ispath(path))
path = text2path(path)
if(!ispath(path))
warning("Z-level [our_turf.z] has invalid baseturf '[SSmapping.level_trait(our_turf.z, ZTRAIT_BASETURF)]'")
path = /turf/open/space
var/mutable_appearance/underlay_appearance = mutable_appearance(initial(path.icon), initial(path.icon_state), layer = TURF_LAYER-0.02, plane = PLANE_SPACE)
var/mutable_appearance/underlay_appearance = mutable_appearance(initial(path.icon), initial(path.icon_state), layer = TURF_LAYER-0.02, offset_spokesman = our_turf, plane = PLANE_SPACE)
underlay_appearance.appearance_flags = RESET_ALPHA | RESET_COLOR
our_turf.underlays += underlay_appearance
return underlay_appearance

View File

@@ -12,7 +12,20 @@
plane = FLOAT_PLANE
/// Helper similar to image()
/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE)
/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, atom/offset_spokesman, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE, offset_const)
if(plane != FLOAT_PLANE)
// Essentially, we allow users that only want one static offset to pass one in
if(!isnull(offset_const))
plane = GET_NEW_PLANE(plane, offset_const)
// That, or you need to pass in some non null object to reference
else if(!isnull(offset_spokesman))
// Note, we are ok with null turfs, that's not an error condition we'll just default to 0, the error would be
// Not passing ANYTHING in, key difference
var/turf/our_turf = get_turf(offset_spokesman)
plane = MUTATE_PLANE(plane, our_turf)
// otherwise if you're setting plane you better have the guts to back it up
else
stack_trace("No plane offset passed in as context for a non floating mutable appearance, ya done fucked up")
var/mutable_appearance/MA = new()
MA.icon = icon
MA.icon_state = icon_state

View File

@@ -33,7 +33,7 @@
goal = goal_number
bar_loc = target
bar = image('icons/effects/progessbar.dmi', bar_loc, "prog_bar_0")
bar.plane = ABOVE_HUD_PLANE
SET_PLANE_EXPLICIT(bar, ABOVE_HUD_PLANE, User)
bar.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
user = User

View File

@@ -45,9 +45,12 @@
effect.icon_state = overlay.icon_state
effect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
effect.layer = ABOVE_ALL_MOB_LAYER
effect.plane = ABOVE_GAME_PLANE
SET_PLANE(effect, ABOVE_GAME_PLANE, target)
LAZYSET(edgeturf_effects, target, effect)
/datum/proximity_monitor/advanced/projectile_dampener/on_z_change(datum/source)
recalculate_field()
/datum/proximity_monitor/advanced/projectile_dampener/cleanup_edge_turf(turf/target)
. = ..()
var/obj/effect/abstract/effect = LAZYACCESS(edgeturf_effects, target)

View File

@@ -34,9 +34,10 @@
hasprox_receiver = new_host
host = new_host
RegisterSignal(new_host, COMSIG_PARENT_QDELETING, .proc/on_host_or_receiver_del)
var/static/list/containers_connections = list(COMSIG_MOVABLE_MOVED = .proc/on_moved)
var/static/list/containers_connections = list(COMSIG_MOVABLE_MOVED = .proc/on_moved, COMSIG_MOVABLE_Z_CHANGED = .proc/on_z_change)
AddComponent(/datum/component/connect_containers, host, containers_connections)
RegisterSignal(host, COMSIG_MOVABLE_MOVED, .proc/on_moved)
RegisterSignal(host, COMSIG_MOVABLE_Z_CHANGED, .proc/on_z_change)
set_range(current_range, TRUE)
/datum/proximity_monitor/proc/on_host_or_receiver_del(datum/source)
@@ -62,6 +63,10 @@
if(source == host)
hasprox_receiver?.HasProximity(host)
/datum/proximity_monitor/proc/on_z_change()
SIGNAL_HANDLER
return
/datum/proximity_monitor/proc/set_ignore_if_not_on_turf(does_ignore = TRUE)
if(ignore_if_not_on_turf == does_ignore)
return

View File

@@ -339,8 +339,8 @@
alert_type = null
/datum/status_effect/cultghost/on_apply()
owner.see_invisible = SEE_INVISIBLE_OBSERVER
owner.see_in_dark = 2
owner.set_invis_see(SEE_INVISIBLE_OBSERVER)
owner.set_see_in_dark(2)
/datum/status_effect/cultghost/tick()
if(owner.reagents)

View File

@@ -290,7 +290,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
/// Refreshes and item to be put back into the real world, out of storage.
/datum/storage/proc/reset_item(obj/item/thing)
thing.layer = initial(thing.layer)
thing.plane = initial(thing.plane)
SET_PLANE_IMPLICIT(thing, initial(thing.plane))
thing.mouse_opacity = initial(thing.mouse_opacity)
thing.screen_loc = null
if(thing.maptext)
@@ -860,15 +860,17 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
boxes.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+cols-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]"
var/current_x = screen_start_x
var/current_y = screen_start_y
var/turf/our_turf = get_turf(resolve_location)
if(islist(numerical_display_contents))
for(var/type in numerical_display_contents)
var/datum/numbered_display/numberdisplay = numerical_display_contents[type]
numberdisplay.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE
numberdisplay.sample_object.screen_loc = "[current_x]:[screen_pixel_x],[current_y]:[screen_pixel_y]"
numberdisplay.sample_object.maptext = MAPTEXT("<font color='white'>[(numberdisplay.number > 1)? "[numberdisplay.number]" : ""]</font>")
numberdisplay.sample_object.plane = ABOVE_HUD_PLANE
var/obj/item/display_sample = numberdisplay.sample_object
display_sample.mouse_opacity = MOUSE_OPACITY_OPAQUE
display_sample.screen_loc = "[current_x]:[screen_pixel_x],[current_y]:[screen_pixel_y]"
display_sample.maptext = MAPTEXT("<font color='white'>[(numberdisplay.number > 1)? "[numberdisplay.number]" : ""]</font>")
SET_PLANE(display_sample, ABOVE_HUD_PLANE, our_turf)
current_x++
@@ -885,6 +887,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
item.screen_loc = "[current_x]:[screen_pixel_x],[current_y]:[screen_pixel_y]"
item.maptext = ""
item.plane = ABOVE_HUD_PLANE
SET_PLANE(item, ABOVE_HUD_PLANE, our_turf)
current_x++

158
code/datums/visual_data.dm Normal file
View File

@@ -0,0 +1,158 @@
// Allows for linking one mob's view to another
// Exists to make debugging stuff on live easier, please do not build gameplay around this it's not stable
// Mostly because we don't have setters for everything (like ui elements IE: client.screen)
// DEBUG ONLY, THIS IS N O T STABLE ENOUGH FOR PLAYERS
// Should potentially support images, might be too hard tho since there's no default "refresh" tool
// Convenience datum, not for use outside of this ui
/datum/visual_data
/// Sight flags
var/sight = NONE
/// see_invisible values
var/see_invis
/// see_in_dark values
var/see_dark
/// What the client is seeing "out of", client.eye
var/datum/weakref/client_eye
/// Weakref to the mob we're mirroring off
var/datum/weakref/mirroring_off_ref
var/do_updates = FALSE
// Note: we do not attempt to mirror all of screen, instead confining ourselves to mirroring
// Plane master and parralax stuff and such
// Again, this isn't stable
/datum/visual_data/proc/shadow(mob/mirror_off)
do_updates = FALSE
mirroring_off_ref = WEAKREF(mirror_off)
RegisterSignal(mirror_off, COMSIG_MOB_SIGHT_CHANGE, .proc/sight_changed)
sight_changed(mirror_off)
RegisterSignal(mirror_off, COMSIG_MOB_SEE_INVIS_CHANGE, .proc/invis_changed)
invis_changed(mirror_off)
RegisterSignal(mirror_off, COMSIG_MOB_SEE_IN_DARK_CHANGE, .proc/in_dark_changed)
in_dark_changed(mirror_off)
RegisterSignal(mirror_off, COMSIG_MOB_LOGIN, .proc/on_login)
RegisterSignal(mirror_off, COMSIG_MOB_LOGOUT, .proc/on_logout)
if(mirror_off.client)
on_login(mirror_off)
do_updates = TRUE
/datum/visual_data/proc/paint_onto(mob/paint_to)
// Note: we explicitly do NOT use setters here, since it would break the behavior
paint_to.sight = sight
paint_to.see_invisible = see_invis
paint_to.see_in_dark = see_dark
if(paint_to.client)
var/atom/eye = client_eye?.resolve()
if(eye)
paint_to.client.eye = eye
// This is hacky I know, I don't have a way to update just
/// Plane masters. I'm sorry
var/mob/mirroring_off = mirroring_off_ref?.resolve()
if(mirroring_off?.client && paint_to != mirroring_off)
paint_to.client.screen = mirroring_off.client.screen
/datum/visual_data/proc/on_update()
return
/datum/visual_data/proc/sight_changed(mob/source)
SIGNAL_HANDLER
sight = source.sight
on_update()
/datum/visual_data/proc/invis_changed(mob/source)
SIGNAL_HANDLER
see_invis = source.see_invisible
on_update()
/datum/visual_data/proc/in_dark_changed(mob/source)
SIGNAL_HANDLER
see_dark = source.see_in_dark
on_update()
/datum/visual_data/proc/on_login(mob/source)
SIGNAL_HANDLER
// visual data can be created off login, so conflicts here are inevitable
// Best to just override
RegisterSignal(source.client, COMSIG_CLIENT_SET_EYE, .proc/eye_change, override = TRUE)
set_eye(source.client.eye)
/datum/visual_data/proc/on_logout(mob/source)
SIGNAL_HANDLER
// Canon here because it'll be gone come the logout signal
UnregisterSignal(source.canon_client, COMSIG_CLIENT_SET_EYE)
// We do NOT unset the eye, because it's still valid even if the mob ain't logged in
/datum/visual_data/proc/eye_change(client/source)
SIGNAL_HANDLER
set_eye(source.eye)
/datum/visual_data/proc/set_eye(atom/new_eye)
var/atom/old_eye = client_eye?.resolve()
if(old_eye)
UnregisterSignal(old_eye, COMSIG_PARENT_QDELETING)
if(new_eye)
// Need to update any party's client.eyes
RegisterSignal(new_eye, COMSIG_PARENT_QDELETING, .proc/eye_deleted)
client_eye = WEAKREF(new_eye)
on_update()
/datum/visual_data/proc/eye_deleted(datum/source)
SIGNAL_HANDLER
set_eye(null)
/// Tracks but does not relay updates to someone's visual data
/// Accepts a second visual data datum to use as a source of truth for the mob's values
/datum/visual_data/tracking
/// Weakref to the visual data datum to reset our mob to
var/datum/weakref/default_to_ref
/datum/visual_data/tracking/Destroy()
var/mob/our_lad = mirroring_off_ref?.resolve()
if(our_lad)
// Reset our mob to his proper visuals
paint_onto(our_lad)
return ..()
/datum/visual_data/tracking/proc/set_truth(datum/visual_data/truth)
default_to_ref = WEAKREF(truth)
on_update()
/datum/visual_data/tracking/paint_onto(mob/paint_onto)
. = ..()
// Rebuild the passed in mob's screen, since we can't track it currently
paint_onto.hud_used?.show_hud(paint_onto.hud_used.hud_version)
/datum/visual_data/tracking/on_update()
var/mob/updated = mirroring_off_ref?.resolve()
var/datum/visual_data/mirror = default_to_ref?.resolve()
if(!updated || !mirror)
return
mirror.paint_onto(updated)
/// Tracks and updates another mob with our mob's visual data
/datum/visual_data/mirroring
/// Weakref to what mob, if any, we should mirror our changes onto
var/datum/weakref/mirror_onto_ref
/datum/visual_data/mirroring/proc/set_mirror_target(mob/target)
var/mob/old_target = mirror_onto_ref?.resolve()
if(old_target)
UnregisterSignal(old_target, COMSIG_MOB_HUD_REFRESHED)
mirror_onto_ref = WEAKREF(target)
if(target)
RegisterSignal(target, COMSIG_MOB_HUD_REFRESHED, .proc/push_ontod_hud_refreshed)
/datum/visual_data/mirroring/proc/push_ontod_hud_refreshed(mob/source)
SIGNAL_HANDLER
// Our mob refreshed its hud, so we're gonna reset it to our screen
// I hate that I don't have a signal for this, hhhh
paint_onto(source)
/datum/visual_data/mirroring/on_update()
var/mob/draw_onto = mirror_onto_ref?.resolve()
if(!draw_onto)
return
paint_onto(draw_onto)

View File

@@ -67,7 +67,7 @@
/// If this bit of weather should also draw an overlay that's uneffected by lighting onto the area
/// Taken from weather_glow.dmi
var/use_glow = TRUE
var/mutable_appearance/current_glow
var/list/offsets_to_overlays
/// The stage of the weather, from 1-4
var/stage = END_STAGE
@@ -239,26 +239,51 @@
if(END_STAGE)
using_icon_state = ""
var/mutable_appearance/glow_overlay = mutable_appearance('icons/effects/glow_weather.dmi', using_icon_state, overlay_layer, ABOVE_LIGHTING_PLANE, 100)
// Note: what we do here is effectively apply two overlays to each area, for every unique multiz layer they inhabit
// One is the base, which will be masked by lighting. the other is "glowing", and provides a nice contrast
// This method of applying one overlay per z layer has some minor downsides, in that it could lead to improperly doubled effects if some have alpha
// I prefer it to creating 2 extra plane masters however, so it's a cost I'm willing to pay
// LU
var/list/new_offsets_to_overlays = list()
for(var/V in impacted_areas)
var/area/N = V
if(current_glow)
N.overlays -= current_glow
if(stage == END_STAGE)
N.color = null
N.icon_state = using_icon_state
N.icon = 'icons/area/areas_misc.dmi'
N.layer = initial(N.layer)
N.plane = initial(N.plane)
N.set_opacity(FALSE)
else
N.layer = overlay_layer
N.plane = overlay_plane
N.icon = 'icons/effects/weather_effects.dmi'
N.icon_state = using_icon_state
N.color = weather_color
if(use_glow)
N.overlays += glow_overlay
current_glow = glow_overlay
// List of overlays this area uses
var/list/mutable_appearance/overlays = list()
// Use all possible offsets
// Yes this is a bit annoying, but it's too slow to calculate and store these, and it shouldn't (I hope) look weird
for(var/offset in 0 to SSmapping.max_plane_offset)
var/keyd_offset = offset + 1
if(length(new_offsets_to_overlays) < keyd_offset)
new_offsets_to_overlays.len = keyd_offset
var/list/mutable_appearance/existing_appearances = new_offsets_to_overlays[keyd_offset]
if(existing_appearances)
overlays += existing_appearances
continue
var/list/offset_overlays = list()
var/mutable_appearance/glow_overlay = mutable_appearance('icons/effects/glow_weather.dmi', using_icon_state, overlay_layer, N, ABOVE_LIGHTING_PLANE, 100, offset_const = offset)
glow_overlay.color = weather_color
offset_overlays += glow_overlay
if(stage != END_STAGE)
var/mutable_appearance/weather_overlay = mutable_appearance('icons/effects/weather_effects.dmi', using_icon_state, overlay_layer, plane = overlay_plane, offset_const = offset)
weather_overlay.color = weather_color
offset_overlays += weather_overlay
new_offsets_to_overlays[keyd_offset] = offset_overlays
overlays += offset_overlays
var/list/mutable_appearance/old_glows = list()
// Offset (ha) by 1 to match the key
for(var/offset in 1 to SSmapping.max_plane_offset + 1)
if(length(offsets_to_overlays) >= offset)
old_glows += offsets_to_overlays[offset]
if(length(old_glows))
N.overlays -= old_glows
if(length(overlays))
N.overlays += overlays
offsets_to_overlays = new_offsets_to_overlays

View File

@@ -65,7 +65,7 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
var/atom/target
var/image/image
var/add_ghost_version = FALSE
var/ghost_appearance
var/datum/atom_hud/alternate_appearance/basic/observers/ghost_appearance
uses_global_hud_category = FALSE
/datum/atom_hud/alternate_appearance/basic/New(key, image/I, options = AA_TARGET_SEE_APPEARANCE)
@@ -73,6 +73,7 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
transfer_overlays = options & AA_MATCH_TARGET_OVERLAYS
image = I
target = I.loc
LAZYADD(target.update_on_z, image)
if(transfer_overlays)
I.copy_overlays(target)
@@ -89,6 +90,7 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
/datum/atom_hud/alternate_appearance/basic/Destroy()
. = ..()
LAZYREMOVE(target.update_on_z, image)
QDEL_NULL(image)
target = null
if(ghost_appearance)
@@ -101,7 +103,7 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
/datum/atom_hud/alternate_appearance/basic/remove_atom_from_hud(atom/A)
. = ..()
A.hud_list -= appearance_key
LAZYREMOVE(A.hud_list, appearance_key)
A.set_hud_image_inactive(appearance_key)
if(. && !QDELETED(src))
qdel(src)

View File

@@ -5,8 +5,8 @@
requires_power = TRUE
always_unpowered = TRUE
static_lighting = FALSE
area_has_base_lighting = FALSE
base_lighting_alpha = 255
power_light = FALSE
power_equip = FALSE
power_environ = FALSE
@@ -17,11 +17,6 @@
sound_environment = SOUND_AREA_SPACE
ambient_buzz = null //Space is deafeningly quiet
/area/space/Initialize(mapload)
. = ..()
add_overlay(GLOB.fullbright_overlay)
/area/space/nearstation
icon_state = "space_near"
area_flags = UNIQUE_AREA | NO_ALERTS | AREA_USES_STARLIGHT

View File

@@ -60,6 +60,10 @@
///overlays managed by [update_overlays][/atom/proc/update_overlays] to prevent removing overlays that weren't added by the same proc. Single items are stored on their own, not in a list.
var/list/managed_overlays
/// Lazylist of all images (hopefully attached to us) to update when we change z levels
/// You will need to manage adding/removing from this yourself, but I'll do the updating for you
var/list/image/update_on_z
///Cooldown tick timer for buckle messages
var/buckle_message_cooldown = 0
///Last fingerprints to touch this atom
@@ -235,6 +239,8 @@
if(loc)
SEND_SIGNAL(loc, COMSIG_ATOM_INITIALIZED_ON, src) /// Sends a signal that the new atom `src`, has been created at `loc`
SET_PLANE_IMPLICIT(src, plane)
if(greyscale_config && greyscale_colors)
update_greyscale()

View File

@@ -102,7 +102,6 @@
/mutable_appearance/emissive_blocker/New()
. = ..()
// Need to do this here because it's overriden by the parent call
plane = EMISSIVE_PLANE
color = EM_BLOCK_COLOR
appearance_flags = EMISSIVE_APPEARANCE_FLAGS
@@ -115,6 +114,7 @@
blocker.icon_state = icon_state
blocker.dir = dir
blocker.appearance_flags |= appearance_flags
blocker.plane = GET_NEW_PLANE(EMISSIVE_PLANE, PLANE_TO_OFFSET(plane))
// Ok so this is really cursed, but I want to set with this blocker cheaply while
// Still allowing it to be removed from the overlays list later
// So I'm gonna flatten it, then insert the flattened overlay into overlays AND the managed overlays list, directly
@@ -139,7 +139,6 @@
managed_overlays = list(managed_overlays, em_block)
else
managed_overlays = em_block
if(opacity)
AddElement(/datum/element/light_blocking)
switch(light_system)
@@ -205,7 +204,7 @@
if(!blocks_emissive)
return
else if (blocks_emissive == EMISSIVE_BLOCK_GENERIC)
var/mutable_appearance/gen_emissive_blocker = emissive_blocker(icon, icon_state, alpha = src.alpha, appearance_flags = src.appearance_flags)
var/mutable_appearance/gen_emissive_blocker = emissive_blocker(icon, icon_state, src, alpha = src.alpha, appearance_flags = src.appearance_flags)
gen_emissive_blocker.dir = dir
return gen_emissive_blocker
else if(blocks_emissive == EMISSIVE_BLOCK_UNIQUE)
@@ -717,7 +716,8 @@
var/turf/new_turf = get_turf(src)
if (old_turf?.z != new_turf?.z)
on_changed_z_level(old_turf, new_turf)
var/same_z_layer = (GET_TURF_PLANE_OFFSET(old_turf) == GET_TURF_PLANE_OFFSET(new_turf))
on_changed_z_level(old_turf, new_turf, same_z_layer)
if(HAS_SPATIAL_GRID_CONTENTS(src))
if(old_turf && new_turf && (old_turf.z != new_turf.z \
@@ -1034,17 +1034,31 @@
* Called when a movable changes z-levels.
*
* Arguments:
* * old_z - The previous z-level they were on before.
* * notify_contents - Whether or not to notify the movable's contents that their z-level has changed.
* * old_turf - The previous turf they were on before.
* * new_turf - The turf they have now entered.
* * same_z_layer - If their old and new z levels are on the same level of plane offsets or not
* * notify_contents - Whether or not to notify the movable's contents that their z-level has changed. NOTE, IF YOU SET THIS, YOU NEED TO MANUALLY SET PLANE OF THE CONTENTS LATER
*/
/atom/movable/proc/on_changed_z_level(turf/old_turf, turf/new_turf, notify_contents = TRUE)
SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_turf, new_turf)
/atom/movable/proc/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents = TRUE)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_turf, new_turf, same_z_layer)
// If our turfs are on different z "layers", recalc our planes
if(!same_z_layer && !QDELETED(src))
SET_PLANE(src, PLANE_TO_TRUE(src.plane), new_turf)
// a TON of overlays use planes, and thus require offsets
// so we do this. sucks to suck
update_appearance()
// I so much wish this could be somewhere else. alas, no.
for(var/image/update in update_on_z)
SET_PLANE(update, PLANE_TO_TRUE(update.plane), new_turf)
if(!notify_contents)
return
for (var/atom/movable/content as anything in src) // Notify contents of Z-transition.
content.on_changed_z_level(old_turf, new_turf)
content.on_changed_z_level(old_turf, new_turf, same_z_layer)
/**
* Called whenever an object moves and by mobs when they attempt to move themselves through space

View File

@@ -488,7 +488,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
GLOB.cameranet.removeCamera(src)
if (isarea(myarea))
LAZYREMOVE(myarea.cameras, src)
GLOB.cameranet.updateChunk(x, y, z)
// We are not guarenteed that the camera will be on a turf. account for that
var/turf/our_turf = get_turf(src)
GLOB.cameranet.updateChunk(our_turf.x, our_turf.y, our_turf.z)
var/change_msg = "deactivates"
if(status)
change_msg = "reactivates"
@@ -532,10 +534,32 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
/obj/machinery/camera/proc/can_see()
var/list/see = null
var/turf/pos = get_turf(src)
var/check_lower = pos != get_lowest_turf(pos)
var/check_higher = pos != get_highest_turf(pos)
if(isXRay())
see = range(view_range, pos)
else
see = get_hear(view_range, pos)
if(check_lower || check_higher)
// Haha datum var access KILL ME
var/datum/controller/subsystem/mapping/local_mapping = SSmapping
for(var/turf/seen in see)
if(check_lower)
var/turf/visible = seen
while(visible && istransparentturf(visible))
var/turf/below = local_mapping.get_turf_below(visible)
for(var/turf/adjacent in range(1, below))
see += adjacent
see += adjacent.contents
visible = below
if(check_higher)
var/turf/above = local_mapping.get_turf_above(seen)
while(above && istransparentturf(above))
for(var/turf/adjacent in range(1, above))
see += adjacent
see += adjacent.contents
above = local_mapping.get_turf_above(above)
return see
/obj/machinery/camera/proc/Togglelight(on=0)
@@ -553,11 +577,12 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2)
/obj/machinery/camera/update_remote_sight(mob/living/user)
user.see_invisible = SEE_INVISIBLE_LIVING //can't see ghosts through cameras
user.set_invis_see(SEE_INVISIBLE_LIVING) //can't see ghosts through cameras
if(isXRay())
user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
user.see_in_dark = max(user.see_in_dark, 8)
user.add_sight(SEE_TURFS|SEE_MOBS|SEE_OBJS)
user.set_see_in_dark(max(user.see_in_dark, 8))
else
user.clear_sight(SEE_TURFS|SEE_MOBS|SEE_OBJS)
user.sight = 0
user.see_in_dark = 2
user.set_see_in_dark(2)
return 1

View File

@@ -44,7 +44,7 @@
return
. += mutable_appearance(icon, icon_screen)
. += emissive_appearance(icon, icon_screen)
. += emissive_appearance(icon, icon_screen, src)
*/
/obj/machinery/computer/power_change()

View File

@@ -15,10 +15,8 @@
var/list/concurrent_users = list()
// Stuff needed to render the map
var/map_name
var/atom/movable/screen/map_view/cam_screen
/// All the plane masters that need to be applied.
var/list/cam_plane_masters
var/atom/movable/screen/background/cam_background
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE|INTERACT_MACHINE_REQUIRES_SIGHT
@@ -28,33 +26,20 @@
// Map name has to start and end with an A-Z character,
// and definitely NOT with a square bracket or even a number.
// I wasted 6 hours on this. :agony:
map_name = "camera_console_[REF(src)]_map"
var/map_name = "camera_console_[REF(src)]_map"
// Convert networks to lowercase
for(var/i in network)
network -= i
network += lowertext(i)
// Initialize map objects
cam_screen = new
cam_screen.name = "screen"
cam_screen.assigned_map = map_name
cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1"
cam_plane_masters = list()
for(var/plane in subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/blackness)
var/atom/movable/screen/plane_master/instance = new plane()
if(instance.blend_mode_override)
instance.blend_mode = instance.blend_mode_override
instance.assigned_map = map_name
instance.del_on_map_removal = FALSE
instance.screen_loc = "[map_name]:CENTER"
cam_plane_masters += instance
cam_screen.generate_view(map_name)
cam_background = new
cam_background.assigned_map = map_name
cam_background.del_on_map_removal = FALSE
/obj/machinery/computer/security/Destroy()
QDEL_NULL(cam_screen)
QDEL_LIST(cam_plane_masters)
QDEL_NULL(cam_background)
return ..()
@@ -83,9 +68,7 @@
playsound(src, 'sound/machines/terminal_on.ogg', 25, FALSE)
use_power(active_power_usage)
// Register map objects
user.client.register_map_obj(cam_screen)
for(var/plane in cam_plane_masters)
user.client.register_map_obj(plane)
cam_screen.display_to(user)
user.client.register_map_obj(cam_background)
// Open UI
ui = new(user, src, "CameraConsole", name)
@@ -110,7 +93,7 @@
/obj/machinery/computer/security/ui_static_data()
var/list/data = list()
data["mapRef"] = map_name
data["mapRef"] = cam_screen.assigned_map
var/list/cameras = get_available_cameras()
data["cameras"] = list()
for(var/i in cameras)
@@ -181,7 +164,7 @@
// Living creature or not, we remove you anyway.
concurrent_users -= user_ref
// Unregister map objects
user.client.clear_map(map_name)
cam_screen.hide_from(user)
// Turn off the console
if(length(concurrent_users) == 0 && is_living)
active_camera = null

View File

@@ -92,6 +92,9 @@
user.remote_control = null
current_user = null
user.unset_machine()
var/atom/movable/screen/plane_master/plane_static = user.hud_used?.get_plane_master(CAMERA_STATIC_PLANE)
if(plane_static)
plane_static.hide_plane(user)
playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
/obj/machinery/computer/camera_advanced/check_eye(mob/user)
@@ -174,8 +177,12 @@
user.remote_control = eyeobj
user.reset_perspective(eyeobj)
eyeobj.setLoc(eyeobj.loc)
if(should_supress_view_changes )
if(should_supress_view_changes)
user.client.view_size.supress()
// Who passes control like this god I hate static code
var/atom/movable/screen/plane_master/plane_static = user.hud_used?.get_plane_master(CAMERA_STATIC_PLANE)
if(plane_static)
plane_static.unhide_plane(user)
/mob/camera/ai_eye/remote
name = "Inactive Camera Eye"
@@ -190,9 +197,9 @@
var/image/user_image = null
/mob/camera/ai_eye/remote/update_remote_sight(mob/living/user)
user.see_invisible = SEE_INVISIBLE_LIVING //can't see ghosts through cameras
user.sight = SEE_TURFS | SEE_BLACKNESS
user.see_in_dark = 2
user.set_invis_see(SEE_INVISIBLE_LIVING) //can't see ghosts through cameras
user.set_sight(SEE_TURFS)
user.set_see_in_dark(2)
return TRUE
/mob/camera/ai_eye/remote/Destroy()
@@ -224,7 +231,7 @@
if(eye_user.client)
eye_user.client.images -= user_image
user_image = image(icon,loc,icon_state, FLY_LAYER)
user_image.plane = ABOVE_GAME_PLANE
SET_PLANE(user_image, ABOVE_GAME_PLANE, destination)
eye_user.client.images += user_image
/mob/camera/ai_eye/remote/relaymove(mob/living/user, direction)

View File

@@ -22,7 +22,7 @@
/// Someone, for the love of god, profile this. Is there a reason to cache mutable_appearance
/// if so, why are we JUST doing the airlocks when we can put this in mutable_appearance.dm for
/// everything
/proc/get_airlock_overlay(icon_state, icon_file, em_block)
/proc/get_airlock_overlay(icon_state, icon_file, atom/offset_spokesman, em_block)
var/static/list/airlock_overlays = list()
var/base_icon_key = "[icon_state][REF(icon_file)]"
@@ -31,10 +31,12 @@
if(isnull(em_block))
return
var/em_block_key = "[base_icon_key][em_block]"
var/turf/our_turf = get_turf(offset_spokesman)
var/em_block_key = "[base_icon_key][em_block][GET_TURF_PLANE_OFFSET(our_turf)]"
var/mutable_appearance/em_blocker = airlock_overlays[em_block_key]
if(!em_blocker)
em_blocker = airlock_overlays[em_block_key] = mutable_appearance(icon_file, icon_state, plane = EMISSIVE_PLANE, appearance_flags = EMISSIVE_APPEARANCE_FLAGS)
em_blocker = airlock_overlays[em_block_key] = mutable_appearance(icon_file, icon_state, offset_spokesman = offset_spokesman, plane = EMISSIVE_PLANE, appearance_flags = EMISSIVE_APPEARANCE_FLAGS)
em_blocker.color = em_block ? GLOB.em_block_color : GLOB.emissive_color
return list(., em_blocker)
@@ -150,23 +152,10 @@
rad_insulation = RAD_MEDIUM_INSULATION
/obj/machinery/door/airlock/Initialize(mapload)
//SKYRAT EDIT ADDITION BEGIN - Door aesthetic overhaul
vis_overlay1 = new()
vis_overlay1.icon = overlays_file
vis_overlay2 = new()
vis_overlay2.icon = overlays_file
vis_overlay2.layer = layer
vis_overlay2.plane = 1
vis_contents += vis_overlay1
vis_contents += vis_overlay2
//SKYRAT EDIT END
. = ..()
//SKYRAT EDIT ADDITION BEGIN - Door aesthetic overhaul
if(multi_tile)
SetBounds()
if(multi_tile)
vis_overlay1.dir = src.dir
vis_overlay2.dir = src.dir
update_overlays()
//SKYRAT EDIT END
init_network_id(NETWORK_DOOR_AIRLOCKS)
@@ -519,45 +508,44 @@
frame_state = AIRLOCK_FRAME_OPENING
light_state = AIRLOCK_LIGHT_OPENING
. += get_airlock_overlay(frame_state, icon, em_block = TRUE)
. += get_airlock_overlay(frame_state, icon, src, em_block = TRUE)
if(airlock_material)
. += get_airlock_overlay("[airlock_material]_[frame_state]", overlays_file, em_block = TRUE)
. += get_airlock_overlay("[airlock_material]_[frame_state]", overlays_file, src, em_block = TRUE)
else
. += get_airlock_overlay("fill_[frame_state]", icon, em_block = TRUE)
. += get_airlock_overlay("fill_[frame_state]", icon, src, em_block = TRUE)
if(lights && hasPower())
. += get_airlock_overlay("lights_[light_state]", overlays_file, em_block = FALSE)
. += get_airlock_overlay("lights_[light_state]", overlays_file, src, em_block = FALSE)
if(panel_open)
. += get_airlock_overlay("panel_[frame_state][security_level ? "_protected" : null]", overlays_file, em_block = TRUE)
. += get_airlock_overlay("panel_[frame_state][security_level ? "_protected" : null]", overlays_file, src, em_block = TRUE)
if(frame_state == AIRLOCK_FRAME_CLOSED && welded)
. += get_airlock_overlay("welded", overlays_file, em_block = TRUE)
. += get_airlock_overlay("welded", overlays_file, src, em_block = TRUE)
if(airlock_state == AIRLOCK_EMAG)
. += get_airlock_overlay("sparks", overlays_file, em_block = FALSE)
. += get_airlock_overlay("sparks", overlays_file, src, em_block = FALSE)
if(hasPower())
if(frame_state == AIRLOCK_FRAME_CLOSED)
if(atom_integrity < integrity_failure * max_integrity)
. += get_airlock_overlay("sparks_broken", overlays_file, em_block = FALSE)
. += get_airlock_overlay("sparks_broken", overlays_file, src, em_block = FALSE)
else if(atom_integrity < (0.75 * max_integrity))
. += get_airlock_overlay("sparks_damaged", overlays_file, em_block = FALSE)
. += get_airlock_overlay("sparks_damaged", overlays_file, src, em_block = FALSE)
else if(frame_state == AIRLOCK_FRAME_OPEN)
if(atom_integrity < (0.75 * max_integrity))
. += get_airlock_overlay("sparks_open", overlays_file, em_block = FALSE)
. += get_airlock_overlay("sparks_open", overlays_file, src, em_block = FALSE)
if(note)
. += get_airlock_overlay(get_note_state(frame_state), note_overlay_file, em_block = TRUE)
. += get_airlock_overlay(get_note_state(frame_state), note_overlay_file, src, em_block = TRUE)
if(frame_state == AIRLOCK_FRAME_CLOSED && seal)
. += get_airlock_overlay("sealed", overlays_file, em_block = TRUE)
. += get_airlock_overlay("sealed", overlays_file, src, em_block = TRUE)
if(hasPower() && unres_sides)
for(var/heading in list(NORTH,SOUTH,EAST,WEST))
if(!(unres_sides & heading))
continue
var/image/floorlight = image(icon='icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_[heading]")
floorlight.plane = ABOVE_LIGHTING_PLANE
var/mutable_appearance/floorlight = mutable_appearance('icons/obj/doors/airlocks/station/overlays.dmi', "unres_[heading]", FLOAT_LAYER, src, ABOVE_LIGHTING_PLANE)
switch (heading)
if (NORTH)
floorlight.pixel_x = 0

View File

@@ -601,7 +601,7 @@
hazards.pixel_x = light_xoffset
hazards.pixel_y = light_yoffset
. += hazards
hazards = emissive_appearance(icon, "[(obj_flags & EMAGGED) ? "firelock_alarm_type_emag" : alarm_type]", alpha = src.alpha)
hazards = emissive_appearance(icon, "[(obj_flags & EMAGGED) ? "firelock_alarm_type_emag" : alarm_type]", src, alpha = src.alpha)
hazards.pixel_x = light_xoffset
hazards.pixel_y = light_yoffset
. += hazards

View File

@@ -54,7 +54,7 @@
/obj/machinery/ecto_sniffer/update_overlays()
. = ..()
if(is_operational && on)
. += emissive_appearance(icon, "[initial(icon_state)]-light-mask", alpha = src.alpha)
. += emissive_appearance(icon, "[initial(icon_state)]-light-mask", src, alpha = src.alpha)
/obj/machinery/ecto_sniffer/wrench_act(mob/living/user, obj/item/tool)
tool.play_tool_sound(src, 15)

View File

@@ -129,25 +129,25 @@
. += mutable_appearance(icon, "fire_overlay")
if(is_station_level(z))
. += mutable_appearance(icon, "fire_[SSsecurity_level.get_current_level_as_number()]")
. += emissive_appearance(icon, "fire_[SSsecurity_level.get_current_level_as_number()]", alpha = src.alpha)
. += emissive_appearance(icon, "fire_[SSsecurity_level.get_current_level_as_number()]", src, alpha = src.alpha)
else
. += mutable_appearance(icon, "fire_[SEC_LEVEL_GREEN]")
. += emissive_appearance(icon, "fire_[SEC_LEVEL_GREEN]", alpha = src.alpha)
. += emissive_appearance(icon, "fire_[SEC_LEVEL_GREEN]", src, alpha = src.alpha)
if(!(my_area?.fire || LAZYLEN(my_area?.active_firelocks)))
if(my_area?.fire_detect) //If this is false, leave the green light missing. A good hint to anyone paying attention.
. += mutable_appearance(icon, "fire_off")
. += emissive_appearance(icon, "fire_off", alpha = src.alpha)
. += emissive_appearance(icon, "fire_off", src, alpha = src.alpha)
else if(obj_flags & EMAGGED)
. += mutable_appearance(icon, "fire_emagged")
. += emissive_appearance(icon, "fire_emagged", alpha = src.alpha)
. += emissive_appearance(icon, "fire_emagged", src, alpha = src.alpha)
else
. += mutable_appearance(icon, "fire_on")
. += emissive_appearance(icon, "fire_on", alpha = src.alpha)
. += emissive_appearance(icon, "fire_on", src, alpha = src.alpha)
if(!panel_open && my_area?.fire_detect && my_area?.fire) //It just looks horrible with the panel open
. += mutable_appearance(icon, "fire_detected")
. += emissive_appearance(icon, "fire_detected", alpha = src.alpha) //Pain
. += emissive_appearance(icon, "fire_detected", src, alpha = src.alpha) //Pain
/obj/machinery/firealarm/emp_act(severity)
. = ..()

View File

@@ -90,8 +90,8 @@ Possible to do for anyone motivated enough:
/obj/machinery/holopad/Initialize(mapload)
. = ..()
/// We set the plane on mapload such that we can see the holopad render over atmospherics pipe and cabling in a map editor (without initialization), but so it gets that "inset" look in the floor in-game.
plane = FLOOR_PLANE
update_overlays()
SET_PLANE_IMPLICIT(src, FLOOR_PLANE)
update_appearance()
/obj/machinery/holopad/secure
name = "secure holopad"
@@ -554,7 +554,7 @@ Possible to do for anyone motivated enough:
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
Hologram.layer = FLY_LAYER //Above all the other objects/mobs. Or the vast majority of them.
Hologram.plane = ABOVE_GAME_PLANE
SET_PLANE_EXPLICIT(Hologram, ABOVE_GAME_PLANE, src)
Hologram.set_anchored(TRUE)//So space wind cannot drag it.
Hologram.name = "[user.name] (Hologram)"//If someone decides to right click.
Hologram.set_light(2) //hologram lighting
@@ -704,6 +704,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
transfered = TRUE
//All is good.
holo.abstract_move(new_turf)
SET_PLANE(holo, ABOVE_GAME_PLANE, new_turf)
if(!transfered)
update_holoray(user,new_turf)
return TRUE
@@ -744,7 +745,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
holder.selected_language = record.language
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
Hologram.plane = ABOVE_GAME_PLANE
SET_PLANE_EXPLICIT(Hologram, ABOVE_GAME_PLANE, src)
Hologram.set_anchored(TRUE)//So space wind cannot drag it.
Hologram.name = "[record.caller_name] (Hologram)"//If someone decides to right click.
Hologram.set_light(2) //hologram lighting

View File

@@ -37,13 +37,17 @@
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_atom_to_hud(src)
update_hud()
/obj/machinery/launchpad/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
if(same_z_layer && !QDELETED(src))
update_hud()
return ..()
/obj/machinery/launchpad/proc/update_hud()
var/image/holder = hud_list[DIAG_LAUNCHPAD_HUD]
var/mutable_appearance/MA = new /mutable_appearance()
MA.icon = 'icons/effects/effects.dmi'
MA.icon_state = "launchpad_target"
MA.layer = ABOVE_OPEN_TURF_LAYER
MA.plane = GAME_PLANE
holder.appearance = MA
var/mutable_appearance/target = mutable_appearance('icons/effects/effects.dmi', "launchpad_target", ABOVE_OPEN_TURF_LAYER, src, GAME_PLANE)
holder.appearance = target
update_indicator()

View File

@@ -52,7 +52,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/light_switch, 26)
. = ..()
if(machine_stat & NOPOWER)
return ..()
. += emissive_appearance(icon, "[base_icon_state]-emissive[area.lightswitch ? "-on" : "-off"]", alpha = src.alpha)
. += emissive_appearance(icon, "[base_icon_state]-emissive[area.lightswitch ? "-on" : "-off"]", src, alpha = src.alpha)
/obj/machinery/light_switch/examine(mob/user)
. = ..()

View File

@@ -34,12 +34,12 @@
glob_lists_deregister()
return ..()
/obj/machinery/navbeacon/on_changed_z_level(turf/old_turf, turf/new_turf)
/obj/machinery/navbeacon/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
if (GLOB.navbeacons["[old_turf?.z]"])
GLOB.navbeacons["[old_turf?.z]"] -= src
if (GLOB.navbeacons["[new_turf?.z]"])
GLOB.navbeacons["[new_turf?.z]"] += src
..()
return ..()
// set the transponder codes assoc list from codes_txt
/obj/machinery/navbeacon/proc/set_codes()

View File

@@ -85,14 +85,15 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
/obj/machinery/newscaster/update_overlays()
. = ..()
if(!(machine_stat & (NOPOWER|BROKEN)))
var/state = "[base_icon_state]_[GLOB.news_network.wanted_issue.active ? "wanted" : "normal"]"
. += mutable_appearance(icon, state)
. += emissive_appearance(icon, state, alpha = src.alpha)
. += emissive_appearance(icon, state, src, alpha = src.alpha)
if(GLOB.news_network.wanted_issue.active && alert)
. += mutable_appearance(icon, "[base_icon_state]_alert")
. += emissive_appearance(icon, "[base_icon_state]_alert", alpha = src.alpha)
. += emissive_appearance(icon, "[base_icon_state]_alert", src, alpha = src.alpha,)
var/hp_percent = atom_integrity * 100 / max_integrity
switch(hp_percent)
@@ -100,13 +101,13 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
return
if(50 to 75)
. += "crack1"
. += emissive_blocker(icon, "crack1", alpha = src.alpha)
. += emissive_blocker(icon, "crack1", src, alpha = src.alpha)
if(25 to 50)
. += "crack2"
. += emissive_blocker(icon, "crack2", alpha = src.alpha)
. += emissive_blocker(icon, "crack2", src, alpha = src.alpha)
else
. += "crack3"
. += emissive_blocker(icon, "crack3", alpha = src.alpha)
. += emissive_blocker(icon, "crack3", src, alpha = src.alpha)
/obj/machinery/newscaster/ui_interact(mob/user, datum/tgui/ui)
. = ..()

View File

@@ -204,13 +204,13 @@
if(!charging)
. += mutable_appearance(icon, "[base_icon_state]-empty", alpha = src.alpha)
. += emissive_appearance(icon, "[base_icon_state]-empty", alpha = src.alpha)
. += emissive_appearance(icon, "[base_icon_state]-empty", src, alpha = src.alpha)
return
if(using_power)
. += mutable_appearance(icon, "[base_icon_state]-charging", alpha = src.alpha)
. += emissive_appearance(icon, "[base_icon_state]-charging", alpha = src.alpha)
. += emissive_appearance(icon, "[base_icon_state]-charging", src, alpha = src.alpha)
return
. += mutable_appearance(icon, "[base_icon_state]-full", alpha = src.alpha)
. += emissive_appearance(icon, "[base_icon_state]-full", alpha = src.alpha)
. += emissive_appearance(icon, "[base_icon_state]-full", src, alpha = src.alpha)

View File

@@ -109,7 +109,7 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments)
screen_state = "[base_icon_state]0"
. += mutable_appearance(icon, screen_state)
. += emissive_appearance(icon, screen_state, alpha = src.alpha)
. += emissive_appearance(icon, screen_state, src, alpha = src.alpha)
/obj/machinery/requests_console/Initialize(mapload)
. = ..()

View File

@@ -88,6 +88,11 @@
var/easing_direction = _running ? EASE_OUT : EASE_IN
animate(mattress_on, alpha = new_alpha, time = 50, easing = CUBIC_EASING|easing_direction)
/obj/machinery/stasis/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
if(same_z_layer)
return ..()
SET_PLANE(mattress_on, PLANE_TO_TRUE(mattress_on.plane), new_turf)
return ..()
/obj/machinery/stasis/atom_break(damage_flag)
. = ..()

View File

@@ -174,7 +174,7 @@
if(message1 == "" && message2 == "")
return
. += emissive_appearance(icon, "outline", alpha = src.alpha)
. += emissive_appearance(icon, "outline", src, alpha = src.alpha)
// Timed process - performs nothing in the base class
/obj/machinery/status_display/process()

View File

@@ -131,6 +131,12 @@
warp = null
return ..()
/obj/effect/anomaly/grav/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
. = ..()
if(same_z_layer)
return
SET_PLANE(warp, PLANE_TO_TRUE(warp.plane), new_turf)
/obj/effect/anomaly/grav/anomalyEffect(delta_time)
..()
boing = 1
@@ -219,7 +225,7 @@
/obj/effect/anomaly/flux/update_overlays()
. = ..()
. += emissive_appearance(icon, icon_state, alpha=src.alpha)
. += emissive_appearance(icon, icon_state, src, alpha=src.alpha)
/obj/effect/anomaly/flux/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER

View File

@@ -72,6 +72,7 @@
/obj/effect/decal/cleanable/blood/splatter/over_window // special layer/plane set to appear on windows
layer = ABOVE_WINDOW_LAYER
plane = GAME_PLANE
vis_flags = VIS_INHERIT_PLANE
turf_loc_check = FALSE
alpha = 180

View File

@@ -321,6 +321,8 @@
/obj/effect/decal/cleanable/ants/update_icon_state()
if(istype(src, /obj/effect/decal/cleanable/ants/fire)) //i fucking hate this but you're forced to call parent in update_icon_state()
return ..()
if(!(flags_1 & INITIALIZED_1))
return ..()
var/datum/component/caltrop/caltrop_comp = GetComponent(/datum/component/caltrop)
if(!caltrop_comp)
@@ -339,7 +341,7 @@
/obj/effect/decal/cleanable/ants/update_overlays()
. = ..()
. += emissive_appearance(icon, "[icon_state]_light", alpha = src.alpha)
. += emissive_appearance(icon, "[icon_state]_light", src, alpha = src.alpha)
/obj/effect/decal/cleanable/ants/fire_act(exposed_temperature, exposed_volume)
var/obj/effect/decal/cleanable/ants/fire/fire_ants = new(loc)

View File

@@ -6,7 +6,6 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
move_resist = INFINITY
obj_flags = NONE
vis_flags = VIS_INHERIT_PLANE
blocks_emissive = EMISSIVE_BLOCK_GENERIC
/obj/effect/attackby(obj/item/weapon, mob/user, params)

View File

@@ -65,8 +65,8 @@
appearance_flags = RESET_TRANSFORM | TILE_BOUND
invisibility = INVISIBILITY_ABSTRACT
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
plane = ATMOS_GROUP_PLANE
vis_flags = VIS_INHERIT_PLANE
plane = HIGH_GAME_PLANE
/// Door overlay for animating closets
/obj/effect/overlay/closet_door

View File

@@ -4,6 +4,7 @@
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = ABOVE_ALL_MOB_LAYER
vis_flags = VIS_INHERIT_PLANE
///typepath of the last location we're in, if it's different when moved then we need to update vis contents
var/last_attached_location_type
///the main item we're attached to at the moment, particle holders hold particles for something

View File

@@ -21,7 +21,7 @@
if(SOUTH)
target_pixel_y = -16
layer = ABOVE_MOB_LAYER
plane = GAME_PLANE_UPPER
SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
if(EAST)
target_pixel_x = 16
if(WEST)
@@ -36,12 +36,12 @@
target_pixel_x = 16
target_pixel_y = -16
layer = ABOVE_MOB_LAYER
plane = GAME_PLANE_UPPER
SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
if(SOUTHWEST)
target_pixel_x = -16
target_pixel_y = -16
layer = ABOVE_MOB_LAYER
plane = GAME_PLANE_UPPER
SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
animate(src, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = duration)
/obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter
@@ -564,6 +564,7 @@
return INITIALIZE_HINT_QDEL
modsuit_image = image(icon = icon, loc = looker.loc, icon_state = real_icon_state, layer = ABOVE_ALL_MOB_LAYER, pixel_x = ((creature.x - looker.x) * 32), pixel_y = ((creature.y - looker.y) * 32))
modsuit_image.plane = ABOVE_LIGHTING_PLANE
SET_PLANE_EXPLICIT(modsuit_image, ABOVE_LIGHTING_PLANE, creature)
mod_man = WEAKREF(looker)
add_mind(looker)

View File

@@ -39,7 +39,7 @@
/obj/effect/projectile/proc/apply_vars(angle_override, p_x = 0, p_y = 0, color_override, scaling = 1, new_loc, increment = 0)
var/mutable_appearance/look = new(src)
look.plane = plane
SET_PLANE_EXPLICIT(look, plane, new_loc)
look.pixel_x = p_x
look.pixel_y = p_y
if(color_override)

View File

@@ -1129,18 +1129,22 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
return 0
/obj/item/doMove(atom/destination)
if (ismob(loc))
var/mob/M = loc
var/hand_index = M.get_held_index_of_item(src)
if(hand_index)
M.held_items[hand_index] = null
M.update_held_items()
if(M.client)
M.client.screen -= src
layer = initial(layer)
plane = initial(plane)
appearance_flags &= ~NO_CLIENT_COLOR
dropped(M, FALSE)
if (!ismob(loc))
return ..()
var/mob/M = loc
var/hand_index = M.get_held_index_of_item(src)
if(!hand_index)
return ..()
M.held_items[hand_index] = null
M.update_held_items()
if(M.client)
M.client.screen -= src
layer = initial(layer)
SET_PLANE_IMPLICIT(src, initial(plane))
appearance_flags &= ~NO_CLIENT_COLOR
dropped(M, FALSE)
return ..()
/obj/item/proc/embedded(atom/embedded_target, obj/item/bodypart/part)
@@ -1452,7 +1456,7 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
if(!istype(loc, /turf))
return
var/image/pickup_animation = image(icon = src, loc = loc, layer = layer + 0.1)
pickup_animation.plane = GAME_PLANE
SET_PLANE(pickup_animation, GAME_PLANE, loc)
pickup_animation.transform.Scale(0.75)
pickup_animation.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA

View File

@@ -148,9 +148,7 @@
return html
/obj/item/camera_bug/proc/get_seens()
if(current?.can_use())
var/list/seen = current.can_see()
return seen
return current?.can_see()
/obj/item/camera_bug/proc/camera_report()
// this should only be called if current exists

View File

@@ -62,10 +62,10 @@
return
playsound(get_turf(src), 'sound/weapons/flash.ogg', 100, TRUE, -6)
to_chat(user, span_notice("Scanned [target]."))
var/obj/temp = new/obj()
var/obj/temp = new /obj()
temp.appearance = target.appearance
temp.layer = initial(target.layer) // scanning things in your inventory
temp.plane = initial(target.plane)
SET_PLANE_EXPLICIT(temp, initial(plane), src)
saved_appearance = temp.appearance
/obj/item/chameleon/proc/check_sprite(atom/target)

View File

@@ -5,20 +5,23 @@
var/obj/item/clothing/accessory/spy_bug/linked_bug
/obj/item/clothing/glasses/sunglasses/spy/proc/show_to_user(mob/user)//this is the meat of it. most of the map_popup usage is in this.
if(!user)
return
if(!user.client)
var/client/cool_guy = user?.client
if(!cool_guy)
return
if(!linked_bug)
user.audible_message(span_warning("[src] lets off a shrill beep!"))
if(user.client.screen_maps["spypopup_map"]) //alright, the popup this object uses is already IN use, so the window is open. no point in doing any other work here, so we're good.
if(cool_guy.screen_maps["spypopup_map"]) //alright, the popup this object uses is already IN use, so the window is open. no point in doing any other work here, so we're good.
return
user.client.setup_popup("spypopup", 3, 3, 2)
user.client.register_map_obj(linked_bug.cam_screen)
for(var/plane in linked_bug.cam_plane_masters)
user.client.register_map_obj(plane)
cool_guy.setup_popup("spypopup", 3, 3, 2, "S.P.Y")
linked_bug.cam_screen.display_to(user)
RegisterSignal(cool_guy, COMSIG_POPUP_CLEARED, .proc/on_screen_clear)
linked_bug.update_view()
/obj/item/clothing/glasses/sunglasses/spy/proc/on_screen_clear(client/source, window)
SIGNAL_HANDLER
linked_bug.cam_screen.hide_from(source.mob)
/obj/item/clothing/glasses/sunglasses/spy/equipped(mob/user, slot)
. = ..()
if(!(slot & ITEM_SLOT_EYES))
@@ -51,7 +54,6 @@
desc = "An advanced piece of espionage equipment in the shape of a pocket protector. It has a built in 360 degree camera for all your \"admirable\" needs. Microphone not included."
var/obj/item/clothing/glasses/sunglasses/spy/linked_glasses
var/atom/movable/screen/map_view/cam_screen
var/list/cam_plane_masters
// Ranges higher than one can be used to see through walls.
var/cam_range = 1
var/datum/movement_detector/tracker
@@ -59,38 +61,19 @@
/obj/item/clothing/accessory/spy_bug/Initialize(mapload)
. = ..()
tracker = new /datum/movement_detector(src, CALLBACK(src, .proc/update_view))
cam_screen = new
cam_screen.name = "screen"
cam_screen.assigned_map = "spypopup_map"
cam_screen.del_on_map_removal = FALSE
cam_screen.set_position(1, 1)
// We need to add planesmasters to the popup, otherwise
// blending fucks up massively. Any planesmaster on the main screen does
// NOT apply to map popups. If there's ever a way to make planesmasters
// omnipresent, then this wouldn't be needed.
cam_plane_masters = list()
for(var/plane in subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/blackness)
var/atom/movable/screen/plane_master/instance = new plane()
if(instance.blend_mode_override)
instance.blend_mode = instance.blend_mode_override
instance.assigned_map = "spypopup_map"
instance.del_on_map_removal = FALSE
instance.screen_loc = "spypopup_map:CENTER"
cam_plane_masters += instance
cam_screen.generate_view("spypopup_map")
/obj/item/clothing/accessory/spy_bug/Destroy()
if(linked_glasses)
linked_glasses.linked_bug = null
QDEL_NULL(cam_screen)
QDEL_LIST(cam_plane_masters)
QDEL_NULL(tracker)
. = ..()
/obj/item/clothing/accessory/spy_bug/proc/update_view()//this doesn't do anything too crazy, just updates the vis_contents of its screen obj
cam_screen.vis_contents.Cut()
for(var/turf/visible_turf in view(1,get_turf(src)))//fuck you usr
for(var/turf/visible_turf in view(cam_range, get_turf(src)))//fuck you usr
cam_screen.vis_contents += visible_turf
//it needs to be linked, hence a kit.

View File

@@ -26,7 +26,7 @@
name = fried.name //We'll determine the other stuff when it's actually removed
appearance = fried.appearance
layer = initial(layer)
plane = initial(plane)
SET_PLANE_IMPLICIT(src, initial(plane))
lefthand_file = fried.lefthand_file
righthand_file = fried.righthand_file
inhand_icon_state = fried.inhand_icon_state

View File

@@ -205,7 +205,7 @@
if(!light_list[i])
continue
var/mutable_appearance/lit_image = mutable_appearance('icons/obj/puzzle_small.dmi', "light_lit")
var/mutable_appearance/emissive_image = emissive_appearance('icons/obj/puzzle_small.dmi', "light_lit")
var/mutable_appearance/emissive_image = emissive_appearance('icons/obj/puzzle_small.dmi', "light_lit", src)
lit_image.pixel_x = 8 * ((i % 3 || 3 ) - 1)
lit_image.pixel_y = -8 * (ROUND_UP(i / 3) - 1)
emissive_image.pixel_x = lit_image.pixel_x

View File

@@ -374,7 +374,7 @@
var/mutable_appearance/neon_overlay = mutable_appearance(neon_icon || icon, neon_icon_state || icon_state, alpha = alpha)
neon_overlay.color = neon_color
. += neon_overlay
. += emissive_appearance(neon_icon || icon, neon_icon_state || icon_state, alpha = emissive_alpha)
. += emissive_appearance(neon_icon || icon, neon_icon_state || icon_state, src, alpha = emissive_alpha)
/obj/item/stack/tile/carpet/neon/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
. = ..()
@@ -384,7 +384,7 @@
var/mutable_appearance/neon_overlay = mutable_appearance(icon_file, neon_inhand_icon_state)
neon_overlay.color = neon_color
. += neon_overlay
. += emissive_appearance(icon_file, neon_inhand_icon_state, alpha = emissive_alpha)
. += emissive_appearance(icon_file, neon_inhand_icon_state, src, alpha = emissive_alpha)
/obj/item/stack/tile/carpet/neon/simple
name = "simple neon carpet"
@@ -1158,11 +1158,11 @@
/obj/item/stack/tile/emissive_test/update_overlays()
. = ..()
. += emissive_appearance(icon, icon_state, alpha = alpha)
. += emissive_appearance(icon, icon_state, src, alpha = alpha)
/obj/item/stack/tile/emissive_test/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
. = ..()
. += emissive_appearance(standing.icon, standing.icon_state, alpha = standing.alpha)
. += emissive_appearance(standing.icon, standing.icon_state, src, alpha = standing.alpha)
/obj/item/stack/tile/emissive_test/sixty
amount = 60

View File

@@ -103,7 +103,7 @@ GLOBAL_LIST_EMPTY(tcgcard_radial_choices)
/obj/item/tcgcard/update_desc(updates)
. = ..()
if(!flipped)
var/datum/card/template = SStrading_card_game.cached_cards[series]["ALL"][id]
var/datum/card/template = extract_datum()
desc = "<i>[template.desc]</i>"
else
desc = "It's the back of a trading card... no peeking!"
@@ -113,7 +113,9 @@ GLOBAL_LIST_EMPTY(tcgcard_radial_choices)
icon_state = "cardback"
return ..()
var/datum/card/template = SStrading_card_game.cached_cards[series]["ALL"][id]
var/datum/card/template = extract_datum()
if(!template)
return
icon_state = template.icon_state
return ..()

View File

@@ -129,9 +129,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/claymore/highlander/process()
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
loc.plane = GAME_PLANE_UPPER_FOV_HIDDEN //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS)
ADD_TRAIT(H, TRAIT_NOBLEED, HIGHLANDER_TRAIT) //AND WE WON'T BLEED OUT LIKE COWARDS
var/mob/living/carbon/human/holder = loc
SET_PLANE_EXPLICIT(holder, GAME_PLANE_UPPER_FOV_HIDDEN, src) //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS)
ADD_TRAIT(holder, TRAIT_NOBLEED, HIGHLANDER_TRAIT) //AND WE WON'T BLEED OUT LIKE COWARDS
else
if(!(flags_1 & ADMIN_SPAWNED_1))
qdel(src)
@@ -245,7 +245,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
return INITIALIZE_HINT_QDEL
/obj/item/claymore/highlander/robot/process()
loc.plane = GAME_PLANE_UPPER_FOV_HIDDEN
SET_PLANE_IMPLICIT(loc, GAME_PLANE_UPPER_FOV_HIDDEN)
/obj/item/katana
name = "katana"

View File

@@ -30,8 +30,6 @@
var/drag_slowdown // Amont of multiplicative slowdown applied if pulled. >1 makes you slower, <1 makes you faster.
vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace.
/// Map tag for something. Tired of it being used on snowflake items. Moved here for some semblance of a standard.
/// Next pr after the network fix will have me refactor door interactions, so help me god.
var/id_tag = null

View File

@@ -114,10 +114,10 @@
/obj/structure/chair/proc/handle_layer()
if(has_buckled_mobs() && dir == NORTH)
layer = ABOVE_MOB_LAYER
plane = GAME_PLANE_UPPER
SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER_FOV_HIDDEN)
else
layer = OBJ_LAYER
plane = GAME_PLANE
SET_PLANE_IMPLICIT(src, GAME_PLANE)
/obj/structure/chair/post_buckle_mob(mob/living/M)
. = ..()
@@ -175,10 +175,22 @@
var/mutable_appearance/armrest
/obj/structure/chair/comfy/Initialize(mapload)
gen_armrest()
return ..()
/obj/structure/chair/comfy/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
if(same_z_layer)
return ..()
cut_overlay(armrest)
QDEL_NULL(armrest)
gen_armrest()
return ..()
/obj/structure/chair/comfy/proc/gen_armrest()
armrest = GetArmrest()
armrest.layer = ABOVE_MOB_LAYER
armrest.plane = GAME_PLANE_UPPER
return ..()
SET_PLANE_EXPLICIT(armrest, GAME_PLANE_UPPER, src)
update_armrest()
/obj/structure/chair/comfy/proc/GetArmrest()
return mutable_appearance(icon, "[icon_state]_armrest")

View File

@@ -19,10 +19,23 @@
var/mutable_appearance/leftpewarmrest
/obj/structure/chair/pew/left/Initialize(mapload)
gen_armrest()
return ..()
/obj/structure/chair/pew/left/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
if(same_z_layer)
return ..()
cut_overlay(leftpewarmrest)
QDEL_NULL(leftpewarmrest)
gen_armrest()
return ..()
/obj/structure/chair/pew/left/proc/gen_armrest()
leftpewarmrest = GetLeftPewArmrest()
leftpewarmrest.layer = ABOVE_MOB_LAYER
leftpewarmrest.plane = GAME_PLANE_UPPER
return ..()
SET_PLANE_EXPLICIT(leftpewarmrest, GAME_PLANE_UPPER, src)
update_leftpewarmrest()
/obj/structure/chair/pew/left/proc/GetLeftPewArmrest()
return mutable_appearance('icons/obj/sofa.dmi', "pewend_left_armrest")
@@ -51,10 +64,20 @@
var/mutable_appearance/rightpewarmrest
/obj/structure/chair/pew/right/Initialize(mapload)
gen_armrest()
return ..()
/obj/structure/chair/pew/right/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
cut_overlay(rightpewarmrest)
QDEL_NULL(rightpewarmrest)
gen_armrest()
return ..()
/obj/structure/chair/pew/right/proc/gen_armrest()
rightpewarmrest = GetRightPewArmrest()
rightpewarmrest.layer = ABOVE_MOB_LAYER
rightpewarmrest.plane = GAME_PLANE_UPPER
return ..()
SET_PLANE_EXPLICIT(rightpewarmrest, GAME_PLANE_UPPER, src)
update_rightpewarmrest()
/obj/structure/chair/pew/right/proc/GetRightPewArmrest()
return mutable_appearance('icons/obj/sofa.dmi', "pewend_right_armrest")

View File

@@ -8,10 +8,22 @@
/obj/structure/chair/sofa/Initialize(mapload)
. = ..()
armrest = mutable_appearance(initial(icon), "[icon_state]_armrest", ABOVE_MOB_LAYER)
armrest.plane = GAME_PLANE_UPPER
gen_armrest()
AddElement(/datum/element/soft_landing)
/obj/structure/chair/sofa/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
if(same_z_layer)
return ..()
cut_overlay(armrest)
QDEL_NULL(armrest)
gen_armrest()
return ..()
/obj/structure/chair/sofa/proc/gen_armrest()
armrest = mutable_appearance(initial(icon), "[icon_state]_armrest", ABOVE_MOB_LAYER)
SET_PLANE_EXPLICIT(armrest, GAME_PLANE_UPPER, src)
update_armrest()
/obj/structure/chair/sofa/electrify_self(obj/item/assembly/shock_kit/input_shock_kit, mob/user, list/overlays_from_child_procs)
if(!overlays_from_child_procs)
overlays_from_child_procs = list(image('icons/obj/chairs.dmi', loc, "echair_over", pixel_x = -1))

View File

@@ -45,13 +45,13 @@ LINEN BINS
return
if(layer == initial(layer))
layer = ABOVE_MOB_LAYER
plane = GAME_PLANE_UPPER
SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER)
to_chat(user, span_notice("You cover yourself with [src]."))
pixel_x = 0
pixel_y = 0
else
layer = initial(layer)
plane = initial(plane)
SET_PLANE_IMPLICIT(src, initial(plane))
to_chat(user, span_notice("You smooth [src] out beneath you."))
if(user.body_position == LYING_DOWN) //The player isn't laying down currently
dir = user.dir

View File

@@ -120,7 +120,7 @@
if(opened && has_opened_overlay)
var/mutable_appearance/door_overlay = mutable_appearance(icon, "[icon_state]_open", alpha = src.alpha)
. += door_overlay
door_overlay.overlays += emissive_blocker(door_overlay.icon, door_overlay.icon_state, alpha = door_overlay.alpha) // If we don't do this the door doesn't block emissives and it looks weird.
door_overlay.overlays += emissive_blocker(door_overlay.icon, door_overlay.icon_state, src, alpha = door_overlay.alpha) // If we don't do this the door doesn't block emissives and it looks weird.
else if(has_closed_overlay)
. += "[icon_door || icon_state]_door"
@@ -133,7 +133,7 @@
if(broken || !secure)
return
//Overlay is similar enough for both that we can use the same mask for both
. += emissive_appearance(icon, "locked", alpha = src.alpha)
. += emissive_appearance(icon, "locked", src, alpha = src.alpha)
. += locked ? "locked" : "unlocked"
/obj/structure/closet/vv_edit_var(vname, vval)

Some files were not shown because too many files have changed in this diff Show More