diff --git a/baystation12.dme b/baystation12.dme
index 5699e226a80..2355bffb6db 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -882,6 +882,8 @@
#include "code\modules\customitems\item_spawning.dm"
#include "code\modules\DetectiveWork\detective_work.dm"
#include "code\modules\DetectiveWork\evidence.dm"
+#include "code\modules\DetectiveWork\footprints_and_rag.dm"
+#include "code\modules\DetectiveWork\scanner.dm"
#include "code\modules\flufftext\Dreaming.dm"
#include "code\modules\flufftext\Hallucination.dm"
#include "code\modules\flufftext\TextFilters.dm"
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index bf54f9bb601..8343001fc8b 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -1030,7 +1030,7 @@
icon_state = "cone"
/obj/item/weapon/paper
- name = "Paper"
+ name = "\improper Paper"
icon = 'paper.dmi'
icon_state = "paper"
var/info = null
diff --git a/code/game/atom_procs.dm b/code/game/atom_procs.dm
index e710ce0e136..26a7c6a5132 100644
--- a/code/game/atom_procs.dm
+++ b/code/game/atom_procs.dm
@@ -104,13 +104,6 @@
if(isnull(M.key)) return
if (!( src.flags ) & FPRINT)
return
- //Smudge up dem prints some
- for(var/P in fingerprints)
- var/test_print = stars(fingerprints[P], rand(85,95))
- if(stringpercent(test_print) == 32) //She's full of stars! (No actual print left)
- fingerprints.Remove(P)
- else
- fingerprints[P] = test_print
if (ishuman(M))
//Add the list if it does not exist.
if(!fingerprintshidden)
@@ -142,6 +135,15 @@
return 0
else if(H.gloves && !istype(H.gloves, /obj/item/clothing/gloves/latex) && !istype(H.gloves, /obj/item/clothing/gloves/fingerless))
return 0
+ //Smudge up dem prints some
+ for(var/P in fingerprints)
+ if(P == md5(H.dna.uni_identity))
+ continue
+ var/test_print = stars(fingerprints[P], rand(85,95))
+ if(stringpercent(test_print) == 32) //She's full of stars! (No actual print left)
+ fingerprints.Remove(P)
+ else
+ fingerprints[P] = test_print
//More adminstuffz
if(fingerprintslast != H.key)
fingerprintshidden += text("Real name: [], Key: []",H.real_name, H.key)
@@ -160,6 +162,13 @@
fingerprints[full_print] = stringmerge(print, stars(full_print, (H.gloves ? rand(10,20) : rand(25,40))))
return 1
else
+ //Smudge up dem prints some
+ for(var/P in fingerprints)
+ var/test_print = stars(fingerprints[P], rand(85,95))
+ if(stringpercent(test_print) == 32) //She's full of stars! (No actual print left)
+ fingerprints.Remove(P)
+ else
+ fingerprints[P] = test_print
if(fingerprintslast != M.key)
fingerprintshidden += text("Real name: [], Key: []",M.real_name, M.key)
fingerprintslast = M.key
diff --git a/code/game/machinery/kitchen/gibber.dm b/code/game/machinery/kitchen/gibber.dm
index c00d181d9f7..146867ffda8 100644
--- a/code/game/machinery/kitchen/gibber.dm
+++ b/code/game/machinery/kitchen/gibber.dm
@@ -181,7 +181,7 @@
allmeat += newmeat
for (var/mob/M in world)
- if (M.client && M.client.holder && (M.client.holder.level != -3))
+ if (M.client && M.client.holder && (M.client.holder.level != -3) && (M.client.holder.level != 0))
M << "\red [user.name]/[user.ckey] has gibbed [src.occupant.name]/[src.occupant.ckey]"
src.occupant.death(1)
src.occupant.ghostize()
diff --git a/code/game/objects/devices/scanners.dm b/code/game/objects/devices/scanners.dm
index 9e3775f3c6a..4d2a52722d3 100644
--- a/code/game/objects/devices/scanners.dm
+++ b/code/game/objects/devices/scanners.dm
@@ -61,167 +61,6 @@ MASS SPECTROMETER
M.invisibility = 2
-/obj/item/device/detective_scanner
- name = "Scanner"
- desc = "Used to scan objects for DNA and fingerprints."
- icon_state = "forensic1"
- var/amount = 20.0
- var/list/stored = list()
- w_class = 3.0
- item_state = "electronic"
- flags = FPRINT | TABLEPASS | CONDUCT | USEDELAY
- slot_flags = SLOT_BELT
-
- attackby(obj/item/weapon/f_card/W as obj, mob/user as mob)
- ..()
- if (istype(W, /obj/item/weapon/f_card))
- if (W.fingerprints)
- return
- if (src.amount == 20)
- return
- if (W.amount + src.amount > 20)
- src.amount = 20
- W.amount = W.amount + src.amount - 20
- else
- src.amount += W.amount
- //W = null
- del(W)
- add_fingerprint(user)
- if (W)
- W.add_fingerprint(user)
- return
-
- attack(mob/living/carbon/human/M as mob, mob/user as mob)
- if (!ishuman(M))
- user << "\red [M] is not human and cannot have the fingerprints."
- return 0
- if (( !( istype(M.dna, /datum/dna) ) || M.gloves) )
- user << "\blue No fingerprints found on [M]"
- return 0
- else
- if (src.amount < 1)
- user << text("\blue Fingerprints scanned on [M]. Need more cards to print.")
- else
- src.amount--
- var/obj/item/weapon/f_card/F = new /obj/item/weapon/f_card( user.loc )
- F.amount = 1
- F.add_fingerprint(M)
- F.icon_state = "fingerprint1"
- F.name = text("FPrintC- '[M.name]'")
-
- user << "\blue Done printing."
- user << "\blue [M]'s Fingerprints: [md5(M.dna.uni_identity)]"
- if ( !M.blood_DNA || !M.blood_DNA.len )
- user << "\blue No blood found on [M]"
- if(M.blood_DNA)
- del(M.blood_DNA)
- else
- user << "\blue Blood found on [M]. Analysing..."
- spawn(15)
- for(var/blood in M.blood_DNA)
- user << "\blue Blood type: [M.blood_DNA[blood]]\nDNA: [blood]"
- return
-
- afterattack(atom/A as obj|turf|area, mob/user as mob)
- if(!in_range(A,user))
- return
- if(loc != user)
- return
- if(istype(A,/obj/machinery/computer/forensic_scanning)) //breaks shit.
- return
- if(istype(A,/obj/item/weapon/f_card))
- user << "The scanner displays on the screen: \"ERROR 43: Object on Excluded Object List.\""
- return
-
- add_fingerprint(user)
-
-
- //Special case for blood splaters.
- if (istype(A, /obj/effect/decal/cleanable/blood) || istype(A, /obj/effect/rune))
- if(!isnull(A.blood_DNA))
- for(var/blood in A.blood_DNA)
- user << "\blue Blood type: [A.blood_DNA[blood]]\nDNA: [blood]"
- return
-
- //General
- if ((!A.fingerprints || !A.fingerprints.len) && !A.suit_fibers && !A.blood_DNA)
- user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.get_gender_form("it")] humming[prob(70) ? " gently." : "."]" ,\
- "\blue Unable to locate any fingerprints, materials, fibers, or blood on [A]!",\
- "You hear a faint hum of electrical equipment.")
- return 0
-
- if(add_data(A))
- user << "\blue Object already in internal memory. Consolidating data..."
- return
-
-
- //PRINTS
- if(!A.fingerprints || !A.fingerprints.len)
- if(A.fingerprints)
- del(A.fingerprints)
- else
- user << "\blue Isolated [A.fingerprints.len] fingerprints: Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
-
- //FIBERS
- if(A.suit_fibers)
- user << "\blue Fibers/Materials Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
-
- //Blood
- if (A.blood_DNA)
- user << "\blue Blood found on [A]. Analysing..."
- spawn(15)
- for(var/blood in A.blood_DNA)
- user << "Blood type: \red [A.blood_DNA[blood]] \t \black DNA: \red [blood]"
- if(prob(5))
- user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.get_gender_form("it")] humming[prob(70) ? " gently." : "."]" ,\
- "You finish scanning \the [A].",\
- "You hear a faint hum of electrical equipment.")
- return 0
- else
- user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.get_gender_form("it")] humming[prob(70) ? " gently." : "."]\n[user.get_gender_form("It")] seems to perk up slightly at the readout." ,\
- "The results of the scan pique your interest.",\
- "You hear a faint hum of electrical equipment, and someone making a thoughtful noise.")
- return 0
- return
-
- proc/add_data(atom/A as mob|obj|turf|area)
- //I love hashtables.
- var/list/data_entry = stored["\ref [A]"]
- if(islist(data_entry)) //Yay, it was already stored!
- //Merge the fingerprints.
- var/list/data_prints = data_entry[1]
- for(var/print in A.fingerprints)
- var/merged_print = data_prints[print]
- if(!merged_print)
- data_prints[print] = A.fingerprints[print]
- else
- data_prints[print] = stringmerge(data_prints[print],A.fingerprints[print])
-
- //Now the fibers
- var/list/fibers = data_entry[2]
- if(!fibers)
- fibers = list()
- if(A.suit_fibers && A.suit_fibers.len)
- for(var/j = 1, j <= A.suit_fibers.len, j++) //Fibers~~~
- if(!fibers.Find(A.suit_fibers[j])) //It isn't! Add!
- fibers += A.suit_fibers[j]
- var/list/blood = data_entry[3]
- if(!blood)
- blood = list()
- if(A.blood_DNA && A.blood_DNA.len)
- for(var/main_blood in A.blood_DNA)
- if(!blood[main_blood])
- blood[main_blood] = A.blood_DNA[blood]
- return 1
- var/list/sum_list[4] //Pack it back up!
- sum_list[1] = A.fingerprints
- sum_list[2] = A.suit_fibers
- sum_list[3] = A.blood_DNA
- sum_list[4] = "\The [A] in \the [get_area(A)]"
- stored["\ref [A]"] = sum_list
- return 0
-
-
/obj/item/device/healthanalyzer
name = "Health Analyzer"
icon_state = "health"
diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm
index 4b4d39d32b3..d138c6ece49 100644
--- a/code/game/verbs/ooc.dm
+++ b/code/game/verbs/ooc.dm
@@ -37,11 +37,13 @@
log_ooc("[src.name]/[src.key] : [msg]")
for (var/client/C)
- if (src.client.holder && (!src.client.stealth || C.holder))
+ if (src.client.holder && (!src.client.stealth || ( C.holder && C.holder.level != 0)))
if (src.client.holder.rank == "Admin Observer")
C << "OOC: [src.key][src.client.stealth ? "/([src.client.fakekey])" : ""]: [msg]"
else if (src.client.holder.rank == "Retired Admin")
C << "OOC: [src.key][src.client.stealth ? "/([src.client.fakekey])" : ""]: [msg]"
+ else if (src.client.holder.rank == "Moderator")
+ C << "OOC: [src.key][src.client.stealth ? "/([src.client.fakekey])" : ""]: [msg]"
else if (src.client.holder.level >= 5)
C << "OOC: [src.key][src.client.stealth ? "/([src.client.fakekey])" : ""]: [msg]"
else
diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm
index eb45379de53..26ac9307b61 100644
--- a/code/game/verbs/who.dm
+++ b/code/game/verbs/who.dm
@@ -30,7 +30,7 @@
for (var/mob/M in world)
if(M && M.client && M.client.holder)
- if(usr.client.holder)
+ if(usr.client.holder && (usr.client.holder.level != 0))
usr << "[M.key] is a [M.client.holder.rank][M.client.stealth ? " (as [M.client.fakekey])" : ""]"
else if(!M.client.stealth && (M.client.holder.level != -3))
usr << "\t[pick(nobles)] [M.client] is a [M.client.holder.rank]"
diff --git a/code/modules/DetectiveWork/detective_work.dm b/code/modules/DetectiveWork/detective_work.dm
index 4c9b20b7281..37524d9c691 100644
--- a/code/modules/DetectiveWork/detective_work.dm
+++ b/code/modules/DetectiveWork/detective_work.dm
@@ -1,4 +1,4 @@
-//gloves w_uniform wear_suit shoes
+//CONTAINS: Suit fibers and Detective's Scanning Computer
atom/var/list/suit_fibers
@@ -52,7 +52,7 @@ var/const/FINGERPRINT_COMPLETE = 6 //This is the output of the stringpercent(pri
//the print must be there for it to be complete. (Prints are 32 digits)
obj/machinery/computer/forensic_scanning
- name = "High-Res Forensic Scanning Computer"
+ name = "\improper High-Res Forensic Scanning Computer"
icon_state = "forensic"
var
obj/item/scanning
@@ -136,9 +136,6 @@ obj/machinery/computer/forensic_scanning
return
if (allowed(M))
authenticated = 1
- //usr << "\green Access Granted"
- //if(!authenticated)
- //usr << "\red Access Denied"
if("logout")
authenticated = 0
if("clear")
@@ -197,10 +194,9 @@ obj/machinery/computer/forensic_scanning
if(files && files.len)
temp = "Criminal Evidence Database
"
temp += "Consolidated data points:
"
- var/i = 1
for(var/print in files)
- temp += "{Dossier [i]}
"
- i++
+ var/list/file = files[print]
+ temp += "{[file[2]]}
"
temp += "
{Insert Finger Print Card (To complete a Dossier)}
"
else
temp = ""
@@ -213,22 +209,37 @@ obj/machinery/computer/forensic_scanning
if("record") //Viewing a record from the "files" database.
canclear = 0
if(files)
- temp = "Criminal Evidence Database
"
- temp += "Consolidated data points: Dossier [files.Find(href_list["identifier"])]
"
var/list/dossier = files[href_list["identifier"]]
+ if(href_list["ren"])
+ var/new_title = copytext(sanitize(input("Rename to what?", "Dossier Editing", "Dossier [files.Find(href_list["identifier"])]") as null|text),1,MAX_MESSAGE_LEN)
+ if(new_title)
+ dossier[2] = new_title
+ else
+ usr << "Illegal or blank name."
+ temp = "Criminal Evidence Database
"
+ temp += "Consolidated data points: [dossier[2]]
"
var/print_string = "Fingerprints: Print not complete!
"
if(stringpercent(dossier[1]) <= FINGERPRINT_COMPLETE)
print_string = "Fingerprints: (80% or higher completion reached)
[dossier[1]]
"
temp += print_string
for(var/object in dossier)
- if(object == dossier[1])
+ if(object == dossier[1] || object == dossier[2])
continue
temp += "
"
var/list/outputs = dossier[object]
- var/list/prints_len = outputs[1]
+ var/list/prints = outputs[1]
temp += "Object: [outputs[4]]
"
temp += " Fingerprints:
"
- temp += " [prints_len.len] Unique fingerprints found.
"
+ temp += " [prints.len] Unique fingerprints found.
"
+ var/complete_prints = 0
+ for(var/print in prints)
+ if(stringpercent(prints[print]) <= FINGERPRINT_COMPLETE)
+ complete_prints++
+ temp += " [prints[print]]
"
+ if(complete_prints)
+ temp += " And [prints.len - complete_prints] unknown unique prints.
"
+ else
+ temp += " No prints of sufficient completeness.
"
var/list/fibers = outputs[2]
if(fibers && fibers.len)
temp += " Fibers:
"
@@ -239,6 +250,7 @@ obj/machinery/computer/forensic_scanning
temp += " Blood:
"
for(var/named in blood)
temp += " Type: [blood[named]], DNA: [named]
"
+ temp += "
{Rename this Dossier}"
temp += "
{Delete this Dossier}"
temp += "
{Print}"
else
@@ -247,24 +259,33 @@ obj/machinery/computer/forensic_scanning
if("databaseprint") //Printing from the "files" database.
if(files)
var/obj/item/weapon/paper/P = new(loc)
- P.name = "Database File (Dossier [files.Find(href_list["identifier"])])"
+ P.name = "\improper Database File (Dossier [files.Find(href_list["identifier"])])"
P.overlays += "paper_words"
P.info = "Criminal Evidence Database
"
- P.info += "Consolidated data points: Dossier [href_list["identifier"]]
"
var/list/dossier = files[href_list["identifier"]]
+ P.info += "Consolidated data points: [dossier[2]]
"
var/print_string = "Fingerprints: Print not complete!
"
if(stringpercent(dossier[1]) <= FINGERPRINT_COMPLETE)
print_string = "Fingerprints: (80% or higher completion reached)
[dossier[1]]
"
P.info += print_string
for(var/object in dossier)
- if(object == dossier[1])
+ if(object == dossier[1] || object == dossier[2])
continue
P.info += "
"
var/list/outputs = dossier[object]
- var/list/prints_len = outputs[1]
+ var/list/prints = outputs[1]
P.info += "Object: [outputs[4]]
"
P.info += " Fingerprints:
"
- P.info += " [prints_len.len] Unique fingerprints found.
"
+ P.info += " [prints.len] Unique fingerprints found.
"
+ var/complete_prints = 0
+ for(var/print in prints)
+ if(stringpercent(prints[print]) <= FINGERPRINT_COMPLETE)
+ complete_prints++
+ P.info += " [prints[print]]
"
+ if(complete_prints)
+ P.info += " And [prints.len - complete_prints] unknown unique prints.
"
+ else
+ P.info += " No prints of sufficient completeness.
"
var/list/fibers = outputs[2]
if(fibers && fibers.len)
P.info += " Fibers:
"
@@ -283,11 +304,24 @@ obj/machinery/computer/forensic_scanning
temp = "Auxiliary Evidence Database
"
var/list/outputs = misc[href_list["identifier"]]
temp += "Consolidated data points: [outputs[3]]
"
+ var/list/prints = outputs[4]
+ if(prints)
+ temp += " Fingerprints:
"
+ temp += " [prints.len] Unique fingerprints found.
"
+ var/complete_prints = 0
+ for(var/print in prints)
+ if(stringpercent(prints[print]) <= FINGERPRINT_COMPLETE)
+ complete_prints++
+ temp += " [prints[print]]
"
+ if(complete_prints)
+ temp += " And [prints.len - complete_prints] unknown unique prints.
"
+ else
+ temp += " No prints of sufficient completeness.
"
var/list/fibers = outputs[1]
if(fibers && fibers.len)
temp += " Fibers:
"
- for(var/j = 1, j <= fibers.len, j++)
- temp += " [fibers[j]]
"
+ for(var/fiber in fibers)
+ temp += " [fiber]
"
var/list/blood = outputs[2]
if(blood && blood.len)
temp += " Blood:
"
@@ -302,15 +336,28 @@ obj/machinery/computer/forensic_scanning
if(misc)
var/obj/item/weapon/paper/P = new(loc)
var/list/outputs = misc[href_list["identifier"]]
- P.name = "Auxiliary Database File ([outputs[3]])"
+ P.name = "\improper Auxiliary Database File ([outputs[3]])"
P.overlays += "paper_words"
P.info = "Auxiliary Evidence Database
"
P.info += "Consolidated data points: [outputs[3]]
"
+ var/list/prints = outputs[4]
+ if(prints)
+ P.info += " Fingerprints:
"
+ P.info += " [prints.len] Unique fingerprints found.
"
+ var/complete_prints = 0
+ for(var/print in prints)
+ if(stringpercent(prints[print]) <= FINGERPRINT_COMPLETE)
+ complete_prints++
+ P.info += " [prints[print]]
"
+ if(complete_prints)
+ P.info += " And [prints.len - complete_prints] unknown unique prints.
"
+ else
+ P.info += " No prints of sufficient completeness.
"
var/list/fibers = outputs[1]
if(fibers && fibers.len)
P.info += " Fibers:
"
- for(var/j = 1, j <= fibers.len, j++)
- P.info += " [fibers[j]]
"
+ for(var/fiber in fibers)
+ P.info += " [fiber]
"
var/list/blood = outputs[2]
if(blood && blood.len)
P.info += " Blood:
"
@@ -361,36 +408,17 @@ obj/machinery/computer/forensic_scanning
if(!scanning.fingerprints)
scan_data += "No Fingerprints Found
"
else
- var/list/L = scanning.fingerprints
- scan_data += "Isolated [L.len] Fingerprints. Loaded into database.
"
+ scan_data += "Isolated [scanning.fingerprints.len] Fingerprints. Loaded into database.
"
add_data(scanning)
if(!scanning.suit_fibers)
- /*if(istype(scanning,/obj/item/device/detective_scanner))
- var/obj/item/device/detective_scanner/scanner = scanning
- if(scanner.stored_name)
- scan_data += "Fibers/Materials Data - [scanner.stored_name]:
"
- for(var/data in scanner.stored_fibers)
- scan_data += "- [data]
"
- else
- scan_data += "No Fibers/Materials Data
"
- else*/
scan_data += "No Fibers/Materials Located
"
else
- /*if(istype(scanning,/obj/item/device/detective_scanner))
- var/obj/item/device/detective_scanner/scanner = scanning
- if(scanner.stored_name)
- scan_data += "Fibers/Materials Data - [scanner.stored_name]:
"
- for(var/data in scanner.stored_fibers)
- scan_data += "- [data]
"
- else
- scan_data += "No Fibers/Materials Data
"*/
-
scan_data += "Fibers/Materials Found:
"
for(var/data in scanning.suit_fibers)
scan_data += "- [data]
"
if(istype(scanning,/obj/item/device/detective_scanner) || (istype(scanning, /obj/item/device/pda) && scanning:cartridge && scanning:cartridge.access_security))
- scan_data += "
Data transfered from Scanner to Database.
"
+ scan_data += "
Data transfered from \the [scanning] to Database.
"
add_data_scanner(scanning)
else if(!scanning.fingerprints)
scan_data += "
Add to Database?
"
@@ -402,7 +430,7 @@ obj/machinery/computer/forensic_scanning
if(scan_data)
temp = "Scan Data Printed."
var/obj/item/weapon/paper/P = new(loc)
- P.name = "Scan Data ([scan_name])"
+ P.name = "\improper Scan Data ([scan_name])"
P.info = "[scan_data]"
P.overlays += "paper_words"
else
@@ -416,6 +444,14 @@ obj/machinery/computer/forensic_scanning
add_data(scanning)
else
temp = "Data Transfer Failed: No Object."
+ if("rename")
+ if(!files || !files[href_list["identifier"]])
+ temp = "ERROR: Record/Database not found!"
+ else
+ var/new_title = copytext(sanitize(input("Rename to what?", "Dossier Editing", "Dossier [files.Find(href_list["identifier"])]") as null|text),1,MAX_MESSAGE_LEN)
+ if(new_title)
+ var/list/file = files[href_list["identifier"]]
+ file[2] = new_title
updateUsrDialog()
ex_act()
@@ -439,7 +475,7 @@ obj/machinery/computer/forensic_scanning
proc/add_data(var/atom/scanned_atom)
return add_data_master("\ref [scanned_atom]", scanned_atom.fingerprints,\
- scanned_atom.suit_fibers, scanned_atom.blood_DNA, scanned_atom.name)
+ scanned_atom.suit_fibers, scanned_atom.blood_DNA, "[scanned_atom.name] (Direct Scan)")
@@ -458,9 +494,9 @@ obj/machinery/computer/forensic_scanning
if(!fibers)
fibers = list()
if(atom_suit_fibers)
- for(var/j = 1, j <= atom_suit_fibers.len, j++) //Fibers~~~
- if(!fibers.Find(atom_suit_fibers[j])) //It isn't! Add!
- fibers += atom_suit_fibers[j]
+ for(var/fiber in atom_suit_fibers) //Fibers~~~
+ if(!fibers.Find(fiber)) //It isn't! Add!
+ fibers += fiber
var/list/blood = data_entry[2]
if(!blood)
blood = list()
@@ -468,11 +504,19 @@ obj/machinery/computer/forensic_scanning
for(var/main_blood in atom_blood_DNA)
if(!blood[main_blood])
blood[main_blood] = atom_blood_DNA[blood]
+ var/list/prints = data_entry[4]
+ if(!prints && atom_fingerprints)
+ prints = list()
+ if(atom_fingerprints)
+ for(var/print in atom_fingerprints)
+ if(!prints[print])
+ prints[print] = atom_fingerprints[print]
else
- var/list/templist[3]
+ var/list/templist[4]
templist[1] = atom_suit_fibers
templist[2] = atom_blood_DNA
templist[3] = atom_name
+ templist[4] = atom_fingerprints
misc[atom_reference] = templist //Store it!
//Has prints.
if(atom_fingerprints)
@@ -500,9 +544,9 @@ obj/machinery/computer/forensic_scanning
if(!fibers)
fibers = list()
if(atom_suit_fibers)
- for(var/j = 1, j < atom_suit_fibers.len, j++) //Fibers~~~
- if(!fibers.Find(atom_suit_fibers[j])) //It isn't! Add!
- fibers += atom_suit_fibers[j]
+ for(var/fiber in atom_suit_fibers) //Fibers~~~
+ if(!fibers.Find(fiber)) //It isn't! Add!
+ fibers += fiber
//Blood.
var/list/blood = internal_atom[3]
if(!blood)
@@ -528,8 +572,9 @@ obj/machinery/computer/forensic_scanning
data_point[2] = atom_suit_fibers
data_point[3] = atom_blood_DNA
data_point[4] = atom_name
- var/list/new_file[1]
+ var/list/new_file[2]
new_file[1] = atom_fingerprints[main_print]
+ new_file[2] = "Dossier [files.len + 1]"
new_file[atom_reference] = data_point
files[main_print] = new_file
return 1
@@ -580,209 +625,3 @@ obj/machinery/computer/forensic_scanning
detective
icon_state = "old"
name = "PowerScan Mk.I"
-
-obj/item/clothing/shoes/var
- track_blood = 0
- mob/living/carbon/human/track_blood_mob
- track_blood_type
-mob/var
- bloody_hands = 0
- mob/living/carbon/human/bloody_hands_mob
- track_blood
- mob/living/carbon/human/track_blood_mob
- track_blood_type
-obj/item/clothing/gloves/var
- transfer_blood = 0
- mob/living/carbon/human/bloody_hands_mob
-
-
-obj/effect/decal/cleanable/var
- track_amt = 3
- mob/blood_owner
-
-turf/Exited(mob/living/carbon/human/M)
- if(istype(M,/mob/living) && !istype(M,/mob/living/carbon/metroid))
- if(!istype(src, /turf/space)) // Bloody tracks code starts here
- var/dofoot = 1
- if(istype(M,/mob/living/simple_animal))
- if(!(istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/constructwraith)))
- dofoot = 0
-
- if(dofoot)
-
- if(!istype(src, /turf/space)) // Bloody tracks code starts here
- if(M.track_blood > 0)
- M.track_blood--
- src.add_bloody_footprints(M.track_blood_mob,1,M.dir,get_tracks(M),M.track_blood_type)
- else if(istype(M,/mob/living/carbon/human))
- if(M.shoes && istype(M.shoes,/obj/item/clothing/shoes))
- var/obj/item/clothing/shoes/S = M.shoes
- if(S.track_blood > 0)
- S.track_blood--
- src.add_bloody_footprints(S.track_blood_mob,1,M.dir,S.name,S.track_blood_type) // And bloody tracks end here
- . = ..()
-turf/Entered(mob/living/carbon/human/M)
- if(istype(M,/mob/living) && !istype(M,/mob/living/carbon/metroid))
- var/dofoot = 1
- if(istype(M,/mob/living/simple_animal))
- if(!(istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/constructwraith)))
- dofoot = 0
-
- if(dofoot)
-
- if(M.track_blood > 0)
- M.track_blood--
- src.add_bloody_footprints(M.track_blood_mob,0,M.dir,get_tracks(M),M.track_blood_type)
- else if(istype(M,/mob/living/carbon/human))
- if(M.shoes && istype(M.shoes,/obj/item/clothing/shoes) && !istype(src,/turf/space))
- var/obj/item/clothing/shoes/S = M.shoes
- if(S.track_blood > 0)
- S.track_blood--
- src.add_bloody_footprints(S.track_blood_mob,0,M.dir,S.name,S.track_blood_type)
-
-
- for(var/obj/effect/decal/cleanable/B in src)
- if(B:track_amt <= 0) continue
- if(B.type != /obj/effect/decal/cleanable/blood/tracks)
- if(istype(B, /obj/effect/decal/cleanable/xenoblood) || istype(B, /obj/effect/decal/cleanable/blood) || istype(B, /obj/effect/decal/cleanable/oil) || istype(B, /obj/effect/decal/cleanable/robot_debris))
-
- var/track_type = "blood"
- if(istype(B, /obj/effect/decal/cleanable/xenoblood))
- track_type = "xeno"
- else if(istype(B, /obj/effect/decal/cleanable/oil) || istype(B, /obj/effect/decal/cleanable/robot_debris))
- track_type = "oil"
-
- if(istype(M,/mob/living/carbon/human))
- if(M.shoes && istype(M.shoes,/obj/item/clothing/shoes))
- var/obj/item/clothing/shoes/S = M.shoes
- S.add_blood(B.blood_owner)
- S.track_blood_mob = B.blood_owner
- S.track_blood = max(S.track_blood,8)
- S.track_blood_type = track_type
- else
- M.add_blood(B.blood_owner)
- M.track_blood_mob = B.blood_owner
- M.track_blood = max(M.track_blood,rand(4,8))
- M.track_blood_type = track_type
- B.track_amt--
- break
- . = ..()
-
-turf/proc/add_bloody_footprints(mob/living/carbon/human/M,leaving,d,info,bloodcolor)
- for(var/obj/effect/decal/cleanable/blood/tracks/T in src)
- if(T.dir == d && findtext(T.icon_state, bloodcolor))
- if((leaving && T.icon_state == "steps2") || (!leaving && T.icon_state == "steps1"))
- T.desc = "These bloody footprints appear to have been made by [info]."
- if(!T.blood_DNA)
- T.blood_DNA = list()
- if(istype(M,/mob/living/carbon/human))
- T.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
- else if(istype(M,/mob/living/carbon/alien))
- T.blood_DNA["UNKNOWN DNA"] = "X*"
- else if(istype(M,/mob/living/carbon/monkey))
- T.blood_DNA["Non-human DNA"] = "A+"
- return
- var/obj/effect/decal/cleanable/blood/tracks/this = new(src)
- this.icon = 'footprints.dmi'
-
- var/preiconstate = ""
-
- if(info == "animal paws")
- preiconstate = "paw"
- else if(info == "alien claws")
- preiconstate = "claw"
- else if(info == "small alien feet")
- preiconstate = "paw"
-
- if(leaving)
- this.icon_state = "[bloodcolor][preiconstate]2"
- else
- this.icon_state = "[bloodcolor][preiconstate]1"
- this.dir = d
-
- if(bloodcolor == "blood")
- this.desc = "These bloody footprints appear to have been made by [info]."
- else if(bloodcolor == "xeno")
- this.desc = "These acidic bloody footprints appear to have been made by [info]."
- else if(bloodcolor == "oil")
- this.name = "oil"
- this.desc = "These oil footprints appear to have been made by [info]."
-
- if(istype(M,/mob/living/carbon/human))
- if(!this.blood_DNA)
- this.blood_DNA = list()
- this.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
-
-proc/get_tracks(mob/M)
- if(istype(M,/mob/living))
- if(istype(M,/mob/living/carbon/human))
- . = "human feet"
- else if(istype(M,/mob/living/carbon/monkey) || istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/crab))
- . = "animal paws"
- else if(istype(M,/mob/living/silicon/robot))
- . = "robot feet"
- else if(istype(M,/mob/living/carbon/alien/humanoid))
- . = "alien claws"
- else if(istype(M,/mob/living/carbon/alien/larva))
- . = "small alien feet"
- else
- . = "an unknown creature"
-
-
-proc/blood_incompatible(donor,receiver)
- var
- donor_antigen = copytext(donor,1,lentext(donor))
- receiver_antigen = copytext(receiver,1,lentext(receiver))
- donor_rh = findtext("+",donor)
- receiver_rh = findtext("+",receiver)
- if(donor_rh && !receiver_rh) return 1
- switch(receiver_antigen)
- if("A")
- if(donor_antigen != "A" && donor_antigen != "O") return 1
- if("B")
- if(donor_antigen != "B" && donor_antigen != "O") return 1
- if("O")
- if(donor_antigen != "O") return 1
- //AB is a universal receiver.
- return 0
-
-/obj/item/weapon/rag
- New() // So I don't have to grab maplock
- spawn(1)
- new/obj/item/weapon/reagent_containers/glass/rag(loc)
- del src
-
-/obj/item/weapon/reagent_containers/glass/rag
- name = "damp rag"
- desc = "For cleaning up messes, you suppose."
- w_class = 1
- icon = 'toy.dmi'
- icon_state = "rag"
- amount_per_transfer_from_this = 5
- possible_transfer_amounts = list(5)
- volume = 5
- can_be_placed_into = null
-
- attack(atom/target as obj|turf|area, mob/user as mob , flag)
- if(ismob(target) && target.reagents && reagents.total_volume)
- user.visible_message("\red \The [target] has been smothered with \the [src] by \the [user]!", "\red You smother \the [target] with \the [src]!", "You hear some struggling and muffled cries of surprise")
- src.reagents.reaction(target, TOUCH)
- spawn(5) src.reagents.clear_reagents()
- return
- else
- ..()
-
- afterattack(atom/A as obj|turf|area, mob/user as mob)
- if(istype(A) && src in user)
- user.visible_message("[user] starts to wipe down [A] with [src]!")
- if(do_after(user,30))
- user.visible_message("[user] finishes wiping off the [A]!")
- A.clean_blood()
- return
-
- examine()
- if (!usr)
- return
- usr << "That's \a [src]."
- usr << desc
- return
diff --git a/code/modules/DetectiveWork/evidence.dm b/code/modules/DetectiveWork/evidence.dm
index a35a4982a2d..8853efb2be6 100644
--- a/code/modules/DetectiveWork/evidence.dm
+++ b/code/modules/DetectiveWork/evidence.dm
@@ -1,6 +1,5 @@
-//CONTAINS:
-//Evidence bags and stuff
-///////////
+//CONTAINS: Evidence bags
+
/obj/item/weapon/evidencebag
name = "evidence bag"
desc = "An empty evidence bag."
diff --git a/code/modules/DetectiveWork/footprints_and_rag.dm b/code/modules/DetectiveWork/footprints_and_rag.dm
new file mode 100644
index 00000000000..83e49347897
--- /dev/null
+++ b/code/modules/DetectiveWork/footprints_and_rag.dm
@@ -0,0 +1,207 @@
+//CONTAINS: Bloody footprints code, code for managing blood incompatability, and a rag to wipe away evidence.
+
+obj/item/clothing/shoes/var
+ track_blood = 0
+ mob/living/carbon/human/track_blood_mob
+ track_blood_type
+mob/var
+ bloody_hands = 0
+ mob/living/carbon/human/bloody_hands_mob
+ track_blood
+ mob/living/carbon/human/track_blood_mob
+ track_blood_type
+obj/item/clothing/gloves/var
+ transfer_blood = 0
+ mob/living/carbon/human/bloody_hands_mob
+
+
+obj/effect/decal/cleanable/var
+ track_amt = 3
+ mob/blood_owner
+
+turf/Exited(mob/living/carbon/human/M)
+ if(istype(M,/mob/living) && !istype(M,/mob/living/carbon/metroid))
+ if(!istype(src, /turf/space)) // Bloody tracks code starts here
+ var/dofoot = 1
+ if(istype(M,/mob/living/simple_animal))
+ if(!(istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/constructwraith)))
+ dofoot = 0
+
+ if(dofoot)
+
+ if(!istype(src, /turf/space)) // Bloody tracks code starts here
+ if(M.track_blood > 0)
+ M.track_blood--
+ src.add_bloody_footprints(M.track_blood_mob,1,M.dir,get_tracks(M),M.track_blood_type)
+ else if(istype(M,/mob/living/carbon/human))
+ if(M.shoes && istype(M.shoes,/obj/item/clothing/shoes))
+ var/obj/item/clothing/shoes/S = M.shoes
+ if(S.track_blood > 0)
+ S.track_blood--
+ src.add_bloody_footprints(S.track_blood_mob,1,M.dir,S.name,S.track_blood_type) // And bloody tracks end here
+ . = ..()
+turf/Entered(mob/living/carbon/human/M)
+ if(istype(M,/mob/living) && !istype(M,/mob/living/carbon/metroid))
+ var/dofoot = 1
+ if(istype(M,/mob/living/simple_animal))
+ if(!(istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/constructwraith)))
+ dofoot = 0
+
+ if(dofoot)
+
+ if(M.track_blood > 0)
+ M.track_blood--
+ src.add_bloody_footprints(M.track_blood_mob,0,M.dir,get_tracks(M),M.track_blood_type)
+ else if(istype(M,/mob/living/carbon/human))
+ if(M.shoes && istype(M.shoes,/obj/item/clothing/shoes) && !istype(src,/turf/space))
+ var/obj/item/clothing/shoes/S = M.shoes
+ if(S.track_blood > 0)
+ S.track_blood--
+ src.add_bloody_footprints(S.track_blood_mob,0,M.dir,S.name,S.track_blood_type)
+
+
+ for(var/obj/effect/decal/cleanable/B in src)
+ if(B:track_amt <= 0) continue
+ if(B.type != /obj/effect/decal/cleanable/blood/tracks)
+ if(istype(B, /obj/effect/decal/cleanable/xenoblood) || istype(B, /obj/effect/decal/cleanable/blood) || istype(B, /obj/effect/decal/cleanable/oil) || istype(B, /obj/effect/decal/cleanable/robot_debris))
+
+ var/track_type = "blood"
+ if(istype(B, /obj/effect/decal/cleanable/xenoblood))
+ track_type = "xeno"
+ else if(istype(B, /obj/effect/decal/cleanable/oil) || istype(B, /obj/effect/decal/cleanable/robot_debris))
+ track_type = "oil"
+
+ if(istype(M,/mob/living/carbon/human))
+ if(M.shoes && istype(M.shoes,/obj/item/clothing/shoes))
+ var/obj/item/clothing/shoes/S = M.shoes
+ S.add_blood(B.blood_owner)
+ S.track_blood_mob = B.blood_owner
+ S.track_blood = max(S.track_blood,8)
+ S.track_blood_type = track_type
+ else
+ M.add_blood(B.blood_owner)
+ M.track_blood_mob = B.blood_owner
+ M.track_blood = max(M.track_blood,rand(4,8))
+ M.track_blood_type = track_type
+ B.track_amt--
+ break
+ . = ..()
+
+turf/proc/add_bloody_footprints(mob/living/carbon/human/M,leaving,d,info,bloodcolor)
+ for(var/obj/effect/decal/cleanable/blood/tracks/T in src)
+ if(T.dir == d && findtext(T.icon_state, bloodcolor))
+ if((leaving && T.icon_state == "steps2") || (!leaving && T.icon_state == "steps1"))
+ T.desc = "These bloody footprints appear to have been made by [info]."
+ if(!T.blood_DNA)
+ T.blood_DNA = list()
+ if(istype(M,/mob/living/carbon/human))
+ T.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
+ else if(istype(M,/mob/living/carbon/alien))
+ T.blood_DNA["UNKNOWN DNA"] = "X*"
+ else if(istype(M,/mob/living/carbon/monkey))
+ T.blood_DNA["Non-human DNA"] = "A+"
+ return
+ var/obj/effect/decal/cleanable/blood/tracks/this = new(src)
+ this.icon = 'footprints.dmi'
+
+ var/preiconstate = ""
+
+ if(info == "animal paws")
+ preiconstate = "paw"
+ else if(info == "alien claws")
+ preiconstate = "claw"
+ else if(info == "small alien feet")
+ preiconstate = "paw"
+
+ if(leaving)
+ this.icon_state = "[bloodcolor][preiconstate]2"
+ else
+ this.icon_state = "[bloodcolor][preiconstate]1"
+ this.dir = d
+
+ if(bloodcolor == "blood")
+ this.desc = "These bloody footprints appear to have been made by [info]."
+ else if(bloodcolor == "xeno")
+ this.desc = "These acidic bloody footprints appear to have been made by [info]."
+ else if(bloodcolor == "oil")
+ this.name = "oil"
+ this.desc = "These oil footprints appear to have been made by [info]."
+
+ if(istype(M,/mob/living/carbon/human))
+ if(!this.blood_DNA)
+ this.blood_DNA = list()
+ this.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
+
+proc/get_tracks(mob/M)
+ if(istype(M,/mob/living))
+ if(istype(M,/mob/living/carbon/human))
+ . = "human feet"
+ else if(istype(M,/mob/living/carbon/monkey) || istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/crab))
+ . = "animal paws"
+ else if(istype(M,/mob/living/silicon/robot))
+ . = "robot feet"
+ else if(istype(M,/mob/living/carbon/alien/humanoid))
+ . = "alien claws"
+ else if(istype(M,/mob/living/carbon/alien/larva))
+ . = "small alien feet"
+ else
+ . = "an unknown creature"
+
+
+proc/blood_incompatible(donor,receiver)
+ var
+ donor_antigen = copytext(donor,1,lentext(donor))
+ receiver_antigen = copytext(receiver,1,lentext(receiver))
+ donor_rh = findtext("+",donor)
+ receiver_rh = findtext("+",receiver)
+ if(donor_rh && !receiver_rh) return 1
+ switch(receiver_antigen)
+ if("A")
+ if(donor_antigen != "A" && donor_antigen != "O") return 1
+ if("B")
+ if(donor_antigen != "B" && donor_antigen != "O") return 1
+ if("O")
+ if(donor_antigen != "O") return 1
+ //AB is a universal receiver.
+ return 0
+
+/obj/item/weapon/rag
+ New() // So I don't have to grab maplock
+ spawn(1)
+ new/obj/item/weapon/reagent_containers/glass/rag(loc)
+ del src
+
+/obj/item/weapon/reagent_containers/glass/rag
+ name = "damp rag"
+ desc = "For cleaning up messes, you suppose."
+ w_class = 1
+ icon = 'toy.dmi'
+ icon_state = "rag"
+ amount_per_transfer_from_this = 5
+ possible_transfer_amounts = list(5)
+ volume = 5
+ can_be_placed_into = null
+
+ attack(atom/target as obj|turf|area, mob/user as mob , flag)
+ if(ismob(target) && target.reagents && reagents.total_volume)
+ user.visible_message("\red \The [target] has been smothered with \the [src] by \the [user]!", "\red You smother \the [target] with \the [src]!", "You hear some struggling and muffled cries of surprise")
+ src.reagents.reaction(target, TOUCH)
+ spawn(5) src.reagents.clear_reagents()
+ return
+ else
+ ..()
+
+ afterattack(atom/A as obj|turf|area, mob/user as mob)
+ if(istype(A) && src in user)
+ user.visible_message("\The [user] starts to wipe down \a [A] with \a [src]!", "You start to wipe down \the [A].", "You hear a damp rag being rubbed against something.")
+ if(do_after(user,30))
+ user.visible_message("\The [user] finishes wiping off \a [A]!", "You finish wiping down \the [A].")
+ A.clean_blood()
+ return
+
+ examine()
+ if (!usr)
+ return
+ usr << "That's \a [src]."
+ usr << desc
+ return
\ No newline at end of file
diff --git a/code/modules/DetectiveWork/scanner.dm b/code/modules/DetectiveWork/scanner.dm
new file mode 100644
index 00000000000..65285684049
--- /dev/null
+++ b/code/modules/DetectiveWork/scanner.dm
@@ -0,0 +1,172 @@
+//CONTAINS: Detective's Scanner
+
+/obj/item/device/detective_scanner
+ name = "Scanner"
+ desc = "Used to scan objects for DNA and fingerprints."
+ icon_state = "forensic1"
+ var/amount = 20.0
+ var/list/stored = list()
+ w_class = 3.0
+ item_state = "electronic"
+ flags = FPRINT | TABLEPASS | CONDUCT | USEDELAY
+ slot_flags = SLOT_BELT
+
+ attackby(obj/item/weapon/f_card/W as obj, mob/user as mob)
+ ..()
+ if (istype(W, /obj/item/weapon/f_card))
+ if (W.fingerprints)
+ return
+ if (src.amount == 20)
+ return
+ if (W.amount + src.amount > 20)
+ src.amount = 20
+ W.amount = W.amount + src.amount - 20
+ else
+ src.amount += W.amount
+ //W = null
+ del(W)
+ add_fingerprint(user)
+ if (W)
+ W.add_fingerprint(user)
+ return
+
+ attack(mob/living/carbon/human/M as mob, mob/user as mob)
+ if (!ishuman(M))
+ user << "\red [M] is not human and cannot have the fingerprints."
+ return 0
+ if (( !( istype(M.dna, /datum/dna) ) || M.gloves) )
+ user << "\blue No fingerprints found on [M]"
+ return 0
+ else
+ if (src.amount < 1)
+ user << text("\blue Fingerprints scanned on [M]. Need more cards to print.")
+ else
+ src.amount--
+ var/obj/item/weapon/f_card/F = new /obj/item/weapon/f_card( user.loc )
+ F.amount = 1
+ F.add_fingerprint(M)
+ F.icon_state = "fingerprint1"
+ F.name = text("FPrintC- '[M.name]'")
+
+ user << "\blue Done printing."
+ user << "\blue [M]'s Fingerprints: [md5(M.dna.uni_identity)]"
+ if ( !M.blood_DNA || !M.blood_DNA.len )
+ user << "\blue No blood found on [M]"
+ if(M.blood_DNA)
+ del(M.blood_DNA)
+ else
+ user << "\blue Blood found on [M]. Analysing..."
+ spawn(15)
+ for(var/blood in M.blood_DNA)
+ user << "\blue Blood type: [M.blood_DNA[blood]]\nDNA: [blood]"
+ return
+
+ afterattack(atom/A as obj|turf|area, mob/user as mob)
+ if(!in_range(A,user))
+ return
+ if(loc != user)
+ return
+ if(istype(A,/obj/machinery/computer/forensic_scanning)) //breaks shit.
+ return
+ if(istype(A,/obj/item/weapon/f_card))
+ user << "The scanner displays on the screen: \"ERROR 43: Object on Excluded Object List.\""
+ return
+
+ add_fingerprint(user)
+
+
+ //Special case for blood splaters.
+ if (istype(A, /obj/effect/decal/cleanable/blood) || istype(A, /obj/effect/rune))
+ if(!isnull(A.blood_DNA))
+ for(var/blood in A.blood_DNA)
+ user << "\blue Blood type: [A.blood_DNA[blood]]\nDNA: [blood]"
+ return
+
+ //General
+ if ((!A.fingerprints || !A.fingerprints.len) && !A.suit_fibers && !A.blood_DNA)
+ user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.get_gender_form("it")] humming[prob(70) ? " gently." : "."]" ,\
+ "\blue Unable to locate any fingerprints, materials, fibers, or blood on [A]!",\
+ "You hear a faint hum of electrical equipment.")
+ return 0
+
+ if(add_data(A))
+ user << "\blue Object already in internal memory. Consolidating data..."
+ return
+
+
+ //PRINTS
+ if(!A.fingerprints || !A.fingerprints.len)
+ if(A.fingerprints)
+ del(A.fingerprints)
+ else
+ user << "\blue Isolated [A.fingerprints.len] fingerprints: Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
+ var/list/complete_prints = list()
+ for(var/i in A.fingerprints)
+ var/print = A.fingerprints[i]
+ if(stringpercent(print) <= FINGERPRINT_COMPLETE)
+ complete_prints += print
+ if(complete_prints.len < 1)
+ user << "\blue No intact prints found"
+ else
+ user << "\blue Found [complete_prints.len] intact prints"
+ for(var/i in complete_prints)
+ user << "\blue [i]"
+
+ //FIBERS
+ if(A.suit_fibers)
+ user << "\blue Fibers/Materials Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
+
+ //Blood
+ if (A.blood_DNA)
+ user << "\blue Blood found on [A]. Analysing..."
+ spawn(15)
+ for(var/blood in A.blood_DNA)
+ user << "Blood type: \red [A.blood_DNA[blood]] \t \black DNA: \red [blood]"
+ if(prob(80) || !A.fingerprints)
+ user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.get_gender_form("it")] humming[prob(70) ? " gently." : "."]" ,\
+ "You finish scanning \the [A].",\
+ "You hear a faint hum of electrical equipment.")
+ return 0
+ else
+ user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.get_gender_form("it")] humming[prob(70) ? " gently." : "."]\n[user.get_gender_form("It")] seems to perk up slightly at the readout." ,\
+ "The results of the scan pique your interest.",\
+ "You hear a faint hum of electrical equipment, and someone making a thoughtful noise.")
+ return 0
+ return
+
+ proc/add_data(atom/A as mob|obj|turf|area)
+ //I love hashtables.
+ var/list/data_entry = stored["\ref [A]"]
+ if(islist(data_entry)) //Yay, it was already stored!
+ //Merge the fingerprints.
+ var/list/data_prints = data_entry[1]
+ for(var/print in A.fingerprints)
+ var/merged_print = data_prints[print]
+ if(!merged_print)
+ data_prints[print] = A.fingerprints[print]
+ else
+ data_prints[print] = stringmerge(data_prints[print],A.fingerprints[print])
+
+ //Now the fibers
+ var/list/fibers = data_entry[2]
+ if(!fibers)
+ fibers = list()
+ if(A.suit_fibers && A.suit_fibers.len)
+ for(var/j = 1, j <= A.suit_fibers.len, j++) //Fibers~~~
+ if(!fibers.Find(A.suit_fibers[j])) //It isn't! Add!
+ fibers += A.suit_fibers[j]
+ var/list/blood = data_entry[3]
+ if(!blood)
+ blood = list()
+ if(A.blood_DNA && A.blood_DNA.len)
+ for(var/main_blood in A.blood_DNA)
+ if(!blood[main_blood])
+ blood[main_blood] = A.blood_DNA[blood]
+ return 1
+ var/list/sum_list[4] //Pack it back up!
+ sum_list[1] = A.fingerprints
+ sum_list[2] = A.suit_fibers
+ sum_list[3] = A.blood_DNA
+ sum_list[4] = "\The [A] in \the [get_area(A)]"
+ stored["\ref [A]"] = sum_list
+ return 0
\ No newline at end of file
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index d0f0b72f56a..f0d7113b7d2 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -6,7 +6,7 @@ var/global/BSACooldown = 0
/proc/message_admins(var/text, var/admin_ref = 0)
var/rendered = "ADMIN LOG: [text]"
for (var/mob/M in world)
- if (M && M.client && M.client.holder && M.client.holder.level > -3) //Lets not spam our retirees.
+ if (M && M.client && M.client.holder && M.client.holder.level > -3 && M.client.holder.level != 0) //Lets not spam our retirees. Or moderators!
if (admin_ref)
M << dd_replaceText(rendered, "%admin_ref%", "\ref[M]")
else
@@ -176,6 +176,7 @@ var/global/BSACooldown = 0
var/datum/player_info/P = new
P.author = usr.key
+ P.rank = usr.client.holder.rank
P.content = add
var/modifyer = "th"
switch(time2text(world.timeofday, "DD"))
@@ -196,6 +197,9 @@ var/global/BSACooldown = 0
info << infos
+ message_admins("\blue [key_name_admin(usr)] has edited [key]'s notes.")
+ log_admin("[key_name(usr)] has edited [key]'s notes.")
+
del info
var/savefile/note_list = new("data/player_notes.sav")
@@ -221,6 +225,9 @@ var/global/BSACooldown = 0
infos.Remove(item)
info << infos
+ message_admins("\blue [key_name_admin(usr)] deleted one of [key]'s notes.")
+ log_admin("[key_name(usr)] deleted one of [key]'s notes.")
+
del info
show_player_info(key)
@@ -612,13 +619,6 @@ var/global/BSACooldown = 0
var/datum/job/temp = job_master.GetJob(jobPos)
if(!temp) continue
joblist += temp.title
- if("miscdept")
- joblist += "pAI"
- for(var/jobPos in nonhuman_positions)
- if(!jobPos) continue
- var/datum/job/temp = job_master.GetJob(jobPos)
- if(!temp) continue
- joblist += temp.title
else
joblist += href_list["jobban3"]
@@ -800,7 +800,7 @@ var/global/BSACooldown = 0
usr << browse(dat, "window=jobban2;size=600x250")
return
if(href_list["newjobban2"])
- if ((src.rank in list("Moderator", "Administrator", "Badmin", "Tyrant" )))
+ if ((src.rank in list(/*"Moderator", */"Administrator", "Badmin", "Tyrant" )))
var/mob/M = locate(href_list["jobban4"])
var/job = href_list["newjobban2"]
if(!ismob(M)) return
@@ -877,7 +877,7 @@ var/global/BSACooldown = 0
remove_goon(t)
*/
if (href_list["mute2"])
- if ((src.rank in list( "Moderator", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" )))
+ if ((src.rank in list(/* "Moderator", */"Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" )))
var/mob/M = locate(href_list["mute2"])
if (ismob(M))
if ((M.client && M.client.holder && (M.client.holder.level >= src.level)))
@@ -891,7 +891,7 @@ var/global/BSACooldown = 0
message_admins("\blue [key_name_admin(usr)] has [(M.client.muted ? "muted" : "voiced")] [key_name_admin(M)].", 1)
M << "You have been [(M.client.muted ? "muted" : "voiced")]. Please resolve this in adminhelp."
if (href_list["mute_complete"])
- if ((src.rank in list( "Moderator", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" )))
+ if ((src.rank in list(/* "Moderator", */"Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" )))
var/mob/M = locate(href_list["mute_complete"])
if (ismob(M))
if ((M.client && M.client.holder && (M.client.holder.level >= src.level)))
@@ -997,7 +997,7 @@ var/global/BSACooldown = 0
return
if (href_list["sendtoprison"])
- if ((src.rank in list( "Moderator", "Admin Candidate", "Temporary Admin", "Trial Admin", "Badmin", "Game Admin", "Game Master" )))
+ if ((src.rank in list(/* "Moderator", */"Admin Candidate", "Temporary Admin", "Trial Admin", "Badmin", "Game Admin", "Game Master" )))
var/confirm = alert(usr, "Send to admin prison for the round?", "Message", "Yes", "No")
if(confirm != "Yes")
@@ -1350,62 +1350,79 @@ var/global/BSACooldown = 0
// Now isn't that much better? IT IS NOW A PROC, i.e. kinda like a big panel like unstable
if (href_list["adminplayeropts"])
- var/mob/M = locate(href_list["adminplayeropts"])
- show_player_panel(M)
+ if(rank in list("Admin Observer", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master"))
+ var/mob/M = locate(href_list["adminplayeropts"])
+ show_player_panel(M)
+ else
+ alert("You cannot perform this action. You must be of a higher administrative rank!")
if (href_list["player_info"])
var/key = href_list["player_info"]
show_player_info(key)
if (href_list["adminplayervars"])
- var/mob/M = locate(href_list["adminplayervars"])
- if(src && src.owner)
- if(istype(src.owner,/client))
- var/client/cl = src.owner
- cl.debug_variables(M)
- else if(ismob(src.owner))
- var/mob/MO = src.owner
- if(MO.client)
- var/client/cl = MO.client
+ if(rank in list("Admin Observer", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master"))
+ var/mob/M = locate(href_list["adminplayervars"])
+ if(src && src.owner)
+ if(istype(src.owner,/client))
+ var/client/cl = src.owner
cl.debug_variables(M)
+ else if(ismob(src.owner))
+ var/mob/MO = src.owner
+ if(MO.client)
+ var/client/cl = MO.client
+ cl.debug_variables(M)
+ else
+ alert("You cannot perform this action. You must be of a higher administrative rank!")
if (href_list["adminplayersubtlemessage"])
- var/mob/M = locate(href_list["adminplayersubtlemessage"])
- if(src && src.owner)
- if(istype(src.owner,/client))
- var/client/cl = src.owner
- cl.cmd_admin_subtle_message(M)
- else if(ismob(src.owner))
- var/mob/MO = src.owner
- if(MO.client)
- var/client/cl = MO.client
+ if(rank in list("Admin Observer", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master"))
+ var/mob/M = locate(href_list["adminplayersubtlemessage"])
+ if(src && src.owner)
+ if(istype(src.owner,/client))
+ var/client/cl = src.owner
cl.cmd_admin_subtle_message(M)
+ else if(ismob(src.owner))
+ var/mob/MO = src.owner
+ if(MO.client)
+ var/client/cl = MO.client
+ cl.cmd_admin_subtle_message(M)
+ else
+ alert("You cannot perform this action. You must be of a higher administrative rank!")
if (href_list["adminplayerobservejump"])
- var/mob/M = locate(href_list["adminplayerobservejump"])
- if(src && src.owner)
- if(istype(src.owner,/client))
- var/client/cl = src.owner
- cl.admin_observe()
- sleep(2)
- cl.jumptomob(M)
- else if(ismob(src.owner))
- var/mob/MO = src.owner
- if(MO.client)
- var/client/cl = MO.client
+ if(rank in list("Admin Observer", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master"))
+ var/mob/M = locate(href_list["adminplayerobservejump"])
+ if(src && src.owner)
+ if(istype(src.owner,/client))
+ var/client/cl = src.owner
cl.admin_observe()
sleep(2)
cl.jumptomob(M)
+ else if(ismob(src.owner))
+ var/mob/MO = src.owner
+ if(MO.client)
+ var/client/cl = MO.client
+ cl.admin_observe()
+ sleep(2)
+ cl.jumptomob(M)
+ else
+ alert("You cannot perform this action. You must be of a higher administrative rank!")
if (href_list["traitor_panel_pp"])
- var/mob/M = locate(href_list["traitor_panel_pp"])
- if(isnull(M))
- usr << "Mob doesn't seem to exist."
- return
- if(!ismob(M))
- usr << "This doen't seem to be a mob."
- return
- show_traitor_panel(M)
+ if(rank in list("Admin Observer", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master"))
+ var/mob/M = locate(href_list["traitor_panel_pp"])
+ if(isnull(M))
+ usr << "Mob doesn't seem to exist."
+ return
+ if(!ismob(M))
+ usr << "This doen't seem to be a mob."
+ return
+ show_traitor_panel(M)
+ else
+ alert("You cannot perform this action. You must be of a higher administrative rank!")
+
+
if (href_list["BlueSpaceArtillery"])
var/mob/M = locate(href_list["BlueSpaceArtillery"])
@@ -2239,7 +2256,7 @@ var/global/BSACooldown = 0
return
if (href_list["secretsadmin"])
- if ((src.rank in list( "Moderator", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" )))
+ if ((src.rank in list(/* "Moderator", */"Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" )))
var/ok = 0
switch(href_list["secretsadmin"])
if("clear_bombs")
@@ -2380,7 +2397,7 @@ var/global/BSACooldown = 0
message_admins("\blue [user.ckey] warned [M.ckey].")
else
AddBan(M.ckey, M.computer_id, "Autobanning due to previous warn", user.ckey, 1, AUTOBANTIME)
- M << "\redYou have been autobanned by [user.ckey]. This is what we in the biz like to call a \"second warning\"."
+ M << "\redYou have been autobanned by [user.ckey]."
M << "\red This is a temporary ban; it will automatically be removed in [AUTOBANTIME] minutes."
log_admin("[user.ckey] warned [M.ckey], resulting in a [AUTOBANTIME] minute autoban.")
ban_unban_log_save("[user.ckey] warned [M.ckey], resulting in a [AUTOBANTIME] minute autoban.")
@@ -2428,6 +2445,7 @@ var/global/BSACooldown = 0
body += "Mob type = [M.type]
"
+ body += "Warn | "
body += "Kick | "
body += "Ban | "
body += "Jobban "
@@ -2527,10 +2545,11 @@ var/global/BSACooldown = 0
//feedback_add_details("admin_verb","SPP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/player_info/var
- author // admin who authored the information
- content // text content of the information
- timestamp // Because this is bloody annoying
+
+/datum/player_info/var/author // admin who authored the information
+/datum/player_info/var/rank //rank of admin who made the notes
+/datum/player_info/var/content // text content of the information
+/datum/player_info/var/timestamp // Because this is bloody annoying
/obj/admins/proc/player_has_info(var/key as text)
var/savefile/info = new("data/player_saves/[copytext(key, 1, 2)]/[key]/info.sav")
@@ -2563,7 +2582,10 @@ var/global/BSACooldown = 0
if(!I.timestamp)
I.timestamp = "Pre-4/3/2012"
update_file = 1
- dat += "[I.content] by [I.author] on [I.timestamp] "
+ if(!I.rank)
+ I.rank = "N/A"
+ update_file = 1
+ dat += "[I.content] by [I.author] ([I.rank]) on [I.timestamp] "
if(I.author == usr.key)
dat += "Remove"
dat += "
"
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index f7eec938d0e..270a9bfe08c 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -107,52 +107,32 @@
if (holder.level >= -1)
verbs += /client/proc/investigate_show
verbs += /client/proc/cmd_admin_say
+ verbs += /client/proc/cmd_mod_say
verbs += /client/proc/cmd_admin_gib_self
verbs += /client/proc/deadmin_self
else if (holder.level == -3) // Retired Admin
verbs += /client/proc/cmd_admin_say
+ verbs += /client/proc/cmd_mod_say
return
else return
//Moderator
if (holder.level >= 0)
- verbs += /obj/admins/proc/announce
- verbs += /obj/admins/proc/startnow
- verbs += /obj/admins/proc/toggleAI //Toggle the AI
- verbs += /obj/admins/proc/toggleenter //Toggle enterting
-// verbs += /obj/admins/proc/toggleguests //Toggle guests entering
- verbs += /obj/admins/proc/toggleooc //toggle ooc
- verbs += /obj/admins/proc/toggleoocdead //toggle ooc for dead/unc
- verbs += /obj/admins/proc/voteres //toggle votes
- verbs += /obj/admins/proc/vmode
- verbs += /obj/admins/proc/votekill
- verbs += /obj/admins/proc/show_player_panel
- verbs += /client/proc/deadchat //toggles deadchat
- //verbs += /client/proc/cmd_admin_mute --was never used (according to stats trackind) - use show player panel --erro
verbs += /client/proc/cmd_admin_pm_context
verbs += /client/proc/cmd_admin_pm_panel
- verbs += /client/proc/cmd_admin_subtle_message
- //verbs += /client/proc/warn - was never used
- verbs += /client/proc/dsay
verbs += /client/proc/admin_play
verbs += /client/proc/admin_observe
- verbs += /client/proc/game_panel
-// verbs += /client/proc/player_panel
- verbs += /client/proc/player_panel_new
- verbs += /client/proc/unban_panel
- verbs += /client/proc/jobbans
-
- verbs += /client/proc/voting
verbs += /client/proc/hide_verbs
- verbs += /client/proc/general_report
- //verbs += /client/proc/air_report
verbs += /client/proc/deadmin_self
- //verbs += /client/proc/cmd_admin_prison --Merged with player panel
- //verbs += /obj/admins/proc/unprison --Merged with player panel
- verbs += /client/proc/playernotes
verbs += /obj/admins/proc/show_skills
else return
+ if(holder.level == 0) //Moderators don't get asay, only msay
+ verbs -= /client/proc/cmd_admin_say
+ verbs -= /client/proc/investigate_show
+ verbs -= /client/proc/cmd_admin_gib_self
+ verbs += /client/proc/mod_panel
+
//Temporary Admin
if (holder.level >= 1)
verbs += /obj/admins/proc/delay //game start delay
@@ -165,6 +145,35 @@
verbs += /client/proc/deadmin_self
//verbs += /client/proc/cmd_admin_attack_log --Merged with view variables
verbs += /client/proc/cmd_admin_change_custom_event
+ //
+ //MOVED FROM MODERATOR
+ //
+ verbs += /client/proc/cmd_admin_subtle_message
+ verbs += /obj/admins/proc/announce
+ verbs += /obj/admins/proc/startnow
+ verbs += /obj/admins/proc/toggleAI //Toggle the AI
+ verbs += /obj/admins/proc/toggleenter //Toggle enterting
+// verbs += /obj/admins/proc/toggleguests //Toggle guests entering
+ verbs += /obj/admins/proc/toggleooc //toggle ooc
+ verbs += /obj/admins/proc/toggleoocdead //toggle ooc for dead/unc
+ verbs += /obj/admins/proc/voteres //toggle votes
+ verbs += /obj/admins/proc/vmode
+ verbs += /obj/admins/proc/votekill
+ verbs += /client/proc/deadchat //toggles deadchat
+ //verbs += /client/proc/cmd_admin_mute --was never used (according to stats trackind) - use show player panel --erro
+ verbs += /client/proc/dsay
+ verbs += /client/proc/game_panel
+ verbs += /obj/admins/proc/show_player_panel
+// verbs += /client/proc/player_panel
+ verbs += /client/proc/player_panel_new
+ verbs += /client/proc/unban_panel
+ verbs += /client/proc/jobbans
+ verbs += /client/proc/voting
+ verbs += /client/proc/general_report
+ //verbs += /client/proc/air_report
+ //verbs += /client/proc/cmd_admin_prison --Merged with player panel
+ //verbs += /obj/admins/proc/unprison --Merged with player panel
+ verbs += /client/proc/playernotes
else return
//Admin Candidate
@@ -393,8 +402,8 @@
verbs -= /client/proc/cmd_admin_pm_context
verbs -= /client/proc/cmd_admin_pm_panel
verbs -= /client/proc/cmd_admin_say
+ verbs -= /client/proc/cmd_mod_say
verbs -= /client/proc/cmd_admin_subtle_message
- //verbs -= /client/proc/warn
verbs -= /client/proc/dsay
verbs -= /client/proc/admin_play
verbs -= /client/proc/admin_observe
@@ -418,6 +427,7 @@
verbs -= /client/proc/toggle_hear_radio
verbs -= /client/proc/tension_report
verbs -= /client/proc/player_panel_new
+ verbs -= /client/proc/mod_panel
verbs -= /client/proc/toggle_gravity_on
verbs -= /client/proc/toggle_gravity_off
verbs -= /client/proc/toggle_random_events
@@ -428,7 +438,7 @@
verbs -= /client/proc/getserverlog
verbs -= /client/proc/cinematic //show a cinematic sequence
verbs -= /client/proc/admin_memo
- verbs -= /client/proc/investigate_show
+ verbs -= /client/proc/investigate_show
verbs -= /client/proc/cmd_admin_change_custom_event
verbs -= /client/proc/admin_invis
verbs -= /client/proc/callprocgen
@@ -538,6 +548,14 @@
// feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
+/client/proc/mod_panel()
+ set name = "Moderator Panel"
+ set category = "Admin"
+ if(holder)
+ holder.mod_panel()
+// feedback_add_details("admin_verb","MP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ return
+
/client/proc/check_antagonists()
set name = "Check Antagonists"
set category = "Admin"
@@ -836,6 +854,7 @@
verbs += /client/proc/deadchat //toggles deadchat
verbs += /obj/admins/proc/toggleooc //toggle ooc
verbs += /client/proc/cmd_admin_say//asay
+ verbs += /client/proc/cmd_mod_say
verbs += /client/proc/toggleadminhelpsound
// feedback_add_details("admin_verb","HMV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
@@ -856,6 +875,7 @@
verbs += /client/proc/deadchat //toggles deadchat
verbs += /obj/admins/proc/toggleooc //toggle ooc
verbs += /client/proc/cmd_admin_say//asay
+ verbs += /client/proc/cmd_mod_say//asay
// feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index 2c87cf9410a..408815774e9 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -113,6 +113,113 @@
usr << browse(dat, "window=players;size=905x600")
+
+/obj/admins/proc/mod_panel()//The new one
+ if (!usr.client.holder)
+ return
+ var/dat = "Player Menu"
+ dat += "| Name/Real Name | IP/CID | Info | Options |
"
+ //add IP: | to this if wanting to add back in IP checking Type | Assigned Job | Traitor? |
+ //add (IP: [M.lastKnownIP]) | if you want to know their ip to the lists below
+ var/list/mobs = get_sorted_mobs()
+ var/i = 1
+
+ for(var/mob/M in mobs)
+ if(M.ckey)
+ var/color = "#e6e6e6"
+ i++
+ if(i%2 == 0)
+ color = "#f2f2f2"
+ var/real = (M.real_name == M.original_name ? "[M.name]/[M.real_name]" : "[M.original_name] (as [M.name]/[M.real_name])")
+ var/turf/T = get_turf(M)
+ var/client_key = (M.key? M.key : "No key")
+ dat += "[real] [M.client ? M.client : "No client ([client_key])"] at ([T.x], [T.y], [T.z]) | " // Adds current name
+/* if(isobserver(M))
+ dat += "Ghost | "
+ else if(isalien(M))
+ dat += "Alien | "
+ else if(islarva(M))
+ dat += "Alien larva | "
+ else if(istajaran(M))
+ dat += "Tajaran | "
+ else if(ishuman(M))
+ dat += "[M.job] | "
+ else if(ismetroid(M))
+ dat += "Metroid | "
+ else if(ismonkey(M))
+ dat += "Monkey | "
+ else if(isAI(M))
+ dat += "AI | "
+ else if(ispAI(M))
+ dat += "pAI | "
+ else if(isrobot(M))
+ dat += "Cyborg | "
+ else if(isanimal(M))
+ dat += "Animal | "
+ else if(iscorgi(M))
+ dat += "Corgi | "
+ else if(istype(M,/mob/new_player))
+ dat += "New Player | "
+ else
+ dat += "\red ERROR | \black"
+
+ if(M.mind && M.mind.assigned_role && istype(M, /mob/living/carbon/human)) // Adds a column to Player Panel that shows their current job.
+ var/mob/living/carbon/human/H = M
+
+ if (H.wear_id)
+ var/obj/item/weapon/card/id/id
+
+ if(istype(H.wear_id, /obj/item/device/pda))
+ var/obj/item/device/pda/PDA = H.wear_id
+ if(!isnull(PDA.id)) // The PDA may contain no ID
+ id = PDA.id // The ID is contained inside the PDA
+
+ else
+ id = H.wear_id // The ID was on the ID slot
+
+ if(!id) // Happens when there's no ID in the PDA located on the wear_id slot
+ dat += "[M.mind.assigned_role] (No ID) | "
+
+ else if(isnull(id.assignment)) // Preventing runtime errors blocking the player panel
+ if(istype(id, /obj/item/weapon/card/id/syndicate))
+ dat += "Antagonist | "
+ else
+ usr << "ERROR: Inform the coders that an [id.name] was checked for its assignment variable, and it was null."
+ dat += "ERROR | "
+
+ else
+ if(M.mind.assigned_role == id.assignment) // Polymorph
+ dat += "[M.mind.assigned_role] | "
+
+ else
+ dat += "[M.mind.assigned_role] ([id.assignment])"
+
+ else
+ dat += " | [M.mind.assigned_role] (No ID) | "
+
+ else
+ dat += "No Assigned Role | "
+*/
+ dat += {"IP: [M.client.address]
+ CID: [M.client.computer_id] |
+ "}
+
+ dat += {"[player_has_info(M.ckey) ? "Info" : "N/A"] |
+ PM |
+ "}
+
+/* switch(is_special_character(M))
+ if(0)
+ dat += {"Traitor? | "}
+ if(1)
+ dat += {"Traitor? | "}
+ if(2)
+ dat += {"Traitor? | "}
+*/
+ dat += "
"
+
+ usr << browse(dat, "window=players;size=600x600")
+
//The old one
/obj/admins/proc/player_panel_old()
if (!usr.client.holder)
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 94bd6098f51..2c0f9eba325 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -61,8 +61,12 @@
if(C.holder)
if(holder) //both are admins
- C << "Admin PM from-[key_name(src, C, 1)]: [msg]"
- src << "Admin PM to-[key_name(C, src, 1)]: [msg]"
+ if(holder.rank == "Moderator") //If moderator
+ C << "Mod PM from-[key_name(src, C, 1)]: [msg]"
+ src << "Mod PM to-[key_name(C, src, 1)]: [msg]"
+ else
+ C << "Admin PM from-[key_name(src, C, 1)]: [msg]"
+ src << "Admin PM to-[key_name(C, src, 1)]: [msg]"
else //recipient is an admin but sender is not
C << "Reply PM from-[key_name(src, C, 1)]: [msg]"
@@ -75,10 +79,14 @@
else
if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT
// C << "-- Administrator private message --"
- C << "Admin PM from-[key_name(src, C, 0)]: [msg]"
- C << "Click on the administrator's name to reply."
- src << "Admin PM to-[key_name(C, src, 1)]: [msg]"
-
+ if(holder.rank == "Moderator")
+ C << "Mod PM from-[key_name(src, C, 0)]: [msg]"
+ C << "Click on the moderators's name to reply."
+ src << "Mod PM to-[key_name(C, src, 1)]: [msg]"
+ else
+ C << "Admin PM from-[key_name(src, C, 0)]: [msg]"
+ C << "Click on the administrator's name to reply."
+ src << "Admin PM to-[key_name(C, src, 1)]: [msg]"
//always play non-admin recipients the adminhelp sound
C << 'adminhelp.ogg'
diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm
index 92165d06a2f..07ccbbbddd3 100644
--- a/code/modules/admin/verbs/adminsay.dm
+++ b/code/modules/admin/verbs/adminsay.dm
@@ -20,9 +20,39 @@
//feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
for (var/mob/M in world)
- if (M.client && M.client.holder)
+ if (M.client && M.client.holder && M.client.holder.level != 0) //Moderators cannot see it.
if (src.holder.rank == "Admin Observer")
M << "ADMIN: [key_name(usr, M)]: [msg]"
else
M << "ADMIN: [key_name(usr, M)] (JMP): [msg]"
+/client/proc/cmd_mod_say(msg as text)
+ set category = "Special Verbs"
+ set name = "Msay"
+ set hidden = 1
+
+ if (!src.holder)
+ src << "Only administrators may use this command."
+ return
+
+ if (src.muted || src.muted_complete)
+ src << "You are muted."
+ return
+
+ msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ log_admin("MOD: [key_name(src)] : [msg]")
+
+
+ if (!msg)
+ return
+ //feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+ for (var/mob/M in world)
+ if (M.client && M.client.holder)
+ if (src.holder.rank == "Admin Observer")
+ M << "MOD: [key_name(usr, M)]: [msg]"
+ else if (src.holder.rank == "Moderator")
+ M << "MOD: [key_name(usr, M)] (JMP): [msg]"
+ else
+ M << "MOD: [key_name(usr, M)] (JMP): [msg]"
+
diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm
index 47e61313879..a4eb0ed0582 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -28,7 +28,7 @@
// log_admin("[key_name(Sender)] sent a message to Centcomm! The message was [msg]") // Handled somewhere else
for (var/mob/M in world)
- if (M.client && M.client.holder)
+ if (M.client && M.client.holder && M.client.holder.level != 0)
M << "\blue CENTCOMM:[key_name(Sender, M)] (PP) (VV) (SM) (JMP) (CA) (BSA) (RPLY): [msg]"
//
/proc/Syndicate_announce(var/text , var/mob/Sender)
@@ -38,6 +38,6 @@
// log_admin("[key_name(Sender)] sent a message to the Syndicate! The message was [msg]") // Handled somewhere else
for (var/mob/M in world)
- if (M.client && M.client.holder)
+ if (M.client && M.client.holder && M.client.holder.level != 0)
M << "\blue SYNDICATE:[key_name(Sender, M)] (PP) (VV) (SM) (JMP) (CA) (BSA) (RPLY): [msg]"
//
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index ec9484a289e..270a8448c10 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -301,7 +301,7 @@
proc/ManifestLateSpawn(var/mob/living/carbon/human/H, icon/H_icon) // Attempted fix to add late joiners to various databases -- TLE
- // This is basically ripped wholesale from the normal code for adding people to the databases during a fresh round //Erth Mark
+ // This is basically ripped wholesale from the normal code for adding people to the databases during a fresh round
if (!isnull(H.mind) && (H.mind.assigned_role != "MODE"))
var/datum/data/record/G = new()
var/datum/data/record/M = new()
diff --git a/code/stylesheet.dm b/code/stylesheet.dm
index a3401ade9fd..bf2272ba291 100644
--- a/code/stylesheet.dm
+++ b/code/stylesheet.dm
@@ -20,6 +20,10 @@ em {font-style: normal; font-weight: bold;}
.adminobserver {color: #996600; font-weight: bold;}
.admin {color: #386aff; font-weight: bold;}
+.mod {color: #735638; font-weight: bold;}
+.modooc {color: #184880; font-weight: bold;}
+.adminmod {color: #402A14; font-weight: bold;}
+
.name { font-weight: bold;}
.say {}
diff --git a/config/alienwhitelist.txt b/config/alienwhitelist.txt
index 7bd575035fe..39be52e1117 100644
--- a/config/alienwhitelist.txt
+++ b/config/alienwhitelist.txt
@@ -3,4 +3,6 @@ searif - Soghun
searif - Tajaran
galenus - Soghun
botanistpower - Tajaran
-fenrisian - Tajaran
\ No newline at end of file
+fenrisian - Tajaran
+forsamori - Tajaran
+forsamori - Soghun
\ No newline at end of file
diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi
index ceb1d3bd021..ce4098bcd85 100644
Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ
diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi
index 4f966143387..91032dbec0a 100644
Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ