I decided to share since there was a question posted recently about this exact topic. That topic was of use to me, so I'm sure there are others who will find this handy.
proc/shadowed_maptext(text, text_color = "#fff", x = 0, y = 0, width = 256, height = 256, offset = 1, shadow_dir = SOUTH, shadow_color = "#000")
var obj/temp = new/obj
temp.maptext = "<span style=\"color:[shadow_color];\">[text]</span>"
temp.maptext_width = width
temp.maptext_height = height
temp.maptext_x = x + ((shadow_dir & (EAST | WEST)) ? ((shadow_dir & EAST && offset) || -offset) : (0))
temp.maptext_y = y + ((shadow_dir & (NORTH | SOUTH)) ? ((shadow_dir & NORTH && offset) || -offset) : (0))
temp.underlays += temp.appearance
temp.maptext = "<span style=\"color:[text_color];\">[text]</span>"
temp.maptext_x = 0
temp.maptext_y = 0
temp.layer = MOB_LAYER + 1
temp.overlays += temp.appearance
return temp
Here's an example of how it's used:
proc/stun_maptext(turf/effect_loc, atom/movable/ref, fade_time = 10)
var obj/o = shadowed_maptext(span("stun_graphic", "Stunned!"))
o.loc = effect_loc
if(ref) o.SetCenter(ref.Cx(), ref.Cy(), ref.z)
animate(o, pixel_y = o.pixel_y + tiles(1), alpha = 0, time = fade_time)
new/timed_event(fade_time, "soft_dispose", o)
Edit: Minor edit switching from using the color variable to using inline CSS.
Used your vanilla code, but sent those values to text.