Merge branch 'master' into Medical-Turtleneck

This commit is contained in:
Michiyamenotehifunana
2018-02-06 07:47:29 +08:00
committed by GitHub
1073 changed files with 103850 additions and 164911 deletions
@@ -0,0 +1,6 @@
/client/proc/citaPPoptions(mob/M) // why is this client and not /datum/admins? noone knows, in PP src == client, instead of holder. wtf.
var/body = "<br>"
if(M.client)
body += "<A href='?_src_=holder;[HrefToken()];makementor=[M.ckey]'>Make mentor</A> | "
body += "<A href='?_src_=holder;[HrefToken()];removementor=[M.ckey]'>Remove mentor</A>"
return body
@@ -0,0 +1,14 @@
/datum/admins
var/following = null
/datum/admins/associate(client/C)
removeMentor(C.ckey) //safety to avoid multiple datums and other weird shit i cannot comprehend
..()
if(istype(C))
C.mentor_datum_set(TRUE)
/datum/admins/disassociate()
if(owner)
owner.remove_mentor_verbs()
owner.mentor_datum = null
..()
@@ -0,0 +1,8 @@
/datum/admins/proc/CitadelMentorLogSecret()
var/dat = "<B>Mentor Log<HR></B>"
for(var/l in GLOB.mentorlog)
dat += "<li>[l]</li>"
if(!GLOB.mentorlog.len)
dat += "No mentors have done anything this round!"
usr << browse(dat, "window=mentor_log")
@@ -0,0 +1,55 @@
/datum/admins/proc/citaTopic(href, href_list)
/datum/admins/proc/makeMentor(ckey)
if(!usr.client)
return
if (!check_rights(0))
return
if(!ckey)
return
var/client/C = GLOB.directory[ckey]
if(C)
if(check_rights_for(C, R_ADMIN,0))
to_chat(usr, "<span class='danger'>The client chosen is an admin! Cannot mentorize.</span>")
return
if(SSdbcore.Connect())
var/datum/DBQuery/query_get_mentor = SSdbcore.NewQuery("SELECT id FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'")
if(query_get_mentor.NextRow())
to_chat(usr, "<span class='danger'>[ckey] is already a mentor.</span>")
return
var/datum/DBQuery/query_add_mentor = SSdbcore.NewQuery("INSERT INTO `[format_table_name("mentor")]` (`id`, `ckey`) VALUES (null, '[ckey]')")
if(!query_add_mentor.warn_execute())
return
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new mentor [ckey]');")
if(!query_add_admin_log.warn_execute())
return
else
to_chat(usr, "<span class='danger'>Failed to establish database connection. The changes will last only for the current round.</span>")
new /datum/mentors(ckey)
to_chat(usr, "<span class='adminnotice'>New mentor added.</span>")
/datum/admins/proc/removeMentor(ckey)
if(!usr.client)
return
if (!check_rights(0))
return
if(!ckey)
return
var/client/C = GLOB.directory[ckey]
if(C)
if(check_rights_for(C, R_ADMIN,0))
to_chat(usr, "<span class='danger'>The client chosen is an admin, not a mentor! Cannot de-mentorize.</span>")
return
C.remove_mentor_verbs()
C.mentor_datum = null
GLOB.mentors -= C
if(SSdbcore.Connect())
var/datum/DBQuery/query_remove_mentor = SSdbcore.NewQuery("DELETE FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'")
if(!query_remove_mentor.warn_execute())
return
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed mentor [ckey]');")
if(!query_add_admin_log.warn_execute())
return
else
to_chat(usr, "<span class='danger'>Failed to establish database connection. The changes will last only for the current round.</span>")
to_chat(usr, "<span class='adminnotice'>Mentor removed.</span>")
@@ -0,0 +1,14 @@
/client/proc/mentor_datum_set(admin)
mentor_datum = GLOB.mentor_datums[ckey]
if(!mentor_datum && check_rights_for(src, R_ADMIN,0)) // admin with no mentor datum?let's fix that
new /datum/mentors(ckey)
if(mentor_datum)
if(!check_rights_for(src, R_ADMIN,0) && !admin)
GLOB.mentors |= src // don't add admins to this list too.
mentor_datum.owner = src
add_mentor_verbs()
mentor_memo_output("Show")
/client/proc/is_mentor() // admins are mentors too.
if(mentor_datum || check_rights_for(src, R_ADMIN,0))
return TRUE
@@ -142,6 +142,7 @@
name = "Russian flask"
category = slot_in_backpack
path = /obj/item/reagent_containers/food/drinks/flask/russian
cost = 2
ckeywhitelist = list("slomka")
/datum/gear/stalkermask
@@ -167,3 +168,9 @@
category = slot_w_uniform
path = /obj/item/clothing/suit/vermillion
ckeywhitelist = list("fractious")
/datum/gear/AM4B
name = "Foam Force AM4-B"
category = slot_in_backpack
path = /obj/item/gun/ballistic/automatic/AM4B
ckeywhitelist = list("zeronetalpha")
@@ -18,6 +18,20 @@
category = slot_in_backpack
path = /obj/item/toy/plush/slimeplushie
/datum/gear/plushvar
name = "Ratvar Plushie"
category = slot_in_backpack
path = /obj/item/toy/plush/plushvar
cost = 5
restricted_roles = list("Chaplain")
/datum/gear/narplush
name = "Narsie Plushie"
category = slot_in_backpack
path = /obj/item/toy/plush/narplush
cost = 5
restricted_roles = list("Chaplain")
/datum/gear/dildo
name = "Customizable dildo"
category = slot_in_backpack
@@ -7,17 +7,17 @@
name = "Eyepatch"
category = slot_glasses
path = /obj/item/clothing/glasses/eyepatch
/datum/gear/heat
name = "Heat goggles"
category = slot_glasses
path = /obj/item/clothing/glasses/heat
/datum/gear/hipster
name = "Hipster glasses"
category = slot_glasses
path = /obj/item/clothing/glasses/regular/hipster
/datum/gear/jamjar
name = "Jamjar glasses"
category = slot_glasses
@@ -33,7 +33,18 @@
category = slot_glasses
path = /obj/item/clothing/glasses/orange
/datum/gear/red
name = "Red Glasses"
category = slot_glasses
path = /obj/item/clothing/glasses/red
/datum/gear/prescription
name = "Prescription glasses"
category = slot_glasses
path = /obj/item/clothing/glasses/regular
/datum/gear/sechud
name = "Security Hud"
category = slot_glasses
path = /obj/item/clothing/glasses/hud/security
restricted_roles = list("Security Officer", "Warden", "Head of Security")
@@ -0,0 +1,4 @@
/datum/gear/fingerless
name = "Fingerless Gloves"
category = slot_gloves
path = /obj/item/clothing/gloves/fingerless
@@ -47,3 +47,15 @@
name = "Wallet"
category = slot_hands
path = /obj/item/storage/wallet
/datum/gear/flask
name = "Flask"
category = slot_hands
path = /obj/item/reagent_containers/food/drinks/flask
cost = 2
/datum/gear/zippolighter
name = "Zippo Lighter"
category = slot_hands
path = /obj/item/lighter
cost = 2
@@ -1,6 +1,6 @@
/datum/gear/baseball
name = "Ballcap"
category = slot_head
category = slot_head
path = /obj/item/clothing/head/soft/mime
/datum/gear/beanie
@@ -37,3 +37,31 @@
name = "Slime hat"
category = slot_head
path = /obj/item/clothing/head/collectable/slime
/datum/gear/fedora
name = "Fedora"
category = slot_head
path = /obj/item/clothing/head/fedora
/datum/gear/that
name = "Top Hat"
category = slot_head
path = /obj/item/clothing/head/that
/datum/gear/navybluehosberet
name = "Head of security's Naviblue beret"
category = slot_head
path = /obj/item/clothing/head/beret/sec/navyhos
restricted_roles = list("Head of Security")
/datum/gear/navyblueofficerberet
name = "Security officer's Navyblue beret"
category = slot_head
path = /obj/item/clothing/head/beret/sec/navyofficer
restricted_roles = list("Security Officer")
/datum/gear/navybluewardenberet
name = "Warden's navyblue beret"
category = slot_head
path = /obj/item/clothing/head/beret/sec/navywarden
restricted_roles = list("Warden")
@@ -57,3 +57,31 @@
name = "Winter coat"
category = slot_wear_suit
path = /obj/item/clothing/suit/hooded/wintercoat
/datum/gear/militaryjacket
name = "Military Jacket"
category = slot_wear_suit
path = /obj/item/clothing/suit/jacket/miljacket
/datum/gear/ianshirt
name = "Ian Shirt"
category = slot_wear_suit
path = /obj/item/clothing/suit/ianshirt
/datum/gear/navybluejackethos
name = "head of security's navyblue jacket"
category = slot_wear_suit
path = /obj/item/clothing/suit/security/hos
restricted_roles = list("Head of Security")
/datum/gear/navybluejacketwarden
name = "warden navyblue jacket"
category = slot_wear_suit
path = /obj/item/clothing/suit/security/warden
restricted_roles = list("Warden")
/datum/gear/navybluejacketofficer
name = "security officer's navyblue jacket"
category = slot_wear_suit
path = /obj/item/clothing/suit/security/officer
restricted_roles = list("Security Officer")
@@ -78,6 +78,65 @@
category = slot_w_uniform
path = /obj/item/clothing/under/kilt
/datum/gear/navyblueuniformhos
name = "Head of Security navyblue uniform"
category = slot_w_uniform
path = /obj/item/clothing/under/rank/head_of_security/navyblue
restricted_roles = list("Head of Security")
/datum/gear/navyblueuniformwarden
name = "Warden navyblue uniform"
category = slot_w_uniform
path = /obj/item/clothing/under/rank/warden/navyblue
restricted_roles = list("Warden")
/datum/gear/navyblueuniformofficer
name = "security officer navyblue uniform"
category = slot_w_uniform
path = /obj/item/clothing/under/rank/security/navyblue
restricted_roles = list("Security officer")
/datum/gear/camoshorts
name = "Camo Pants"
category = slot_w_uniform
path = /obj/item/clothing/under/pants/camo
/datum/gear/bjeans
name = "Black Jeans"
category = slot_w_uniform
path = /obj/item/clothing/under/pants/blackjeans
/datum/gear/cjeans
name = "Classic Jeans"
category = slot_w_uniform
path = /obj/item/clothing/under/pants/classicjeans
/datum/gear/khaki
name = "Khaki Pants"
category = slot_w_uniform
path = /obj/item/clothing/under/pants/khaki
/datum/gear/wpants
name = "White Pants"
category = slot_w_uniform
path = /obj/item/clothing/under/pants/white
/datum/gear/rpants
name = "Red Pants"
category = slot_w_uniform
path = /obj/item/clothing/under/pants/red
/datum/gear/tpants
name = "Tan Pants"
category = slot_w_uniform
path = /obj/item/clothing/under/pants/tan
/datum/gear/trpants
name = "Track Pants"
category = slot_w_uniform
path = /obj/item/clothing/under/pants/track
/datum/gear/secskirt
name = "Security skirt"
category = slot_w_uniform
@@ -89,3 +148,9 @@
category = slot_w_uniform
path = /obj/item/clothing/under/rank/head_of_security/skirt
restricted_roles = list("Head of Security")
/datum/gear/turtleneck
name = "Tactitool Turtleneck"
category = slot_w_uniform
path = /obj/item/clothing/under/syndicate/cosmetic
@@ -0,0 +1,125 @@
/client/verb/mentorwho()
set category = "Mentor"
set name = "Mentorwho"
var/msg = "<b>Current Mentors:</b>\n"
for(var/X in GLOB.mentors)
var/client/C = X
if(!C)
GLOB.mentors -= C
continue // weird runtime that happens randomly
var/suffix = ""
if(holder)
if(isobserver(C.mob))
suffix += " - Observing"
else if(istype(C.mob,/mob/dead/new_player))
suffix += " - Lobby"
else
suffix += " - Playing"
if(C.is_afk())
suffix += " (AFK)"
msg += "\t[C][suffix]\n"
to_chat(src, msg)
/client/verb/who()
set name = "Who"
set category = "OOC"
var/msg = ""
var/list/Lines = list()
if(length(GLOB.admins))
Lines += "<b>Admins:</b>"
for(var/X in GLOB.admins)
var/client/C = X
if(C && C.holder && !C.holder.fakekey)
Lines += "\t <font color='#FF0000'>[C.key]</font>[show_admin_info(C)] ([round(C.avgping, 1)]ms)"
if(length(GLOB.mentors))
Lines += "<b>Mentors:</b>"
for(var/X in GLOB.mentors)
var/client/C = X
if(C)
Lines += "\t <font color='#0033CC'>[C.key]</font>[show_admin_info(C)] ([round(C.avgping, 1)]ms)"
Lines += "<b>Players:</b>"
for(var/X in sortList(GLOB.clients))
var/client/C = X
if(!C) continue
var/key = C.key
if(C.holder && C.holder.fakekey)
key = C.holder.fakekey
Lines += "\t [key][show_admin_info(C)] ([round(C.avgping, 1)]ms)"
for(var/line in Lines)
msg += "[line]\n"
msg += "<b>Total Players: [length(GLOB.clients)]</b>"
to_chat(src, msg)
/client/proc/show_admin_info(var/client/C)
if(!C)
return ""
if(!check_rights_for(src, R_ADMIN))
return ""
var/entry = ""
if(C.holder && C.holder.fakekey)
entry += " <i>(as [C.holder.fakekey])</i>"
if (isnewplayer(C.mob))
entry += " - <font color='darkgray'><b>In Lobby</b></font>"
else
entry += " - Playing as [C.mob.real_name]"
switch(C.mob.stat)
if(UNCONSCIOUS)
entry += " - <font color='darkgray'><b>Unconscious</b></font>"
if(DEAD)
if(isobserver(C.mob))
var/mob/dead/observer/O = C.mob
if(O.started_as_observer)
entry += " - <font color='gray'>Observing</font>"
else
entry += " - <font color='black'><b>DEAD</b></font>"
else
entry += " - <font color='black'><b>DEAD</b></font>"
if(is_special_character(C.mob))
entry += " - <b><font color='red'>Antagonist</font></b>"
entry += " (<A HREF='?_src_=holder;[HrefToken()];adminmoreinfo=\ref[C.mob]'>?</A>)"
return entry
/client/verb/adminwho()
set category = "Admin"
set name = "Adminwho"
var/msg = "<b>Current Admins:</b>\n"
if(check_rights_for(src, R_ADMIN))
for(var/X in GLOB.admins)
var/client/C = X
if(!check_rights_for(C, R_ADMIN))
continue
msg += "\t[C] is a [C.holder.rank]"
if(C.holder.fakekey)
msg += " <i>(as [C.holder.fakekey])</i>"
if(isobserver(C.mob))
msg += " - Observing"
else if(isnewplayer(C.mob))
msg += " - Lobby"
else
msg += " - Playing"
if(C.is_afk())
msg += " (AFK)"
msg += "\n"
else
for(var/X in GLOB.admins)
var/client/C = X
if(!check_rights_for(C, R_ADMIN))
continue
if(C.is_afk())
continue //Don't show afk admins to adminwho
if(!C.holder.fakekey)
msg += "\t[C] is a [C.holder.rank]\n"
msg += "<span class='info'>Adminhelps are also sent to Discord. If no admins are available in game adminhelp anyways and an admin on Discord will see it and respond.</span>"
to_chat(src, msg)
@@ -0,0 +1,7 @@
/obj/item/clothing/under/syndicate/cosmetic
name = "tactitool turtleneck"
desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-."
icon_state = "tactifool"
item_state = "bl_suit"
item_color = "tactifool"
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0)
@@ -0,0 +1,26 @@
/client/proc/mentor_follow(mob/living/M)
if(!is_mentor())
return
if(isnull(M))
return
if(!ismob(usr))
return
mentor_datum.following = M
usr.reset_perspective(M)
verbs += /client/proc/mentor_unfollow
to_chat(GLOB.admins, "<span class='mentor'><span class='prefix'>MENTOR:</span> <EM>[key_name(usr)]</EM> is now following <EM>[key_name(M)]</span>")
to_chat(usr, "<span class='info'>Click the \"Stop Following\" button in the Mentor tab to stop following [key_name(M)].</span>")
log_mentor("[key_name(usr)] began following [key_name(M)]")
/client/proc/mentor_unfollow()
set category = "Mentor"
set name = "Stop Following"
set desc = "Stop following the followed."
if(!is_mentor())
return
usr.reset_perspective()
verbs -= /client/proc/mentor_unfollow
to_chat(GLOB.admins, "<span class='mentor'><span class='prefix'>MENTOR:</span> <EM>[key_name(usr)]</EM> is no longer following <EM>[key_name(mentor_datum.following)]</span>")
log_mentor("[key_name(usr)] stopped following [key_name(mentor_datum.following)]")
mentor_datum.following = null
@@ -0,0 +1,113 @@
GLOBAL_LIST_EMPTY(mentor_datums)
GLOBAL_PROTECT(mentor_datums)
GLOBAL_VAR_INIT(mentor_href_token, GenerateToken())
GLOBAL_PROTECT(mentor_href_token)
/datum/mentors
var/name = "someone's mentor datum"
var/client/owner // the actual mentor, client type
var/target // the mentor's ckey
var/href_token // href token for mentor commands, uses the same token used by admins.
var/mob/following
/datum/mentors/New(ckey)
if(!ckey)
QDEL_IN(src, 0)
throw EXCEPTION("Mentor datum created without a ckey")
return
target = ckey(ckey)
name = "[ckey]'s mentor datum"
href_token = GenerateToken()
GLOB.mentor_datums[target] = src
//set the owner var and load commands
owner = GLOB.directory[ckey]
if(owner)
owner.mentor_datum = src
owner.add_mentor_verbs()
if(!check_rights_for(owner, R_ADMIN,0)) // don't add admins to mentor list.
GLOB.mentors += owner
/datum/mentors/proc/CheckMentorHREF(href, href_list)
var/auth = href_list["mentor_token"]
. = auth && (auth == href_token || auth == GLOB.mentor_href_token)
if(.)
return
var/msg = !auth ? "no" : "a bad"
message_admins("[key_name_admin(usr)] clicked an href with [msg] authorization key!")
if(CONFIG_GET(flag/debug_admin_hrefs))
message_admins("Debug mode enabled, call not blocked. Please ask your coders to review this round's logs.")
log_world("UAH: [href]")
return TRUE
log_admin_private("[key_name(usr)] clicked an href with [msg] authorization key! [href]")
/proc/RawMentorHrefToken(forceGlobal = FALSE)
var/tok = GLOB.mentor_href_token
if(!forceGlobal && usr)
var/client/C = usr.client
to_chat(world, C)
to_chat(world, usr)
if(!C)
CRASH("No client for HrefToken()!")
var/datum/mentors/holder = C.mentor_datum
if(holder)
tok = holder.href_token
return tok
/proc/MentorHrefToken(forceGlobal = FALSE)
return "mentor_token=[RawMentorHrefToken(forceGlobal)]"
/datum/mentors/Topic(href, href_list)
..()
if(!usr || !usr.client || usr.client != owner || !usr.client.is_mentor())
return
if(!CheckMentorHREF(href, href_list))
return
if(href_list["mentor_msg"])
if(CONFIG_GET(flag/mentors_mobname_only))
var/mob/M = locate(href_list["mentor_msg"])
usr.client.cmd_mentor_pm(M,null)
else
usr.client.cmd_mentor_pm(href_list["mentor_msg"],null)
return
//Mentor Follow
if(href_list["mentor_follow"])
var/mob/living/M = locate(href_list["mentor_follow"])
if(istype(M))
usr.client.mentor_follow(M)
return
/proc/load_mentors()
GLOB.mentor_datums.Cut()
for(var/client/C in GLOB.mentors)
C.remove_mentor_verbs()
C.mentor_datum = null
GLOB.mentors.Cut()
if(CONFIG_GET(flag/mentor_legacy_system))//legacy
var/list/lines = world.file2list("config/mentors.txt")
for(var/line in lines)
if(!length(line))
continue
if(findtextEx(line, "#", 1, 2))
continue
new /datum/mentors(line)
else//Database
if(!SSdbcore.Connect())
log_world("Failed to connect to database in load_mentors(). Reverting to legacy system.")
WRITE_FILE(GLOB.world_game_log, "Failed to connect to database in load_mentors(). Reverting to legacy system.")
CONFIG_SET(flag/mentor_legacy_system, TRUE)
load_mentors()
return
var/datum/DBQuery/query_load_mentors = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("mentor")]")
if(!query_load_mentors.Execute())
return
while(query_load_mentors.NextRow())
var/ckey = ckey(query_load_mentors.item[1])
new /datum/mentors(ckey)
// new client var: mentor_datum. Acts the same way holder does towards admin: it holds the mentor datum. if set, the guy's a mentor.
/client
var/datum/mentors/mentor_datum
@@ -0,0 +1,141 @@
/client/proc/mentor_memo()
set name = "Mentor Memos"
set category = "Server"
if(!check_rights(0))
return
if(!SSdbcore.IsConnected())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return
var/memotask = input(usr,"Choose task.","Memo") in list("Show","Write","Edit","Remove")
if(!memotask)
return
mentor_memo_output(memotask)
/client/proc/show_mentor_memo()
set name = "Show Memos"
set category = "Mentor"
if(!is_mentor())
return
if(!SSdbcore.IsConnected())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return
mentor_memo_output("Show")
/client/proc/mentor_memo_output(task)
if(!task)
return
if(!SSdbcore.IsConnected())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return
var/sql_ckey = sanitizeSQL(ckey)
switch(task)
if("Write")
var/datum/DBQuery/query_memocheck = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("mentor_memo")] WHERE ckey = '[sql_ckey]'")
if(!query_memocheck.Execute())
var/err = query_memocheck.ErrorMsg()
log_game("SQL ERROR obtaining ckey from memo table. Error : \[[err]\]\n")
return
if(query_memocheck.NextRow())
to_chat(src, "You already have set a memo.")
return
var/memotext = input(src,"Write your Memo","Memo") as message
if(!memotext)
return
memotext = sanitizeSQL(memotext)
var/timestamp = SQLtime()
var/datum/DBQuery/query_memoadd = SSdbcore.NewQuery("INSERT INTO [format_table_name("mentor_memo")] (ckey, memotext, timestamp) VALUES ('[sql_ckey]', '[memotext]', '[timestamp]')")
if(!query_memoadd.Execute())
var/err = query_memoadd.ErrorMsg()
log_game("SQL ERROR adding new memo. Error : \[[err]\]\n")
return
log_admin("[key_name(src)] has set a mentor memo: [memotext]")
message_admins("[key_name_admin(src)] has set a mentor memo:<br>[memotext]")
if("Edit")
var/datum/DBQuery/query_memolist = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("mentor_memo")]")
if(!query_memolist.Execute())
var/err = query_memolist.ErrorMsg()
log_game("SQL ERROR obtaining ckey from memo table. Error : \[[err]\]\n")
return
var/list/memolist = list()
while(query_memolist.NextRow())
var/lkey = query_memolist.item[1]
memolist += "[lkey]"
if(!memolist.len)
to_chat(src, "No memos found in database.")
return
var/target_ckey = input(src, "Select whose memo to edit", "Select memo") as null|anything in memolist
if(!target_ckey)
return
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/datum/DBQuery/query_memofind = SSdbcore.NewQuery("SELECT memotext FROM [format_table_name("mentor_memo")] WHERE ckey = '[target_sql_ckey]'")
if(!query_memofind.Execute())
var/err = query_memofind.ErrorMsg()
log_game("SQL ERROR obtaining memotext from memo table. Error : \[[err]\]\n")
return
if(query_memofind.NextRow())
var/old_memo = query_memofind.item[1]
var/new_memo = input("Input new memo", "New Memo", "[old_memo]", null) as message
if(!new_memo)
return
new_memo = sanitizeSQL(new_memo)
var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from<br>[old_memo]<br>to<br>[new_memo]<hr>"
edit_text = sanitizeSQL(edit_text)
var/datum/DBQuery/update_query = SSdbcore.NewQuery("UPDATE [format_table_name("mentor_memo")] SET memotext = '[new_memo]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE ckey = '[target_sql_ckey]'")
if(!update_query.Execute())
var/err = update_query.ErrorMsg()
log_game("SQL ERROR editing memo. Error : \[[err]\]\n")
return
if(target_sql_ckey == sql_ckey)
log_admin("[key_name(src)] has edited their mentor memo from [old_memo] to [new_memo]")
message_admins("[key_name_admin(src)] has edited their mentor memo from<br>[old_memo]<br>to<br>[new_memo]")
else
log_admin("[key_name(src)] has edited [target_sql_ckey]'s mentor memo from [old_memo] to [new_memo]")
message_admins("[key_name_admin(src)] has edited [target_sql_ckey]'s mentor memo from<br>[old_memo]<br>to<br>[new_memo]")
if("Show")
var/datum/DBQuery/query_memoshow = SSdbcore.NewQuery("SELECT ckey, memotext, timestamp, last_editor FROM [format_table_name("mentor_memo")]")
if(!query_memoshow.Execute())
var/err = query_memoshow.ErrorMsg()
log_game("SQL ERROR obtaining ckey, memotext, timestamp, last_editor from memo table. Error : \[[err]\]\n")
return
var/output = null
while(query_memoshow.NextRow())
var/ckey = query_memoshow.item[1]
var/memotext = query_memoshow.item[2]
var/timestamp = query_memoshow.item[3]
var/last_editor = query_memoshow.item[4]
output += "<span class='memo'>Mentor memo by <span class='prefix'>[ckey]</span> on [timestamp]"
if(last_editor)
output += "<br><span class='memoedit'>Last edit by [last_editor] <A href='?_src_=holder;mentormemoeditlist=[ckey]'>(Click here to see edit log)</A></span>"
output += "<br>[memotext]</span><br>"
if(!output)
to_chat(src, "No memos found in database.")
return
to_chat(src, output)
if("Remove")
var/datum/DBQuery/query_memodellist = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("mentor_memo")]")
if(!query_memodellist.Execute())
var/err = query_memodellist.ErrorMsg()
log_game("SQL ERROR obtaining ckey from memo table. Error : \[[err]\]\n")
return
var/list/memolist = list()
while(query_memodellist.NextRow())
var/ckey = query_memodellist.item[1]
memolist += "[ckey]"
if(!memolist.len)
to_chat(src, "No memos found in database.")
return
var/target_ckey = input(src, "Select whose mentor memo to delete", "Select mentor memo") as null|anything in memolist
if(!target_ckey)
return
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/datum/DBQuery/query_memodel = SSdbcore.NewQuery("DELETE FROM [format_table_name("memo")] WHERE ckey = '[target_sql_ckey]'")
if(!query_memodel.Execute())
var/err = query_memodel.ErrorMsg()
log_game("SQL ERROR removing memo. Error : \[[err]\]\n")
return
if(target_sql_ckey == sql_ckey)
log_admin("[key_name(src)] has removed their mentor memo.")
message_admins("[key_name_admin(src)] has removed their mentor memo.")
else
log_admin("[key_name(src)] has removed [target_sql_ckey]'s mentor memo.")
message_admins("[key_name_admin(src)] has removed [target_sql_ckey]'s mentor memo.")
@@ -0,0 +1,12 @@
GLOBAL_PROTECT(mentor_verbs)
GLOBAL_LIST_INIT(mentor_verbs, list(
/client/proc/cmd_mentor_say,
/client/proc/show_mentor_memo
))
/client/proc/add_mentor_verbs()
if(mentor_datum)
verbs += GLOB.mentor_verbs
/client/proc/remove_mentor_verbs()
verbs -= GLOB.mentor_verbs
@@ -0,0 +1,98 @@
/client/verb/mentorhelp(msg as text)
set category = "Mentor"
set name = "Mentorhelp"
//clean the input msg
if(!msg) return
//remove out mentorhelp verb temporarily to prevent spamming of mentors.
verbs -= /client/verb/mentorhelp
spawn(300)
verbs += /client/verb/mentorhelp // 30 second cool-down for mentorhelp
msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
if(!msg) return
if(!mob) return //this doesn't happen
var/show_char = CONFIG_GET(flag/mentors_mobname_only)
var/mentor_msg = "<span class='mentornotice'><b><font color='purple'>MENTORHELP:</b> <b>[key_name_mentor(src, 1, 0, 1, show_char)]</b>: [msg]</font></span>"
log_mentor("MENTORHELP: [key_name_mentor(src, 0, 0, 0, 0)]: [msg]")
for(var/client/X in GLOB.mentors | GLOB.admins)
X << 'sound/items/bikehorn.ogg'
to_chat(X, mentor_msg)
to_chat(src, "<span class='mentornotice'><font color='purple'>PM to-<b>Mentors</b>: [msg]</font></span>")
return
/proc/get_mentor_counts()
. = list("total" = 0, "afk" = 0, "present" = 0)
for(var/X in GLOB.mentors)
var/client/C = X
.["total"]++
if(C.is_afk())
.["afk"]++
else
.["present"]++
/proc/key_name_mentor(var/whom, var/include_link = null, var/include_name = 0, var/include_follow = 0, var/char_name_only = 0)
var/mob/M
var/client/C
var/key
var/ckey
if(!whom) return "*null*"
if(istype(whom, /client))
C = whom
M = C.mob
key = C.key
ckey = C.ckey
else if(ismob(whom))
M = whom
C = M.client
key = M.key
ckey = M.ckey
else if(istext(whom))
key = whom
ckey = ckey(whom)
C = GLOB.directory[ckey]
if(C)
M = C.mob
else
return "*invalid*"
. = ""
if(!ckey)
include_link = 0
if(key)
if(include_link)
if(CONFIG_GET(flag/mentors_mobname_only))
. += "<a href='?_src_=mentor;mentor_msg=[REF(M)];[MentorHrefToken(TRUE)]'>"
else
. += "<a href='?_src_=mentor;mentor_msg=[ckey];[MentorHrefToken(TRUE)]'>"
if(C && C.holder && C.holder.fakekey)
. += "Administrator"
else if (char_name_only && CONFIG_GET(flag/mentors_mobname_only))
if(istype(C.mob,/mob/dead/new_player) || istype(C.mob, /mob/dead/observer)) //If they're in the lobby or observing, display their ckey
. += key
else if(C && C.mob) //If they're playing/in the round, only show the mob name
. += C.mob.name
else //If for some reason neither of those are applicable and they're mentorhelping, show ckey
. += key
else
. += key
if(!C)
. += "\[DC\]"
if(include_link)
. += "</a>"
else
. += "*no key*"
if(include_follow)
. += " (<a href='?_src_=mentor;mentor_follow=[REF(M)];[MentorHrefToken(TRUE)]'>F</a>)"
return .
@@ -0,0 +1,71 @@
//shows a list of clients we could send PMs to, then forwards our choice to cmd_Mentor_pm
/client/proc/cmd_mentor_pm_panel()
set category = "Mentor"
set name = "Mentor PM"
if(!is_mentor())
to_chat(src, "<font color='red'>Error: Mentor-PM-Panel: Only Mentors and Admins may use this command.</font>")
return
var/list/client/targets[0]
for(var/client/T)
targets["[T]"] = T
var/list/sorted = sortList(targets)
var/target = input(src,"To whom shall we send a message?","Mentor PM",null) in sorted|null
cmd_mentor_pm(targets[target],null)
SSblackbox.record_feedback("tally", "Mentor_verb", 1, "APM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//takes input from cmd_mentor_pm_context, cmd_Mentor_pm_panel or /client/Topic and sends them a PM.
//Fetching a message if needed. src is the sender and C is the target client
/client/proc/cmd_mentor_pm(whom, msg)
var/client/C
if(ismob(whom))
var/mob/M = whom
C = M.client
else if(istext(whom))
C = GLOB.directory[whom]
else if(istype(whom,/client))
C = whom
if(!C)
if(is_mentor()) to_chat(src, "<font color='red'>Error: Mentor-PM: Client not found.</font>")
else mentorhelp(msg) //Mentor we are replying to left. Mentorhelp instead(check below)
return
//get message text, limit it's length.and clean/escape html
if(!msg)
msg = input(src,"Message:", "Private message") as text|null
if(!msg) return
if(!C)
if(is_mentor()) to_chat(src, "<font color='red'>Error: Mentor-PM: Client not found.</font>")
else mentorhelp(msg) //Mentor we are replying to has vanished, Mentorhelp instead (how the fuck does this work?let's hope it works,shrug)
return
msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
if(!msg) return
log_mentor("Mentor PM: [key_name(src)]->[key_name(C)]: [msg]")
msg = emoji_parse(msg)
C << 'sound/items/bikehorn.ogg'
var/show_char = CONFIG_GET(flag/mentors_mobname_only)
if(C.is_mentor())
if(is_mentor())//both are mentors
to_chat(C, "<font color='purple'>Mentor PM from-<b>[key_name_mentor(src, C, 1, 0, 0)]</b>: [msg]</font>")
to_chat(src, "<font color='green'>Mentor PM to-<b>[key_name_mentor(C, C, 1, 0, 0)]</b>: [msg]</font>")
else //recipient is an mentor but sender is not
to_chat(C, "<font color='purple'>Reply PM from-<b>[key_name_mentor(src, C, 1, 0, show_char)]</b>: [msg]</font>")
to_chat(src, "<font color='green'>Mentor PM to-<b>[key_name_mentor(C, C, 1, 0, 0)]</b>: [msg]</font>")
else
if(is_mentor()) //sender is an mentor but recipient is not.
to_chat(C, "<font color='purple'>Mentor PM from-<b>[key_name_mentor(src, C, 1, 0, 0)]</b>: [msg]</font>")
to_chat(src, "<font color='green'>Mentor PM to-<b>[key_name_mentor(C, C, 1, 0, show_char)]</b>: [msg]</font>")
//we don't use message_Mentors here because the sender/receiver might get it too
var/show_char_sender = !is_mentor() && CONFIG_GET(flag/mentors_mobname_only)
var/show_char_recip = !C.is_mentor() && CONFIG_GET(flag/mentors_mobname_only)
for(var/client/X in GLOB.mentors | GLOB.admins)
if(X.key!=key && X.key!=C.key) //check client/X is an Mentor and isn't the sender or recipient
to_chat(X, "<B><font color='green'>Mentor PM: [key_name_mentor(src, X, 0, 0, show_char_sender)]-&gt;[key_name_mentor(C, X, 0, 0, show_char_recip)]:</B> <font color ='blue'> [msg]</font>") //inform X
@@ -0,0 +1,18 @@
/client/proc/cmd_mentor_say(msg as text)
set category = "Mentor"
set name = "Msay" //Gave this shit a shorter name so you only have to time out "msay" rather than "mentor say" to use it --NeoFite
set hidden = 1
if(!is_mentor())
return
msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
if(!msg) return
msg = emoji_parse(msg)
log_mentor("MSAY: [key_name(src)] : [msg]")
if(check_rights_for(src, R_ADMIN,0))
msg = "<b><font color ='#8A2BE2'><span class='prefix'>MENTOR:</span> <EM>[key_name(src, 0, 0)]</EM>: <span class='message'>[msg]</span></font></b>"
else
msg = "<b><font color ='#E236D8'><span class='prefix'>MENTOR:</span> <EM>[key_name(src, 0, 0)]</EM>: <span class='message'>[msg]</span></font></b>"
to_chat(GLOB.admins | GLOB.mentors, msg)
@@ -0,0 +1,12 @@
/mob/living/carbon/human/Life()
//citadel code
if(stat != DEAD)
handle_arousal()
. = ..()
/mob/living/carbon/human/calculate_affecting_pressure(pressure)
if(ismob(loc))
return ONE_ATMOSPHERE
if(istype(loc, /obj/item/device/dogborg/sleeper))
return ONE_ATMOSPHERE
. = ..()