ID:1613721
 
Since there's been some request for me to write down a lot of the stuff I know in tutorial form, I've opted to once a weekish (You guys will have to keep me interested in doing these!) write out a simple tutorial on some finer points of working with DM.

This week's installment will be regarding overlays, and I'll be touching on some tricks that will allow you to do things a bit more easily than I see you guys trying to do in the source code I often run across.


Overlays. What are they good for?

Overlays are great for clothing and special effects that last a little while, and move with the player. Most BYOND games use them fairly well, with a few notable exceptions.

The basics of adding overlays are pretty simple, but we will first explain how they work on the inside.


How they work

On the surface, most people think overlays are a list of objects, but actually, they are a list of something called appearances. Appearances are a special internal type in BYOND, and are generated based on a number of appearance-related variables.

Appearances are a lot like very limited atoms, but they only keep track of the following variables (list is not 100% complete):

icon
icon_state
text
pixel_x
pixel_y
pixel_z
color
blend_mode
alpha
layer
overlays
underlays
transform
maptext
maptext_width
maptext_height


Appearances are sort of like a visual snapshot of an object. When you change the above variables of atoms, DM searches for the correct appearance reference to use.

This is why overlays allows you to add objects, and to add images, icons, text strings, etc. to the list. It's because we're always storing a list of appearances, and not all these bizarre types. Internally, the overlay += / -= operations figure out the appearance that's being manipulated and respond accordingly.

Knowing all of this allows us to get around a few bugs we often see in BYOND games, and it also allows us to abuse a few more to make our lives easier.


Layering

By default, when you add overlays to an object, they will render according to:

1) Layer
1) Underlay
2) Overlay
3) Sequence


That means that the layer of the appearance is the dominant trait in determining how the image should layer, second/third is whether it's in the underlays or overlays list, and last, the order it was added to underlays/overlays.


FLOAT_LAYER

The float layer is a special layer that's useful for making overlays "float" on top of something. By default, the "float layer" of an overlay means the item will share the exact same layer value of the root object.

This means that I can add an overlay using float layer to the player's overlays, then change the player's layer, and the overlay will appear to change layers with the player.

You can also set a differential value by subtracting a number from FLOAT_LAYER.

Let's put this into practice:

#define HAT_LAYER     FLOAT_LAYER
#define SHIRT_LAYER FLOAT_LAYER-0.01
#define PANTS_LAYER FLOAT_LAYER-0.02
#define SHOES_LAYER FLOAT_LAYER-0.03

obj
overlay
New(icon,layer)
..(loc=null)
src.icon = icon
src.layer = layer
underlay
New(icon,layer)
..(loc=null)
src.icon = icon
src.layer = layer

obj
item
equipment
var
equipped = FALSE
equip_icon
equip_layer
tmp
obj/overlay/overlay
proc
equip()
if(ismob(src.loc))
var/mob/m = src.loc
else
src.equipped = FALSE
return
if(!m.equipment.Find(src))
if(m.equip(src))
src.equipped(m)
else
src.equipped = FALSE
unequipped()
m.unequipped(src)
if(overlay)
m.overlays -= overlay
equipped = FALSE

equipped(mob/m)
m.equipped(src)
if(equip_icon)
overlay = new/obj/overlay(src.equip_icon,src.equip_layer)
m.overlays += overlay
equipped = TRUE

Read(savefile/F)
. = ..()
if(src.equipped==TRUE)
src.equip()


Bonus: for the complete equipment system, see this comment: http://www.byond.com/forum/?post=1187381#comment4165925


That's all there is to it. Using the float layer, you can better manage equipment than using MOB_LAYER+1 and such to set up your overlays manually. You also get the benefit of the overlays staying in place after the player's layer changes.


Efficiency tricks: Abusing appearances

One side-effect of the way overlay lists work, is that you can abuse their use of appearances to save you a lot of CPU time when generating a number of similar objects:

var/string = "Hello"
var/strlen = length(string)
var/obj/word = new()
var/obj/o = new()
o.icon = 'font.dmi'
for(var/pos in 1 to strlen)
o.icon_state = "[text2ascii(string,pos)]"
word.overlays += o
o.pixel_x = letter_widths[o.icon_state]


You'll notice in the above example I'm adding the same object to the overlays list of another object over and over again. This saves us a lot of CPU, and the outcome looks the same as using a different object for each letter. This is because when we change the icon_state and pixel_x of the object, it changes the appearance of the object, and we're adding that new appearance to the overlays of the word object.


Immutability

There's a trend in BYOND games to subtract things from overlays five or six times because for some reason, some overlays just tend to stick around when they are no longer needed. It's not a BYOND bug, it's a mutability problem.

This problem is best shown rather than explained:

var/obj/o = new/obj()
o.icon = 'derp.dmi'
o.icon_state = "42"
src.overlays += o
o.icon_state = "33"
src.overlays -= o


You'll notice that the icon_state "42" doesn't go away. That's because we changed the appearance of the object before subtracting it. If you add an object to an overlays list and want to remove it later, you can't change the object reference. If you want to allow that object to go away, but still want to remove that appearance from the overlays later, you can do something kind of sneaky:

var/obj/o = new/obj()
o.icon = 'derp.dmi'
o.icon_state = "42"
src.overlays += o
var/appearance = overlays[overlays.len]
o.icon-state = "33"
src.overlays += o
src.overlays -= appearance


You'll notice that this time, we managed to get rid of the overlay. That's because I kept a reference to the appearance itself.


And that's all for this week. Please leave suggestions for topics for Snippet Sunday #2 below!
Taking suggestions for the next week's topic.
In response to Ter13
Ter13 wrote:
Taking suggestions for the next week's topic.

The neglected eval function.
In response to Kozuma3
Kozuma3 wrote:
Ter13 wrote:
Taking suggestions for the next week's topic.

The neglected eval function.

Eval() has been non-functional from day 1. It's a non-topic.
should start covering topics from the red book and see which are actually like, useful in terms of stuff. like, if they can independently stand as aspects of the language and development.
Great job, didn't really know that overlays where like this on the inside.
The FLOAT_LAYER functionality exists for all negative values. FLOAT_LAYER is defined as -1.
This was a very helpful read. Thank you. :) I look forward to more posts like this and your savefile tutorials, although I do not have any suggestions for future topics right now
This week's topic has been selected. It's going to be about mapping and instance management.
Vocalized.
I will never understand any of this.
In response to Snackz
Snackz wrote:
I will never understand any of this.

With that attitude, no, you won't.

And the reason I started this series was because other people nagged me to give information out that I felt the majority of this community wouldn't grasp.

Don't prove my point. Prove me wrong.
This is very helpful. Thanks!