Mentor System (#5235)

* reworked mentor system, again. wew

* Update admin.txt

* uhg
This commit is contained in:
Poojawa
2018-02-05 04:09:56 -06:00
committed by GitHub
parent 0eff0b730b
commit 03b33075e5
31 changed files with 502 additions and 214 deletions

View File

@@ -0,0 +1 @@

View File

@@ -99,3 +99,5 @@
//Brainslugs
#define isborer(A) (istype(A, /mob/living/simple_animal/borer))
#define CITADEL_MENTOR_OOC_COLOUR "#ad396e"

View File

@@ -17,11 +17,11 @@ proc/TextPreview(var/string,var/len=40)
else
return "[copytext(string, 1, 37)]..."
GLOBAL_LIST_EMPTY(mentor_log)
GLOBAL_PROTECT(mentor_log)
GLOBAL_LIST_EMPTY(mentorlog)
GLOBAL_PROTECT(mentorlog)
GLOBAL_LIST_EMPTY(whitelisted_species_list)
/proc/log_mentor(text)
GLOB.mentor_log.Add(text)
GLOB.world_game_log << "\[[time_stamp()]]MENTOR: [text]"
GLOB.mentorlog.Add(text)
GLOB.world_game_log << "\[[time_stamp()]]MENTOR: [text]"

View File

@@ -86,9 +86,6 @@ GLOBAL_LIST_INIT(dildo_colors, list(//mostly neon colors
"Purple" = "#e300ff"//purple
))
//mentor stuff
GLOBAL_LIST_EMPTY(mentors)
//Looc stuff
GLOBAL_VAR_INIT(looc_allowed, 1)
GLOBAL_VAR_INIT(dlooc_allowed, 1)

View File

@@ -44,7 +44,10 @@
body += "<br><br><b>Show related accounts by:</b> "
body += "\[ <a href='?_src_=holder;[HrefToken()];showrelatedacc=cid;client=[REF(M.client)]'>CID</a> | "
body += "<a href='?_src_=holder;[HrefToken()];showrelatedacc=ip;client=[REF(M.client)]'>IP</a> \]"
body += "<br><br>"
body += "<A href='?_src_=holder;[HrefToken()];makementor=[M.ckey]'>Make mentor</A> | "
body += "<A href='?_src_=holder;[HrefToken()];removementor=[M.ckey]'>Remove mentor</A>"
body += "<br>"
body += "<br><br>\[ "
body += "<a href='?_src_=vars;[HrefToken()];Vars=[REF(M)]'>VV</a> - "

View File

@@ -107,6 +107,8 @@
dat += "No-one has done anything this round!"
usr << browse(dat, "window=admin_log")
if("mentor_log")
CitadelMentorLogSecret()
if("list_job_debug")
var/dat = "<B>Job Debug info.</B><HR>"
for(var/line in SSjob.job_debug)

View File

@@ -22,6 +22,8 @@
if(!CheckAdminHref(href, href_list))
return
citaTopic(href, href_list) // Citadel
if(href_list["ahelp"])
if(!check_rights(R_ADMIN, TRUE))
return

View File

@@ -87,6 +87,8 @@
hsrc = holder
if("usr")
hsrc = mob
if("mentor") // CITADEL
hsrc = mentor_datum // CITADEL END
if("prefs")
if (inprefs)
return
@@ -183,6 +185,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
else if(GLOB.deadmins[ckey])
verbs += /client/proc/readmin
connecting_admin = TRUE
mentor_datum_set()// Citadel mentor_holder setting
//preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum)
prefs = GLOB.preferences_datums[ckey]

View File

@@ -67,6 +67,8 @@
to_chat(C, "<span class='adminobserverooc'><span class='prefix'>OOC:</span> <EM>[keyname][holder.fakekey ? "/([holder.fakekey])" : ""]:</EM> <span class='message'>[msg]</span></span>")
else
to_chat(C, "<font color='[GLOB.normal_ooc_colour]'><span class='ooc'><span class='prefix'>OOC:</span> <EM>[holder.fakekey ? holder.fakekey : key]:</EM> <span class='message'>[msg]</span></span></font>")
else if(is_mentor()) // Citadel Mentors
to_chat(C, "<font color='[CITADEL_MENTOR_OOC_COLOUR]'><span class='ooc'><span class='prefix'>OOC:</span> <EM>[keyname]:</EM> <span class='message'>[msg]</span></span>") // hippie end
else if(!(key in C.prefs.ignoring))
to_chat(C, "<font color='[GLOB.normal_ooc_colour]'><span class='ooc'><span class='prefix'>OOC:</span> <EM>[keyname]:</EM> <span class='message'>[msg]</span></span></font>")

View File

@@ -84,6 +84,6 @@
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 IRC. If no admins are available in game adminhelp anyways and an admin on IRC will see it and respond.</span>"
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)

View File

@@ -1,50 +0,0 @@
/client/proc/mentor_follow(var/mob/living/M)
if(!check_mentor())
return
if(isnull(M))
return
if(!istype(usr, /mob))
return
if(!holder)
var/datum/mentors/mentor = GLOB.mentor_datums[usr.client.ckey]
mentor.following = M
/* else
holder.following = M*/
usr.reset_perspective(M)
src.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'>You are now following [M]. Click the \"Stop Following\" button in the Mentor tab to stop.</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(!check_mentor())
return
usr.reset_perspective(null)
src.verbs -= /client/proc/mentor_unfollow
var/following = null
if(!holder)
var/datum/mentors/mentor = GLOB.mentor_datums[usr.client.ckey]
following = mentor.following
/*else
following = holder.following*/
to_chat(GLOB.admins, "<span class='mentor'><span class='prefix'>MENTOR:</span> <EM>[key_name(usr)]</EM> is no longer following <EM>[key_name(following)]</span>")
to_chat(usr, "<span class='info'>You are no longer following [following].</span>")
log_mentor("[key_name(usr)] stopped following [key_name(following)]")
following = null

View File

@@ -1,46 +0,0 @@
GLOBAL_LIST(mentor_datums)
/datum/mentors
var/client/owner = null
var/following = null
/datum/mentors/New(ckey)
if(!ckey)
del(src)
return
GLOB.mentor_datums[ckey] = src
/datum/mentors/proc/associate(client/C)
if(istype(C))
owner = C
GLOB.mentors |= C
/datum/mentors/proc/disassociate()
if(owner)
GLOB.mentors -= owner
owner = null
/client/proc/dementor()
var/mentor = GLOB.mentor_datums[ckey]
GLOB.mentor_datums -= ckey
qdel(mentor)
return 1
/proc/check_mentor()
if(usr && usr.client)
var/mentor = GLOB.mentor_datums[usr.client.ckey]
if(mentor || check_rights(R_ADMIN,0))
return 1
return 0
/proc/check_mentor_other(var/client/C)
if(C)
var/mentor = GLOB.mentor_datums[C.ckey]
if(C.holder && C.holder.rank)
if(C.holder.rank.rights & R_ADMIN)
return 1
else if(mentor)
return 1
return 0

View File

@@ -1,43 +0,0 @@
/proc/load_mentors()
//clear the datums references
GLOB.mentor_datums.Cut()
GLOB.mentors.Cut()
if(!config.mentor_legacy_system)
if(!GLOB.dbcon.IsConnected())
world.log << "Failed to connect to database in load_mentors()."
GLOB.diary << "Failed to connect to database in load_mentors()."
config.mentor_legacy_system = 1
load_mentors()
return
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("mentor")]")
query.Execute()
while(query.NextRow())
var/ckey = ckey(query.item[1])
var/datum/mentors/D = new(ckey) //create the mentor datum and store it for later use
if(!D) continue //will occur if an invalid rank is provided
D.associate(GLOB.directory[ckey]) //find the client for a ckey if they are connected and associate them with the new mentor datum
else
world.log << "Using legacy mentor system."
var/list/Lines = file2list("config/mentors.txt")
//process each line seperately
for(var/line in Lines)
if(!length(line)) continue
if(findtextEx(line,"#",1,2)) continue
//ckey is before the first "="
var/ckey = ckey(line)
if(!ckey) continue
var/datum/mentors/D = new(ckey) //create the admin datum and store it for later use
if(!D) continue //will occur if an invalid rank is provided
D.associate(GLOB.directory[ckey]) //find the client for a ckey if they are connected and associate them with the new admin datum
#ifdef TESTING
var/msg = "mentors Built:\n"
for(var/ckey in GLOB.mentor_datums)
msg += "\t[ckey] - mentor\n"
testing(msg)
#endif

View File

@@ -35,6 +35,14 @@ ROUND_END_COUNTDOWN 90
## This flag is automatically enabled if SQL_ENABLED isn't
ADMIN_LEGACY_SYSTEM
## Comment this out if you want to use the SQL based mentor system, the legacy system uses mentors.txt.
## You need to set up your database to use the SQL based system.
## This flag is automatically enabled if SQL_ENABLED isn't
MENTOR_LEGACY_SYSTEM
#Mentors only see ckeys by default. Uncomment to have them only see mob name
#MENTORS_MOBNAME_ONLY
## Comment this out if you want to use the SQL based banning system. The legacy systems use the files in the data folder. You need to set up your database to use the SQL based system.
BAN_LEGACY_SYSTEM

View File

@@ -0,0 +1,2 @@
GLOBAL_LIST_EMPTY(mentors) //all clients whom are admins
GLOBAL_PROTECT(mentors)

View File

@@ -0,0 +1,4 @@
/datum/config_entry/flag/mentors_mobname_only
/datum/config_entry/flag/mentor_legacy_system //Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system
protection = CONFIG_ENTRY_LOCKED

View File

@@ -1,4 +1,26 @@
//init file stolen from hippie
/proc/cit_initialize()
initialize_global_loadout_items()
load_mentors()
initialize_global_loadout_items()
//body parts and things
init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_body_markings, GLOB.mam_body_markings_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_tails, GLOB.mam_tails_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_ears, GLOB.mam_ears_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_tails_animated, GLOB.mam_tails_animated_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/taur, GLOB.taur_list)
// init_sprite_accessory_subtypes(/datum/sprite_accessory/beaks/avian, GLOB.avian_beaks_list)
// init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/avian, GLOB.avian_tails_list)
// init_sprite_accessory_subtypes(/datum/sprite_accessory/avian_wings, GLOB.avian_wings_list)
// init_sprite_accessory_subtypes(/datum/sprite_accessory/avian_open_wings, GLOB.avian_open_wings_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/xeno_head, GLOB.xeno_head_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/xeno_tail, GLOB.xeno_tail_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/xeno_dorsal, GLOB.xeno_dorsal_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/penis, GLOB.cock_shapes_list)
for(var/K in GLOB.cock_shapes_list)
var/datum/sprite_accessory/penis/value = GLOB.cock_shapes_list[K]
GLOB.cock_shapes_icons[K] = value.icon_state
init_sprite_accessory_subtypes(/datum/sprite_accessory/vagina, GLOB.vagina_shapes_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/breasts, GLOB.breasts_shapes_list)
GLOB.breasts_size_list = list("a","b","c","d","e") //We need the list to choose from initialized, but it's no longer a sprite_accessory thing.

View File

@@ -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

View File

@@ -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
..()

View File

@@ -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")

View File

@@ -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>")

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -1,8 +1,9 @@
/client/proc/mentor_memo()
set name = "Mentor Memos"
set category = "Server"
if(!check_rights(0)) return
if(!GLOB.dbcon.IsConnected())
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")
@@ -13,8 +14,9 @@
/client/proc/show_mentor_memo()
set name = "Show Memos"
set category = "Mentor"
if(!check_mentor()) return
if(!GLOB.dbcon.IsConnected())
if(!is_mentor())
return
if(!SSdbcore.IsConnected())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return
mentor_memo_output("Show")
@@ -22,13 +24,13 @@
/client/proc/mentor_memo_output(task)
if(!task)
return
if(!GLOB.dbcon.IsConnected())
if(!SSdbcore.IsConnected())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return
var/sql_ckey = sanitizeSQL(src.ckey)
var/sql_ckey = sanitizeSQL(ckey)
switch(task)
if("Write")
var/DBQuery/query_memocheck = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("mentor_memo")] WHERE ckey = '[sql_ckey]'")
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")
@@ -41,7 +43,7 @@
return
memotext = sanitizeSQL(memotext)
var/timestamp = SQLtime()
var/DBQuery/query_memoadd = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("mentor_memo")] (ckey, memotext, timestamp) VALUES ('[sql_ckey]', '[memotext]', '[timestamp]')")
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")
@@ -49,7 +51,7 @@
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/DBQuery/query_memolist = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("mentor_memo")]")
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")
@@ -65,7 +67,7 @@
if(!target_ckey)
return
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/DBQuery/query_memofind = GLOB.dbcon.NewQuery("SELECT memotext FROM [format_table_name("mentor_memo")] WHERE ckey = '[target_sql_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")
@@ -78,7 +80,7 @@
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/DBQuery/update_query = GLOB.dbcon.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]'")
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")
@@ -90,7 +92,7 @@
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/DBQuery/query_memoshow = GLOB.dbcon.NewQuery("SELECT ckey, memotext, timestamp, last_editor FROM [format_table_name("mentor_memo")]")
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")
@@ -110,7 +112,7 @@
return
to_chat(src, output)
if("Remove")
var/DBQuery/query_memodellist = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("mentor_memo")]")
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")
@@ -126,7 +128,7 @@
if(!target_ckey)
return
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/DBQuery/query_memodel = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("memo")] WHERE ckey = '[target_sql_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")

View File

@@ -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

View File

@@ -1,39 +1,36 @@
/client/verb/mentorhelp(msg as text)
set category = "Mentor"
set name = "mentorhelp"
//remove out adminhelp verb temporarily to prevent spamming of mentors.
src.verbs -= /client/verb/mentorhelp
spawn(300)
src.verbs += /client/verb/mentorhelp // 30 second cool-down for mentorhelp
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.mentors_mobname_only
var/mentor_msg = "<span class='mentornotice'><b><font color='purple'>MENTORHELP:</b> <b>[key_name_mentor(src, 1, 0, 0, show_char)]</b>: [msg]</font></span>"
var/admin_msg = "<span class='mentornotice'><b><font color='purple'>MENTORHELP:</b> <b>[ADMIN_FULLMONTY(src.mob)]</b>: [msg]</font></span>"
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)
to_chat(X, 'sound/items/bikehorn.ogg')
for(var/client/X in GLOB.mentors | GLOB.admins)
X << 'sound/items/bikehorn.ogg'
to_chat(X, mentor_msg)
for(var/client/A in GLOB.admins)
to_chat(A, 'sound/items/bikehorn.ogg')
to_chat(A, admin_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/client/X in GLOB.mentors)
for(var/X in GLOB.mentors)
var/client/C = X
.["total"]++
if(X.is_afk())
if(C.is_afk())
.["afk"]++
else
.["present"]++
@@ -71,14 +68,14 @@
if(key)
if(include_link)
if(config.mentors_mobname_only)
. += "<a href='?mentor_msg=\ref[M]'>"
if(CONFIG_GET(flag/mentors_mobname_only))
. += "<a href='?_src_=mentor;mentor_msg=[REF(M)];[MentorHrefToken(TRUE)]'>"
else
. += "<a href='?mentor_msg=[ckey]'>"
. += "<a href='?_src_=mentor;mentor_msg=[ckey];[MentorHrefToken(TRUE)]'>"
if(C && C.holder && C.holder.fakekey)
. += "Administrator"
else if (char_name_only && config.mentors_mobname_only)
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
@@ -96,6 +93,6 @@
. += "*no key*"
if(include_follow)
. += " (<a href='?mentor_follow=\ref[M]'>F</a>)"
. += " (<a href='?_src_=mentor;mentor_follow=[REF(M)];[MentorHrefToken(TRUE)]'>F</a>)"
return .

View File

@@ -2,8 +2,8 @@
/client/proc/cmd_mentor_pm_panel()
set category = "Mentor"
set name = "Mentor PM"
if(!holder)
to_chat(src, "<font color='red'>Error: Mentor-PM-Panel: Only Mentors may use this command.</font>")
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)
@@ -12,7 +12,7 @@
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)
feedback_add_details("Mentor_verb","APM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
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.
@@ -27,8 +27,8 @@
else if(istype(whom,/client))
C = whom
if(!C)
if(holder) to_chat(src, "<font color='red'>Error: Mentor-PM: Client not found.</font>")
else mentorhelp(msg) //Mentor we are replying to left. Mentorhelp instead
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
@@ -37,8 +37,8 @@
if(!msg) return
if(!C)
if(holder) 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
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))
@@ -48,9 +48,9 @@
msg = emoji_parse(msg)
C << 'sound/items/bikehorn.ogg'
var/show_char = config.mentors_mobname_only
if(check_mentor_other(C))
if(check_mentor()) //both are mentors
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>")
@@ -59,16 +59,13 @@
to_chat(src, "<font color='green'>Mentor PM to-<b>[key_name_mentor(C, C, 1, 0, 0)]</b>: [msg]</font>")
else
if(check_mentor()) //sender is an mentor but recipient is not.
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 = !check_mentor_other(src) && config.mentors_mobname_only
var/show_char_recip = !check_mentor_other(C) && config.mentors_mobname_only
for(var/client/X in GLOB.mentors)
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> \blue [msg]</font>") //inform X
for(var/client/A in GLOB.admins)
if(A.key!=key && A.key!=C.key) //check client/A is an Mentor and isn't the sender or recipient
to_chat(A, "<B><font color='green'>Mentor PM: [key_name_mentor(src, A, 0, 0, show_char_sender)]-&gt;[key_name_mentor(C, A, 0, 0, show_char_recip)]:</B> \blue [msg]</font>") //inform A
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

View File

@@ -2,7 +2,8 @@
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(!check_mentor()) return
if(!is_mentor())
return
msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
if(!msg) return
@@ -10,13 +11,8 @@
msg = emoji_parse(msg)
log_mentor("MSAY: [key_name(src)] : [msg]")
if(check_rights(R_ADMIN,0))
msg = "<span class='mentoradmin'><span class='prefix'>MENTOR:</span> <EM>[key_name(src, 0, 0)]</EM>: <span class='message'>[msg]</span></span>"
to_chat(GLOB.mentors, msg)
to_chat(GLOB.admins, 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 = "<span class='mentor'><span class='prefix'>MENTOR:</span> <EM>[key_name(src, 0, 0)]</EM>: <span class='message'>[msg]</span></span>"
to_chat(GLOB.mentors, msg)
to_chat(GLOB.admins, msg)
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)

View File

@@ -1344,7 +1344,6 @@
#include "code\modules\client\verbs\ooc.dm"
#include "code\modules\client\verbs\ping.dm"
#include "code\modules\client\verbs\suicide.dm"
#include "code\modules\client\verbs\who.dm"
#include "code\modules\clothing\chameleon.dm"
#include "code\modules\clothing\clothing.dm"
#include "code\modules\clothing\ears\_ears.dm"
@@ -2505,6 +2504,8 @@
#include "modular_citadel\code\init.dm"
#include "modular_citadel\code\__HELPERS\lists.dm"
#include "modular_citadel\code\__HELPERS\mobs.dm"
#include "modular_citadel\code\_globalvars\lists\mobs.dm"
#include "modular_citadel\code\controllers\configuration\entries\general.dm"
#include "modular_citadel\code\controllers\subsystem\cit_nightshift.dm"
#include "modular_citadel\code\controllers\subsystem\job.dm"
#include "modular_citadel\code\controllers\subsystem\shuttle.dm"
@@ -2520,9 +2521,14 @@
#include "modular_citadel\code\game\objects\items\devices\PDA\PDA.dm"
#include "modular_citadel\code\game\objects\items\devices\radio\shockcollar.dm"
#include "modular_citadel\code\game\objects\items\melee\eutactic_blades.dm"
#include "modular_citadel\code\modules\admin\admin.dm"
#include "modular_citadel\code\modules\admin\holder2.dm"
#include "modular_citadel\code\modules\admin\secrets.dm"
#include "modular_citadel\code\modules\admin\topic.dm"
#include "modular_citadel\code\game\objects\structures\beds_chairs\sofa.dm"
#include "modular_citadel\code\modules\cargo\console.dm"
#include "modular_citadel\code\modules\cargo\packs.dm"
#include "modular_citadel\code\modules\client\client_procs.dm"
#include "modular_citadel\code\modules\client\preferences.dm"
#include "modular_citadel\code\modules\client\preferences_savefile.dm"
#include "modular_citadel\code\modules\client\loadout\_donator.dm"
@@ -2537,8 +2543,16 @@
#include "modular_citadel\code\modules\client\loadout\shoes.dm"
#include "modular_citadel\code\modules\client\loadout\suit.dm"
#include "modular_citadel\code\modules\client\loadout\uniform.dm"
#include "modular_citadel\code\modules\client\verbs\who.dm"
#include "modular_citadel\code\modules\clothing\under.dm"
#include "modular_citadel\code\modules\crafting\recipes.dm"
#include "modular_citadel\code\modules\mentor\follow.dm"
#include "modular_citadel\code\modules\mentor\mentor.dm"
#include "modular_citadel\code\modules\mentor\mentor_memo.dm"
#include "modular_citadel\code\modules\mentor\mentor_verbs.dm"
#include "modular_citadel\code\modules\mentor\mentorhelp.dm"
#include "modular_citadel\code\modules\mentor\mentorpm.dm"
#include "modular_citadel\code\modules\mentor\mentorsay.dm"
#include "modular_citadel\code\modules\mining\mine_items.dm"
#include "modular_citadel\code\modules\mob\living\banana_spider.dm"
#include "modular_citadel\code\modules\mob\living\carbon\human\human_defense.dm"