Persistent Trash/Dirt/Graffiti/Notices

This commit is contained in:
Cerebulon
2020-07-24 10:23:34 +01:00
committed by VirgoBot
parent 79de2902ba
commit 8b031dd8ad
58 changed files with 1166 additions and 190 deletions

View File

@@ -0,0 +1,35 @@
/datum/persistent/filth
name = "filth"
tokens_per_line = 5
entries_expire_at = 5
/datum/persistent/filth/LabelTokens(var/list/tokens)
var/list/labelled_tokens = ..()
labelled_tokens["path"] = text2path(tokens[LAZYLEN(labelled_tokens)+1])
return labelled_tokens
/datum/persistent/filth/IsValidEntry(var/atom/entry)
. = ..() && entry.invisibility == 0
/datum/persistent/filth/CheckTokenSanity(var/list/tokens)
return ..() && ispath(tokens["path"])
/datum/persistent/filth/CheckTurfContents(var/turf/T, var/list/tokens)
var/_path = tokens["path"]
return (locate(_path) in T) ? FALSE : TRUE
/datum/persistent/filth/CreateEntryInstance(var/turf/creating, var/list/tokens)
var/_path = tokens["path"]
new _path(creating, tokens["age"]+1)
/datum/persistent/filth/GetEntryAge(var/atom/entry)
var/obj/effect/decal/cleanable/filth = entry
return filth.age
/datum/persistent/filth/proc/GetEntryPath(var/atom/entry)
var/obj/effect/decal/cleanable/filth = entry
return filth.generic_filth ? /obj/effect/decal/cleanable/filth : filth.type
/datum/persistent/filth/CompileEntry(var/atom/entry)
. = ..()
LAZYADD(., "[GetEntryPath(entry)]")

View File

@@ -0,0 +1,51 @@
/datum/persistent/graffiti
name = "graffiti"
tokens_per_line = 6
entries_expire_at = 50
has_admin_data = TRUE
/datum/persistent/graffiti/LabelTokens(var/list/tokens)
var/list/labelled_tokens = ..()
var/entries = LAZYLEN(labelled_tokens)
labelled_tokens["author"] = tokens[entries+1]
labelled_tokens["message"] = tokens[entries+2]
return labelled_tokens
/datum/persistent/graffiti/GetValidTurf(var/turf/T, var/list/tokens)
var/turf/checking_turf = ..()
if(istype(checking_turf) && checking_turf.can_engrave())
return checking_turf
/datum/persistent/graffiti/CheckTurfContents(var/turf/T, var/list/tokens)
var/too_much_graffiti = 0
for(var/obj/effect/decal/writing/W in .)
too_much_graffiti++
if(too_much_graffiti >= 5)
return FALSE
return TRUE
/datum/persistent/graffiti/CreateEntryInstance(var/turf/creating, var/list/tokens)
new /obj/effect/decal/writing(creating, tokens["age"]+1, tokens["message"], tokens["author"])
/datum/persistent/graffiti/IsValidEntry(var/atom/entry)
. = ..()
if(.)
var/turf/T = entry.loc
. = T.can_engrave()
/datum/persistent/graffiti/GetEntryAge(var/atom/entry)
var/obj/effect/decal/writing/save_graffiti = entry
return save_graffiti.graffiti_age
/datum/persistent/graffiti/CompileEntry(var/atom/entry, var/write_file)
. = ..()
var/obj/effect/decal/writing/save_graffiti = entry
LAZYADD(., "[save_graffiti.author ? save_graffiti.author : "unknown"]")
LAZYADD(., "[save_graffiti.message]")
/datum/persistent/graffiti/GetAdminDataStringFor(var/thing, var/can_modify, var/mob/user)
var/obj/effect/decal/writing/save_graffiti = thing
if(can_modify)
. = "<td colspan = 2>[save_graffiti.message]</td><td>[save_graffiti.author]</td><td><a href='byond://?src=\ref[src];caller=\ref[user];remove_entry=\ref[thing]'>Destroy</a></td>"
else
. = "<td colspan = 3>[save_graffiti.message]</td><td>[save_graffiti.author]</td>"

View File

@@ -0,0 +1,56 @@
/datum/persistent/paper
name = "paper"
tokens_per_line = 7
entries_expire_at = 50
has_admin_data = TRUE
var/paper_type = /obj/item/weapon/paper
var/requires_noticeboard = TRUE
/datum/persistent/paper/LabelTokens(var/list/tokens)
var/list/labelled_tokens = ..()
var/entries = LAZYLEN(labelled_tokens)
labelled_tokens["author"] = tokens[entries+1]
labelled_tokens["message"] = tokens[entries+2]
labelled_tokens["title"] = tokens[entries+3]
return labelled_tokens
/datum/persistent/paper/CheckTurfContents(var/turf/T, var/list/tokens)
if(requires_noticeboard && !(locate(/obj/structure/noticeboard) in T))
new /obj/structure/noticeboard(T)
. = ..()
/datum/persistent/paper/CreateEntryInstance(var/turf/creating, var/list/tokens)
var/obj/structure/noticeboard/board = locate() in creating
if(requires_noticeboard && LAZYLEN(board.notices) >= board.max_notices)
return
var/obj/item/weapon/paper/paper = new paper_type(creating)
paper.set_content(tokens["message"], tokens["title"])
paper.last_modified_ckey = tokens["author"]
if(requires_noticeboard)
board.add_paper(paper)
SSpersistence.track_value(paper, type)
return paper
/datum/persistent/paper/GetEntryAge(var/atom/entry)
var/obj/item/weapon/paper/paper = entry
return paper.age
/datum/persistent/paper/CompileEntry(var/atom/entry, var/write_file)
. = ..()
var/obj/item/weapon/paper/paper = entry
LAZYADD(., "[paper.last_modified_ckey ? paper.last_modified_ckey : "unknown"]")
LAZYADD(., "[paper.info]")
LAZYADD(., "[paper.name]")
/datum/persistent/paper/GetAdminDataStringFor(var/thing, var/can_modify, var/mob/user)
var/obj/item/weapon/paper/paper = thing
if(can_modify)
. = "<td style='background-color:[paper.color]'>[paper.info]</td><td>[paper.name]</td><td>[paper.last_modified_ckey]</td><td><a href='byond://?src=\ref[src];caller=\ref[user];remove_entry=\ref[thing]'>Destroy</a></td>"
else
. = "<td colspan = 2;style='background-color:[paper.color]'>[paper.info]</td><td>[paper.name]</td><td>[paper.last_modified_ckey]</td>"
/datum/persistent/paper/RemoveValue(var/atom/value)
var/obj/structure/noticeboard/board = value.loc
if(istype(board))
board.remove_paper(value)
qdel(value)

View File

@@ -0,0 +1,28 @@
/datum/persistent/paper/sticky
name = "stickynotes"
paper_type = /obj/item/weapon/paper/sticky
requires_noticeboard = FALSE
tokens_per_line = 10
/datum/persistent/paper/sticky/LabelTokens(var/list/tokens)
var/list/labelled_tokens = ..()
var/entries = LAZYLEN(labelled_tokens)
labelled_tokens["offset_x"] = tokens[entries+1]
labelled_tokens["offset_y"] = tokens[entries+2]
labelled_tokens["color"] = tokens[entries+3]
return labelled_tokens
/datum/persistent/paper/sticky/CreateEntryInstance(var/turf/creating, var/list/tokens)
var/atom/paper = ..()
if(paper)
paper.pixel_x = text2num(tokens["offset_x"])
paper.pixel_y = text2num(tokens["offset_y"])
paper.color = tokens["color"]
return paper
/datum/persistent/paper/sticky/CompileEntry(var/atom/entry, var/write_file)
. = ..()
var/obj/item/weapon/paper/sticky/paper = entry
LAZYADD(., "[paper.pixel_x]")
LAZYADD(., "[paper.pixel_y]")
LAZYADD(., "[paper.color]")

View File

@@ -0,0 +1,17 @@
/datum/persistent/filth/trash
name = "trash"
/datum/persistent/filth/trash/CheckTurfContents(var/turf/T, var/list/tokens)
var/too_much_trash = 0
for(var/obj/item/trash/trash in T)
too_much_trash++
if(too_much_trash >= 5)
return FALSE
return TRUE
/datum/persistent/filth/trash/GetEntryAge(var/atom/entry)
var/obj/item/trash/trash = entry
return trash.age
/datum/persistent/filth/trash/GetEntryPath(var/atom/entry)
return entry.type

View File

@@ -0,0 +1,160 @@
// This is a set of datums instantiated by SSpersistence.
// They basically just handle loading, processing and saving specific forms
// of persistent data like graffiti and round to round filth.
/datum/persistent
var/name
var/filename
var/tokens_per_line
var/entries_expire_at
var/entries_decay_at
var/entry_decay_weight = 0.5
var/file_entry_split_character = "\t"
var/file_entry_substitute_character = " "
var/file_line_split_character = "\n"
var/has_admin_data
/datum/persistent/New()
SetFilename()
..()
/datum/persistent/proc/SetFilename()
if(name)
filename = "data/persistent/[lowertext(using_map.name)]-[lowertext(name)].txt"
if(!isnull(entries_decay_at) && !isnull(entries_expire_at))
entries_decay_at = round(entries_expire_at * entries_decay_at)
/datum/persistent/proc/LabelTokens(var/list/tokens)
var/list/labelled_tokens = list()
labelled_tokens["x"] = text2num(tokens[1])
labelled_tokens["y"] = text2num(tokens[2])
labelled_tokens["z"] = text2num(tokens[3])
labelled_tokens["age"] = text2num(tokens[4])
return labelled_tokens
/datum/persistent/proc/GetValidTurf(var/turf/T, var/list/tokens)
if(T && CheckTurfContents(T, tokens))
return T
/datum/persistent/proc/CheckTurfContents(var/turf/T, var/list/tokens)
return TRUE
/datum/persistent/proc/CheckTokenSanity(var/list/tokens)
return ( \
!isnull(tokens["x"]) && \
!isnull(tokens["y"]) && \
!isnull(tokens["z"]) && \
!isnull(tokens["age"]) && \
tokens["age"] <= entries_expire_at \
)
/datum/persistent/proc/CreateEntryInstance(var/turf/creating, var/list/tokens)
return
/datum/persistent/proc/ProcessAndApplyTokens(var/list/tokens)
// If it's old enough we start to trim down any textual information and scramble strings.
if(tokens["message"] && !isnull(entries_decay_at) && !isnull(entry_decay_weight))
var/_n = tokens["age"]
var/_message = tokens["message"]
if(_n >= entries_decay_at)
var/decayed_message = ""
for(var/i = 1 to length(_message))
var/char = copytext(_message, i, i + 1)
if(prob(round(_n * entry_decay_weight)))
if(prob(99))
decayed_message += pick(".",",","-","'","\\","/","\"",":",";")
else
decayed_message += char
_message = decayed_message
if(length(_message))
tokens["message"] = _message
else
return
var/_z = tokens["z"]
if(_z in using_map.station_levels)
. = GetValidTurf(locate(tokens["x"], tokens["y"], _z), tokens)
if(.)
CreateEntryInstance(., tokens)
/datum/persistent/proc/IsValidEntry(var/atom/entry)
if(!istype(entry))
return FALSE
if(GetEntryAge(entry) >= entries_expire_at)
return FALSE
var/turf/T = get_turf(entry)
if(!T || !(T.z in using_map.station_levels) )
return FALSE
var/area/A = get_area(T)
if(!A || (A.flags & AREA_FLAG_IS_NOT_PERSISTENT))
return FALSE
return TRUE
/datum/persistent/proc/GetEntryAge(var/atom/entry)
return 0
/datum/persistent/proc/CompileEntry(var/atom/entry)
var/turf/T = get_turf(entry)
. = list(
T.x,
T.y,
T.z,
GetEntryAge(entry)
)
/datum/persistent/proc/Initialize()
if(fexists(filename))
for(var/entry_line in file2list(filename, file_line_split_character))
if(!entry_line)
continue
var/list/tokens = splittext(entry_line, file_entry_split_character)
if(LAZYLEN(tokens) < tokens_per_line)
continue
tokens = LabelTokens(tokens)
if(!CheckTokenSanity(tokens))
continue
ProcessAndApplyTokens(tokens)
/datum/persistent/proc/Shutdown()
if(fexists(filename))
fdel(filename)
var/write_file = file(filename)
for(var/thing in SSpersistence.tracking_values[type])
if(IsValidEntry(thing))
var/list/entry = CompileEntry(thing)
if(LAZYLEN(entry) == tokens_per_line)
for(var/i = 1 to LAZYLEN(entry))
if(istext(entry[i]))
entry[i] = replacetext(entry[i], file_entry_split_character, file_entry_substitute_character)
to_file(write_file, jointext(entry, file_entry_split_character))
/datum/persistent/proc/RemoveValue(var/atom/value)
qdel(value)
/datum/persistent/proc/GetAdminSummary(var/mob/user, var/can_modify)
. = list("<tr><td colspan = 4><b>[capitalize(name)]</b></td></tr>")
. += "<tr><td colspan = 4><hr></td></tr>"
for(var/thing in SSpersistence.tracking_values[type])
. += "<tr>[GetAdminDataStringFor(thing, can_modify, user)]</tr>"
. += "<tr><td colspan = 4><hr></td></tr>"
/datum/persistent/proc/GetAdminDataStringFor(var/thing, var/can_modify, var/mob/user)
if(can_modify)
. = "<td colspan = 3>[thing]</td><td><a href='byond://?src=\ref[src];caller=\ref[user];remove_entry=\ref[thing]'>Destroy</a></td>"
else
. = "<td colspan = 4>[thing]</td>"
/datum/persistent/Topic(var/href, var/href_list)
. = ..()
if(!.)
if(href_list["remove_entry"])
var/datum/value = locate(href_list["remove_entry"])
if(istype(value))
RemoveValue(value)
. = TRUE
if(.)
var/mob/user = locate(href_list["caller"])
if(user)
SSpersistence.show_info(user)

View File

@@ -0,0 +1,12 @@
/obj/effect/decal/cleanable/filth
name = "filth"
desc = "Disgusting. Someone from last shift didn't do their job properly."
icon = 'icons/effects/blood.dmi'
icon_state = "mfloor1"
random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7")
color = "#464f33"
persistent = TRUE
/obj/effect/decal/cleanable/filth/Initialize()
. = ..()
alpha = rand(180,220)

View File

@@ -0,0 +1,64 @@
/obj/effect/decal/writing
name = "hand graffiti"
icon_state = "writing1"
icon = 'icons/effects/writing.dmi'
desc = "It looks like someone has scratched something here."
plane = DIRTY_PLANE
gender = PLURAL
blend_mode = BLEND_MULTIPLY
color = "#000000"
alpha = 120
var/message
var/graffiti_age = 0
var/author = "unknown"
/obj/effect/decal/writing/New(var/newloc, var/_age, var/_message, var/_author)
..(newloc)
if(!isnull(_age))
graffiti_age = _age
message = _message
if(!isnull(author))
author = _author
/obj/effect/decal/writing/Initialize()
var/list/random_icon_states = icon_states(icon)
for(var/obj/effect/decal/writing/W in loc)
random_icon_states.Remove(W.icon_state)
if(random_icon_states.len)
icon_state = pick(random_icon_states)
SSpersistence.track_value(src, /datum/persistent/graffiti)
. = ..()
/obj/effect/decal/writing/Destroy()
SSpersistence.forget_value(src, /datum/persistent/graffiti)
. = ..()
/obj/effect/decal/writing/examine(mob/user)
. = ..()
to_chat(user, "It reads \"[message]\".")
/obj/effect/decal/writing/attackby(var/obj/item/thing, var/mob/user)
if(is_hot(thing))
var/obj/item/weapon/weldingtool/welder = thing
if(welder.isOn() && welder.remove_fuel(0,user) && do_after(user, 5, src) && !QDELETED(src))
playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
user.visible_message("<span class='notice'>\The [user] clears away some graffiti.</span>")
qdel(src)
else if(thing.sharp)
if(jobban_isbanned(user, "Graffiti"))
to_chat(user, SPAN_WARNING("You are banned from leaving persistent information across rounds."))
return
var/_message = sanitize(input("Enter an additional message to engrave.", "Graffiti") as null|text, trim = TRUE)
if(_message && loc && user && !user.incapacitated() && user.Adjacent(loc) && thing.loc == user)
user.visible_message("<span class='warning'>\The [user] begins carving something into \the [loc].</span>")
if(do_after(user, max(20, length(_message)), src) && loc)
user.visible_message("<span class='danger'>\The [user] carves some graffiti into \the [loc].</span>")
message = "[message] [_message]"
author = user.ckey
if(lowertext(message) == "elbereth")
to_chat(user, "<span class='notice'>You feel much safer.</span>")
else
. = ..()

View File

@@ -0,0 +1,221 @@
/obj/structure/noticeboard
name = "notice board"
desc = "A board for pinning important notices upon."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "nboard00"
density = 0
anchored = 1
var/list/notices
var/base_icon_state = "nboard0"
var/const/max_notices = 5
/obj/structure/noticeboard/Initialize()
. = ..()
// Grab any mapped notices.
notices = list()
for(var/obj/item/weapon/paper/note in get_turf(src))
note.forceMove(src)
LAZYADD(notices, note)
if(LAZYLEN(notices) >= max_notices)
break
// Automatically place noticeboards that aren't mapped to specific positions.
if(pixel_x == 0 && pixel_y == 0)
var/turf/here = get_turf(src)
var/placing = 0
for(var/checkdir in GLOB.cardinal)
var/turf/T = get_step(here, checkdir)
if(T.density)
placing = checkdir
break
for(var/thing in T)
var/atom/A = thing
if(A.simulated && !A.CanPass(src, T))
placing = checkdir
break
switch(placing)
if(NORTH)
pixel_x = 0
pixel_y = 32
if(SOUTH)
pixel_x = 0
pixel_y = -32
if(EAST)
pixel_x = 32
pixel_y = 0
if(WEST)
pixel_x = -32
pixel_y = 0
update_icon()
/obj/structure/noticeboard/proc/add_paper(var/atom/movable/paper, var/skip_icon_update)
if(istype(paper))
LAZYDISTINCTADD(notices, paper)
paper.forceMove(src)
if(!skip_icon_update)
update_icon()
/obj/structure/noticeboard/proc/remove_paper(var/atom/movable/paper, var/skip_icon_update)
if(istype(paper) && paper.loc == src)
paper.dropInto(loc)
LAZYREMOVE(notices, paper)
SSpersistence.forget_value(paper, /datum/persistent/paper)
if(!skip_icon_update)
update_icon()
/obj/structure/noticeboard/proc/dismantle()
for(var/thing in notices)
remove_paper(thing, skip_icon_update = TRUE)
new /obj/item/stack/material/wood(get_turf(src))
qdel(src)
/obj/structure/noticeboard/Destroy()
QDEL_NULL_LIST(notices)
. = ..()
/obj/structure/noticeboard/ex_act(var/severity)
dismantle()
/obj/structure/noticeboard/update_icon()
icon_state = "[base_icon_state][LAZYLEN(notices)]"
/obj/structure/noticeboard/attackby(var/obj/item/weapon/thing, var/mob/user)
if(thing.is_screwdriver())
var/choice = input("Which direction do you wish to place the noticeboard?", "Noticeboard Offset") as null|anything in list("North", "South", "East", "West")
if(choice && Adjacent(user) && thing.loc == user && !user.incapacitated())
playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
switch(choice)
if("North")
pixel_x = 0
pixel_y = 32
if("South")
pixel_x = 0
pixel_y = -32
if("East")
pixel_x = 32
pixel_y = 0
if("West")
pixel_x = -32
pixel_y = 0
return
else if(thing.is_wrench())
visible_message(SPAN_WARNING("\The [user] begins dismantling \the [src]."))
playsound(loc, 'sound/items/Ratchet.ogg', 50, 1)
if(do_after(user, 50, src))
visible_message(SPAN_DANGER("\The [user] has dismantled \the [src]!"))
dismantle()
return
else if(istype(thing, /obj/item/weapon/paper) || istype(thing, /obj/item/weapon/photo))
if(jobban_isbanned(user, "Graffiti"))
to_chat(user, SPAN_WARNING("You are banned from leaving persistent information across rounds."))
else
if(LAZYLEN(notices) < max_notices && user.unEquip(thing, src))
add_fingerprint(user)
add_paper(thing)
to_chat(user, SPAN_NOTICE("You pin \the [thing] to \the [src]."))
SSpersistence.track_value(thing, /datum/persistent/paper)
else
to_chat(user, SPAN_WARNING("You hesitate, certain \the [thing] will not be seen among the many others already attached to \the [src]."))
return
..()
/obj/structure/noticeboard/attack_ai(var/mob/user)
examine(user)
/obj/structure/noticeboard/attack_hand(var/mob/user)
examine(user)
/obj/structure/noticeboard/examine(var/mob/user)
. = ..()
if(.)
var/list/dat = list("<table>")
for(var/thing in notices)
LAZYADD(dat, "<tr><td>[thing]</td><td>")
if(istype(thing, /obj/item/weapon/paper))
LAZYADD(dat, "<a href='?src=\ref[src];read=\ref[thing]'>Read</a><a href='?src=\ref[src];write=\ref[thing]'>Write</a>")
else if(istype(thing, /obj/item/weapon/photo))
LAZYADD(dat, "<a href='?src=\ref[src];look=\ref[thing]'>Look</a>")
LAZYADD(dat, "<a href='?src=\ref[src];remove=\ref[thing]'>Remove</a></td></tr>")
var/datum/browser/popup = new(user, "noticeboard-\ref[src]", "Noticeboard")
popup.set_content(jointext(dat, null))
popup.open()
/obj/structure/noticeboard/Topic(var/mob/user, var/list/href_list)
if(href_list["read"])
var/obj/item/weapon/paper/P = locate(href_list["read"])
if(P && P.loc == src)
P.show_content(user)
. = TOPIC_HANDLED
if(href_list["look"])
var/obj/item/weapon/photo/P = locate(href_list["look"])
if(P && P.loc == src)
P.show(user)
. = TOPIC_HANDLED
if(href_list["remove"])
remove_paper(locate(href_list["remove"]))
add_fingerprint(user)
. = TOPIC_REFRESH
if(href_list["write"])
if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
return
var/obj/item/P = locate(href_list["write"])
if((P && P.loc == src)) //ifthe paper's on the board
var/mob/living/M = usr
if(istype(M))
var/obj/item/weapon/pen/E = M.get_type_in_hands(/obj/item/weapon/pen)
if(E)
add_fingerprint(M)
P.attackby(E, usr)
else
to_chat(M, "<span class='notice'>You'll need something to write with!</span>")
. = TOPIC_REFRESH
if(. == TOPIC_REFRESH)
interact(user)
/obj/structure/noticeboard/anomaly
notices = 5
icon_state = "nboard05"
/obj/structure/noticeboard/anomaly/New()
var/obj/item/weapon/paper/P = new()
P.name = "Memo RE: proper analysis procedure"
P.info = "<br>We keep test dummies in pens here for a reason, so standard procedure should be to activate newfound alien artifacts and place the two in close proximity. Promising items I might even approve monkey testing on."
P.stamped = list(/obj/item/weapon/stamp/rd)
P.overlays = list("paper_stamped_rd")
src.contents += P
P = new()
P.name = "Memo RE: materials gathering"
P.info = "Corasang,<br>the hands-on approach to gathering our samples may very well be slow at times, but it's safer than allowing the blundering miners to roll willy-nilly over our dig sites in their mechs, destroying everything in the process. And don't forget the escavation tools on your way out there!<br>- R.W"
P.stamped = list(/obj/item/weapon/stamp/rd)
P.overlays = list("paper_stamped_rd")
src.contents += P
P = new()
P.name = "Memo RE: ethical quandaries"
P.info = "Darion-<br><br>I don't care what his rank is, our business is that of science and knowledge - questions of moral application do not come into this. Sure, so there are those who would employ the energy-wave particles my modified device has managed to abscond for their own personal gain, but I can hardly see the practical benefits of some of these artifacts our benefactors left behind. Ward--"
P.stamped = list(/obj/item/weapon/stamp/rd)
P.overlays = list("paper_stamped_rd")
src.contents += P
P = new()
P.name = "READ ME! Before you people destroy any more samples"
P.info = "how many times do i have to tell you people, these xeno-arch samples are del-i-cate, and should be handled so! careful application of a focussed, concentrated heat or some corrosive liquids should clear away the extraneous carbon matter, while application of an energy beam will most decidedly destroy it entirely - like someone did to the chemical dispenser! W, <b>the one who signs your paychecks</b>"
P.stamped = list(/obj/item/weapon/stamp/rd)
P.overlays = list("paper_stamped_rd")
src.contents += P
P = new()
P.name = "Reminder regarding the anomalous material suits"
P.info = "Do you people think the anomaly suits are cheap to come by? I'm about a hair trigger away from instituting a log book for the damn things. Only wear them if you're going out for a dig, and for god's sake don't go tramping around in them unless you're field testing something, R"
P.stamped = list(/obj/item/weapon/stamp/rd)
P.overlays = list("paper_stamped_rd")
src.contents += P