Skip to content

Commit

Permalink
[READY] Faxes (OracleStation#487)
Browse files Browse the repository at this point in the history
* Faxes Part 1: It compiles

* TGUI compilation courtesy of Andrew

* Fixes, feedback, etc

* Template fluff

* Cleanup

* Fixes fax panel. FFS.

* Fixes a thing

* span

* Changes

* It's called a period not a full stop.

* fuck I didn't close a span

* Evil fax sound/animation
  • Loading branch information
ike709 authored and AndrewMontagne committed Feb 2, 2018
1 parent 0e749be commit ed44d91
Show file tree
Hide file tree
Showing 13 changed files with 892 additions and 91 deletions.
1 change: 1 addition & 0 deletions code/__DEFINES/admin.dm
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]"
#define ADMIN_COORDJMP(src) "[src ? "[COORD(src)] [ADMIN_JMP(src)]" : "nonexistent location"]"
#define ADMIN_INDIVIDUALLOG(user) "(<a href='?_src_=holder;[HrefToken(TRUE)];individuallog=[REF(user)]'>LOGS</a>)"
#define ADMIN_FAX(user, fax, faxtype, sent) "(<a href='?_src_=holder;[HrefToken(TRUE)];AdminFaxCreate=[REF(user)];originfax=[REF(fax)];faxtype=[faxtype];replyto=[REF(sent)]'>FAX</a>)"

#define ADMIN_PUNISHMENT_LIGHTNING "Lightning bolt"
#define ADMIN_PUNISHMENT_BRAINDAMAGE "Brain damage"
Expand Down
44 changes: 44 additions & 0 deletions code/__HELPERS/unique_ids.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Unique Datum Identifiers

// Basically, a replacement for plain REF()s that ensure the reference still
// points to the exact same datum/client, but doesn't prevent GC like tags do.

// An unintended side effect of the way UIDs are formatted is that the locate()
// proc will ignore the number and attempt to locate the reference. I consider
// this a feature, since it means they're conveniently backwards compatible.

// Turns this:
// var/myref = "[REF(mydatum)]"
// var/datum/D = locate(myref)
// into this:
// var/myUID = mydatum.UID()
// var/datum/D = locateUID(myUID)

GLOBAL_VAR_INIT(next_unique_datum_id, 1)

/datum/var/tmp/unique_datum_id = null

/datum/proc/UID()
if(!unique_datum_id)
var/tag_backup = tag
tag = null // Grab the raw ref, not the tag
unique_datum_id = "[REF(src)]_[GLOB.next_unique_datum_id++]"
tag = tag_backup
return unique_datum_id

/proc/locateUID(uid)
if(!istext(uid))
return null

var/splitat = findlasttext(uid, "_")

if(!splitat)
return null

var/datum/D = locate(copytext(uid, 1, splitat))

// We might locate a client instead of a datum, but just using : is easier
// than actually checking and typecasting
if(D && D:unique_datum_id == uid)
return D
return null
4 changes: 3 additions & 1 deletion code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/mass_zombie_cure,
/client/proc/polymorph_all,
/client/proc/show_tip,
/client/proc/smite
/client/proc/smite,
/client/proc/fax_panel
))
GLOBAL_PROTECT(admin_verbs_spawn)
GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /client/proc/respawn_character))
Expand Down Expand Up @@ -236,6 +237,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/debug_huds,
/client/proc/customiseSNPC,
/client/proc/resetSNPC,
/client/proc/fax_panel
))

/client/proc/add_admin_verbs()
Expand Down
201 changes: 201 additions & 0 deletions code/modules/admin/topic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2289,6 +2289,207 @@

usr << browse(dat.Join("<br>"), "window=related_[C];size=420x300")

//Topics relating to Faxes
else if(href_list["AdminFaxCreate"])
if(!check_rights(R_FUN))
return

var/mob/sender = locate(href_list["AdminFaxCreate"])
var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"])
var/faxtype = href_list["faxtype"]
var/reply_to = locate(href_list["replyto"])
var/destination
var/notify

var/obj/item/paper/P = new /obj/item/paper(null) //hopefully the null loc won't cause trouble for us

if(!fax)
var/list/departmentoptions = GLOB.alldepartments + "All Departments"
destination = input(usr, "To which department?", "Choose a department", "") as null|anything in departmentoptions
if(!destination)
qdel(P)
return

for(var/thing in GLOB.allfaxes)
var/obj/machinery/photocopier/faxmachine/F = thing
if(destination != "All Departments" && F.department == destination)
fax = F


var/input_text = input(src.owner, "Please enter a message to send a fax via secure connection. Use <br> for line breaks. Both pencode and HTML work.", "Outgoing message from Centcomm", "") as message|null
if(!input_text)
qdel(P)
return

var/obj/item/pen/admin_writer = new /obj/item/pen(null)

input_text = P.parsepencode(input_text, admin_writer, usr) // Encode everything from pencode to html
qdel(admin_writer)

var/customname = input(src.owner, "Pick a title for the fax.", "Fax Title") as text|null
if(!customname)
customname = "paper"

var/sendername
switch(faxtype)
if("Central Command")
sendername = "Central Command"
if("Syndicate")
sendername = "UNKNOWN"
if("Custom")
sendername = input(owner, "What organization does the fax come from? This determines the prefix of the paper (i.e. Central Command- Title). This is optional.", "Organization") as text|null

if(sender)
notify = alert(owner, "Would you like to inform the original sender that a fax has arrived?","Notify Sender","Yes","No")

// Create the reply message
if(sendername)
P.name = "[sendername]- [customname]"
else
P.name = "[customname]"
P.info = input_text
P.update_icon()
P.x = rand(-2, 0)
P.y = rand(-1, 2)

if(destination != "All Departments")
if(fax.receivefax(P) == FALSE)
to_chat(owner, "<span class='warning'>Message transmission failed.</span>")
return
else
for(var/thing in GLOB.allfaxes)
var/obj/machinery/photocopier/faxmachine/F = thing
if(F.z in GLOB.station_z_levels)
addtimer(CALLBACK(src, .proc/handle_sendall, F, P), 0)

var/datum/fax/admin/A = new /datum/fax/admin()
A.name = P.name
A.from_department = faxtype
if(destination != "All Departments")
A.to_department = fax.department
else
A.to_department = "All Departments"
A.origin = "Custom"
A.message = P
A.reply_to = reply_to
A.sent_by = usr
A.sent_at = world.time

to_chat(src.owner, "<span class='notice'>Message transmitted successfully.</span>")
if(notify == "Yes")
var/mob/living/carbon/human/H = sender
if(istype(H) && H.stat == CONSCIOUS && (istype(H.ears, /obj/item/device/radio/headset)))
to_chat(sender, "<span class='notice'>Your headset pings, notifying you that a reply to your fax has arrived.</span>")
if(sender)
log_admin("[key_name(src.owner)] replied to a fax message from [key_name(sender)]: [input_text]")
message_admins("[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(sender)] (<a href='?_src_=holder;[HrefToken(TRUE)];AdminFaxView=[REF(P)]'>VIEW</a>).", 1)
else
log_admin("[key_name(src.owner)] sent a fax message to [destination]: [input_text]")
message_admins("[key_name_admin(src.owner)] sent a fax message to [destination] (<a href='?_src_=holder;[HrefToken(TRUE)];AdminFaxView=[REF(P)]'>VIEW</a>).", 1)
return

else if(href_list["refreshfaxpanel"])
if(!check_rights(R_FUN))
return

fax_panel(usr)
return

else if(href_list["EvilFax"])
if(!check_rights(R_FUN))
return
var/mob/living/carbon/human/H = locate(href_list["EvilFax"])
if(!istype(H))
to_chat(usr, "<span class='notice'>This can only be used on instances of type /mob/living/carbon/human.</span>")
return
var/etypes = list("Borgification","Corgification","Death By Fire","Demotion Notice")
var/eviltype = input(src.owner, "Which type of evil fax do you wish to send [H]?","Its good to be baaaad...", "") as null|anything in etypes
if(!(eviltype in etypes))
return
var/customname = input(src.owner, "Pick a title for the evil fax.", "Fax Title") as text|null
if(!customname)
customname = "paper"
var/obj/item/paper/evilfax/P = new /obj/item/paper/evilfax(null)
var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"])

P.name = "Central Command - [customname]"
P.info = "<span class='danger'>You really should've known better.</span>"
P.myeffect = eviltype
P.mytarget = H
if(alert("Do you want the Evil Fax to activate automatically if [H] tries to ignore it?",,"Yes", "No") == "Yes")
P.activate_on_timeout = TRUE
P.x = rand(-2, 0)
P.y = rand(-1, 2)
P.update_icon()
//we have to physically teleport the fax paper
fax.handle_animation()
P.forceMove(fax.loc)
if(istype(H) && H.stat == CONSCIOUS && (istype(H.ears, /obj/item/device/radio/headset)))
to_chat(H, "<span class='notice'>Your headset pings, notifying you that a reply to your fax has arrived.</span>")
to_chat(src.owner, "<span class='notice'>You sent a [eviltype] fax to [H].</span>")
log_admin("[key_name(src.owner)] sent [key_name(H)] a [eviltype] fax")
message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a [eviltype] fax")
return

else if(href_list["FaxReplyTemplate"])
if(!check_rights(R_FUN))
return
var/mob/living/carbon/human/H = locate(href_list["FaxReplyTemplate"])
if(!istype(H))
to_chat(usr, "<span class='notice'>This can only be used on instances of type /mob/living/carbon/human.</span>")
return
var/obj/item/paper/P = new /obj/item/paper(null)
var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"])
P.name = "Central Command - paper"
var/stypes = list("Handle it yourselves!","Illegible fax","Fax not signed","Not Right Now","You are wasting our time", "Keep up the good work")
var/stype = input(src.owner, "Which type of standard reply do you wish to send to [H]?","Choose your paperwork", "") as null|anything in stypes
var/tmsg = "<font face='Verdana' color='black'><center><BR><font size='4'><B>[GLOB.station_name]</B></font><BR><BR><BR><font size='4'>Nanotrasen Communications Department Report</font></center><BR><BR>"
if(stype == "Handle it yourselves!")
tmsg += "Greetings, esteemed crewmember. Your fax has been <B><I>DECLINED</I></B> automatically by the Communications Department Fax Registration System.<BR><BR>Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.<BR><BR><i><small>This is an automatic message.</small>"
else if(stype == "Illegible fax")
tmsg += "Greetings, esteemed crewmember. Your fax has been <B><I>DECLINED</I></B> automatically by the Communications Department Fax Registration System.<BR><BR>Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.<BR><BR>Please consult your nearest dictionary and/or thesaurus and try again.<BR><BR><i><small>This is an automatic message.</small>"
else if(stype == "Fax not signed")
tmsg += "Greetings, esteemed crewmember. Your fax has been <B><I>DECLINED</I></B> automatically by the Communications Department Fax Registration System.<BR><BR>Your fax has not been correctly signed and, as such, we cannot verify your identity.<BR><BR>Please sign your faxes before sending them so that we may verify your identity.<BR><BR><i><small>This is an automatic message.</small>"
else if(stype == "Not Right Now")
tmsg += "Greetings, esteemed crewmember. Your fax has been <B><I>DECLINED</I></B> automatically by the Communications Department Fax Registration System.<BR><BR>Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.<BR><BR>This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the Communications Department Fax Registration System.<BR><BR>Please try again later.<BR><BR><i><small>This is an automatic message.</small>"
else if(stype == "You are wasting our time")
tmsg += "Greetings, esteemed crewmember. Your fax has been <B><I>DECLINED</I></B> automatically by the Communications Department Fax Registration System.<BR><BR>In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.<BR><BR>Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.<BR><BR><i><small>This is an automatic message.</i></small>"
else if(stype == "Keep up the good work")
tmsg += "Greetings, esteemed crewmember. Your fax has been received successfully by the Communications Department Fax Registration System.<BR><BR>We at Central Command appreciate the good work that you have done here, and sincerely recommend that you continue such a display of dedication to the company.<BR><BR><i><small>This is absolutely not an automated message.</i></small>"
else
return
tmsg += "</font>"
P.info = tmsg
P.x = rand(-2, 0)
P.y = rand(-1, 2)
P.update_icon()
fax.receivefax(P)
if(istype(H) && H.stat == CONSCIOUS && (istype(H.ears, /obj/item/device/radio/headset)))
to_chat(H, "<span class='notice'>Your headset pings, notifying you that a reply to your fax has arrived.</span>")
to_chat(src.owner, "<span class='notice'>You sent a standard '[stype]' fax to [H].</span>")
log_admin("[key_name(src.owner)] sent [key_name(H)] a standard '[stype]' fax")
message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a standard '[stype]' fax")
return

else if(href_list["AdminFaxView"])
if(!check_rights(R_FUN))
return

var/obj/item/fax = locate(href_list["AdminFaxView"])
if(istype(fax, /obj/item/paper))
var/obj/item/paper/P = fax
P.show_content(usr, TRUE)
else if(istype(fax, /obj/item/photo))
var/obj/item/photo/H = fax
H.show(usr)
else
to_chat(usr, "<span class='warning'>The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]</span>")
return

/datum/admins/proc/handle_sendall(var/obj/machinery/photocopier/faxmachine/F, var/obj/item/paper/P)
if(F.receivefax(P) == FALSE)
to_chat(owner, "<span class='warning'>Message transmission to [F.department] failed.</span>")

/datum/admins/proc/HandleCMode()
if(!check_rights(R_ADMIN))
return
Expand Down
89 changes: 89 additions & 0 deletions code/modules/paperwork/fax.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Fax datum - holds all faxes sent during the round
GLOBAL_LIST_EMPTY(faxes)
GLOBAL_LIST_EMPTY(adminfaxes)

/datum/fax
var/name = "fax"
var/from_department = null
var/to_department = null
var/origin = null
var/message = null
var/sent_by = null
var/sent_at = null

/datum/fax/New()
GLOB.faxes += src

/datum/fax/admin
var/list/reply_to = null

/datum/fax/admin/New()
GLOB.adminfaxes += src

// Fax panel - lets admins check all faxes sent during the round
/client/proc/fax_panel()
set name = "Fax Panel"
set category = "Fun"
if(holder)
holder.fax_panel(usr)
SSblackbox.add_details("admin_verb","FXP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return

/datum/admins/proc/fax_panel(var/mob/living/user)
var/html = "<A align='right' href='?src=[UID()];[HrefToken(TRUE)];refreshfaxpanel=1'>Refresh</A>"
html += "<A align='right' href='?src=[UID()];[HrefToken(TRUE)];AdminFaxCreate=1;faxtype=Custom'>Create Fax</A>"

html += "<div class='block'>"
html += "<h2>Admin Faxes</h2>"
html += "<table>"
html += "<tr style='font-weight:bold;'><td width='150px'>Name</td><td width='150px'>From Department</td><td width='150px'>To Department</td><td width='75px'>Sent At</td><td width='150px'>Sent By</td><td width='50px'>View</td><td width='50px'>Reply</td><td width='75px'>Replied To</td></td></tr>"
for(var/thing in GLOB.adminfaxes)
var/datum/fax/admin/A = thing
html += "<tr>"
html += "<td>[A.name]</td>"
html += "<td>[A.from_department]</td>"
html += "<td>[A.to_department]</td>"
html += "<td>[worldtime2text(A.sent_at)]</td>"
if(A.sent_by)
var/mob/living/S = A.sent_by
html += "<td><A HREF='?_src_=holder;[HrefToken(TRUE)];adminplayeropts=[REF(A.sent_by)]'>[S.name]</A></td>"
else
html += "<td>Unknown</td>"
html += "<td><A align='right' href='?src=[UID()];[HrefToken(TRUE)];AdminFaxView=[REF(A.message)]'>View</A></td>"
if(!A.reply_to)
if(A.from_department == "Administrator")
html += "<td>N/A</td>"
else
html += "<td><A align='right' href='?src=[UID()];[HrefToken(TRUE)];AdminFaxCreate=[REF(A.sent_by)];originfax=[REF(A.origin)];faxtype=[A.to_department];replyto=[REF(A.message)]'>Reply</A></td>"
html += "<td>N/A</td>"
else
html += "<td>N/A</td>"
html += "<td><A align='right' href='?src=[UID()];[HrefToken(TRUE)];AdminFaxView=[REF(A.reply_to)]'>Original</A></td>"
html += "</tr>"
html += "</table>"
html += "</div>"

html += "<div class='block'>"
html += "<h2>Departmental Faxes</h2>"
html += "<table>"
html += "<tr style='font-weight:bold;'><td width='150px'>Name</td><td width='150px'>From Department</td><td width='150px'>To Department</td><td width='75px'>Sent At</td><td width='150px'>Sent By</td><td width='175px'>View</td></td></tr>"
for(var/thing in GLOB.faxes)
var/datum/fax/F = thing
html += "<tr>"
html += "<td>[F.name]</td>"
html += "<td>[F.from_department]</td>"
html += "<td>[F.to_department]</td>"
html += "<td>[worldtime2text(F.sent_at)]</td>"
if(F.sent_by)
var/mob/living/S = F.sent_by
html += "<td><A HREF='?_src_=holder;[HrefToken(TRUE)];adminplayeropts=[REF(F.sent_by)]'>[S.name]</A></td>"
else
html += "<td>Unknown</td>"
html += "<td><A align='right' href='?src=[UID()];[HrefToken(TRUE)];AdminFaxView=[REF(F.message)]'>View</A></td>"
html += "</tr>"
html += "</table>"
html += "</div>"

var/datum/browser/popup = new(user, "fax_panel", "Fax Panel", 950, 450)
popup.set_content(html)
popup.open()
Loading

0 comments on commit ed44d91

Please sign in to comment.