Merge remote-tracking branch 'VOREStation/master' into upstream-merge-7697

This commit is contained in:
Unknown
2021-03-11 10:01:39 -05:00
323 changed files with 181375 additions and 155860 deletions
+1
View File
@@ -26,6 +26,7 @@
var/datum/chatOutput/chatOutput
var/datum/volume_panel/volume_panel = null // Initialized by /client/verb/volume_panel()
var/chatOutputLoadedAt
var/seen_news = 0
var/adminhelped = 0
@@ -1,3 +1,5 @@
#define LOADOUT_BAN_STRING "Custom loadout"
/datum/gear_tweak/proc/get_contents(var/metadata)
return
@@ -165,7 +167,7 @@ var/datum/gear_tweak/custom_name/gear_tweak_free_name = new()
return ""
/datum/gear_tweak/custom_name/get_metadata(var/user, var/metadata)
if(jobban_isbanned(user, "Custom loadout"))
if(jobban_isbanned(user, LOADOUT_BAN_STRING))
to_chat(user, SPAN_WARNING("You are banned from using custom loadout names/descriptions."))
return
if(valid_custom_names)
@@ -196,7 +198,7 @@ var/datum/gear_tweak/custom_desc/gear_tweak_free_desc = new()
return ""
/datum/gear_tweak/custom_desc/get_metadata(var/user, var/metadata)
if(jobban_isbanned(user, "Custom loadout"))
if(jobban_isbanned(user, LOADOUT_BAN_STRING))
to_chat(user, SPAN_WARNING("You are banned from using custom loadout names/descriptions."))
return
if(valid_custom_desc)
@@ -507,3 +509,46 @@ var/datum/gear_tweak/custom_desc/gear_tweak_free_desc = new()
var/t = ValidTeslaLinks[metadata[7]]
I.tesla_link = new t(I)
I.update_verbs()
/datum/gear_tweak/implant_location
var/static/list/bodypart_names_to_tokens = list(
"head" = BP_HEAD,
"upper body" = BP_TORSO,
"lower body" = BP_GROIN,
"left hand" = BP_L_HAND,
"left arm" = BP_L_ARM,
"right hand" = BP_R_HAND,
"right arm" = BP_R_ARM,
"left foot" = BP_L_FOOT,
"left leg" = BP_L_LEG,
"right foot" = BP_R_FOOT,
"right leg" = BP_R_LEG
)
var/static/list/bodypart_tokens_to_names = list(
BP_HEAD = "head",
BP_TORSO = "upper body",
BP_GROIN = "lower body",
BP_LEFT_HAND = "left hand",
BP_LEFT_ARM = "left arm",
BP_RIGHT_HAND = "right hand",
BP_RIGHT_ARM = "right arm",
BP_LEFT_FOOT = "left foot",
BP_LEFT_LEG = "left leg",
BP_RIGHT_FOOT = "right foot",
BP_RIGHT_LEG = "right leg"
)
/datum/gear_tweak/implant_location/get_default()
return bodypart_names_to_tokens[1]
/datum/gear_tweak/implant_location/tweak_item(var/obj/item/weapon/implant/I, var/metadata)
if(istype(I))
I.initialize_loc = bodypart_names_to_tokens[metadata] || BP_TORSO
/datum/gear_tweak/implant_location/get_contents(var/metadata)
return "Location: [metadata]"
/datum/gear_tweak/implant_location/get_metadata(var/user, var/metadata)
return (input(user, "Select a bodypart for the implant to be implanted inside.", "Implant Location", metadata || "upper body") as null|anything in bodypart_names_to_tokens) || bodypart_tokens_to_names[BP_TORSO]
#undef LOADOUT_BAN_STRING
@@ -77,6 +77,12 @@
display_name = "drop pouches, white (Medical)"
allowed_roles = list("Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic")
/datum/gear/accessory/bluespace
display_name = "bluespace badge (Eng, Sec, Med, Exploration, Miner)"
path = /obj/item/clothing/accessory/storage/bluespace
allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Explorer","Pathfinder","Shaft Miner")
cost = 2
/datum/gear/accessory/webbing
cost = 1
@@ -0,0 +1,56 @@
/datum/gear/utility/implant
display_name = "implant, neural assistance web"
description = "A complex web implanted into the subject, medically in order to compensate for neurological disease."
path = /obj/item/weapon/implant/neural
slot = "implant"
exploitable = 1
sort_category = "Cyberware"
cost = 6
/datum/gear/utility/implant/tracking
display_name = "implant, tracking"
path = /obj/item/weapon/implant/tracking/weak
cost = 10
// Remove these after generic implant has been in for awhile and everyone has had a reasonable period to copy their old descs.
/datum/gear/utility/implant/dud1
display_name = "implant, head"
description = "An implant with no obvious purpose (DEPRECATED, USE GENERIC IMPLANT)."
path = /obj/item/weapon/implant/dud
cost = 1
/datum/gear/utility/implant/dud2
display_name = "implant, torso"
description = "An implant with no obvious purpose (DEPRECATED, USE GENERIC IMPLANT)."
path = /obj/item/weapon/implant/dud/torso
cost = 1
// End removal marker.
/datum/gear/utility/implant/generic
display_name = "implant, generic, primary"
description = "An implant with no obvious purpose."
path = /obj/item/weapon/implant
cost = 1
/datum/gear/utility/implant/generic/second
display_name = "implant, generic, secondary"
/datum/gear/utility/implant/generic/third
display_name = "implant, generic, tertiary"
/datum/gear/utility/implant/generic/New()
..()
gear_tweaks += global.gear_tweak_implant_location
/datum/gear/utility/implant/language
cost = 2
exploitable = 0
/datum/gear/utility/implant/language/eal
display_name = "vocal synthesizer, EAL"
description = "A surgically implanted vocal synthesizer which allows the owner to speak EAL, if they know it."
path = /obj/item/weapon/implant/language/eal
/datum/gear/utility/implant/language/skrellian
display_name = "vocal synthesizer, Skrellian"
description = "A surgically implanted vocal synthesizer which allows the owner to speak Common Skrellian, if they know it."
path = /obj/item/weapon/implant/language/skrellian
@@ -391,4 +391,8 @@
/datum/gear/head/blackngoldheaddress
display_name = "black and gold headdress"
path = /obj/item/clothing/head/blackngoldheaddress
path = /obj/item/clothing/head/blackngoldheaddress
/datum/gear/head/plaguedoctor2
display_name = "golden plague doctor's hat"
path = /obj/item/clothing/head/plaguedoctorhat/gold
@@ -24,4 +24,9 @@
/datum/gear/mask/veil
display_name = "black veil"
path = /obj/item/clothing/mask/veil
path = /obj/item/clothing/mask/veil
/datum/gear/mask/plaguedoctor2
display_name = "golden plague doctor's mask"
path = /obj/item/clothing/mask/gas/plaguedoctor/gold
cost = 3 ///Because it functions as a gas mask, and therefore has a mechanical advantage.
@@ -173,6 +173,10 @@ datum/gear/suit/duster
path = /obj/item/clothing/suit/storage/toggle/labcoat/emt
allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist")
/datum/gear/suit/miscellaneous/labcoat
display_name = "plague doctor's coat"
path = /obj/item/clothing/suit/storage/toggle/labcoat/plaguedoctor
/datum/gear/suit/roles/surgical_apron
display_name = "surgical apron"
path = /obj/item/clothing/suit/surgicalapron
@@ -644,4 +644,16 @@
/datum/gear/uniform/gothic2
display_name = "lacey gothic dress"
path = /obj/item/clothing/under/dress/gothic2
path = /obj/item/clothing/under/dress/gothic2
/datum/gear/uniform/flowerskirt
display_name = "flower skirt"
path = /obj/item/clothing/under/flower_skirt
/datum/gear/uniform/flowerskirt/New()
..()
gear_tweaks += gear_tweak_free_color_choice
/datum/gear/uniform/countess
display_name = "countess dress"
path = /obj/item/clothing/under/dress/countess
@@ -204,3 +204,11 @@ Qipao
/datum/gear/uniform/qipao_white
display_name = "qipao, white"
path = /obj/item/clothing/under/dress/qipao/white
/*
Bluespace jumpsuit
*/
/datum/gear/uniform/hfjumpsuit
display_name = "HYPER jumpsuit"
path = /obj/item/clothing/under/hyperfiber
cost = 2
@@ -115,60 +115,10 @@
display_name = "cell, device"
path = /obj/item/weapon/cell/device
/datum/gear/utility/implant
slot = "implant"
exploitable = 1
/datum/gear/utility/implant/tracking
display_name = "implant, tracking"
path = /obj/item/weapon/implant/tracking/weak
cost = 0 //VOREStation Edit. Changed cost to 0
/datum/gear/utility/implant/neural
display_name = "implant, neural assistance web"
description = "A complex web implanted into the subject, medically in order to compensate for neurological disease."
path = /obj/item/weapon/implant/neural
cost = 6
/datum/gear/utility/implant/dud1
display_name = "implant, head"
description = "An implant with no obvious purpose."
path = /obj/item/weapon/implant/dud
cost = 1
/datum/gear/utility/implant/dud2
display_name = "implant, torso"
description = "An implant with no obvious purpose."
path = /obj/item/weapon/implant/dud/torso
cost = 1
/datum/gear/utility/implant/language
cost = 2
exploitable = 0
/datum/gear/utility/implant/language/eal
display_name = "vocal synthesizer, EAL"
description = "A surgically implanted vocal synthesizer which allows the owner to speak EAL, if they know it."
path = /obj/item/weapon/implant/language/eal
/datum/gear/utility/implant/language/skrellian
display_name = "vocal synthesizer, Skrellian"
description = "A surgically implanted vocal synthesizer which allows the owner to speak Common Skrellian, if they know it."
path = /obj/item/weapon/implant/language/skrellian
/datum/gear/utility/pen
display_name = "Fountain Pen"
path = /obj/item/weapon/pen/fountain
/datum/gear/utility/wheelchair/color
display_name = "wheelchair"
path = /obj/item/wheelchair
cost = 4
/datum/gear/utility/wheelchair/color/New()
..()
gear_tweaks += gear_tweak_free_color_choice
/datum/gear/utility/umbrella
display_name = "Umbrella"
path = /obj/item/weapon/melee/umbrella
@@ -178,6 +128,20 @@
..()
gear_tweaks += gear_tweak_free_color_choice
/datum/gear/utility/wheelchair
display_name = "wheelchair selection"
path = /obj/item/wheelchair
cost = 4
/datum/gear/utility/wheelchair/New()
..()
gear_tweaks += gear_tweak_free_color_choice
var/list/wheelchairs = list(
"wheelchair" = /obj/item/wheelchair,
"motorized wheelchair" = /obj/item/wheelchair/motor
)
gear_tweaks += new/datum/gear_tweak/path(wheelchairs)
/****************
modular computers
****************/
@@ -670,3 +670,10 @@
path = /obj/item/clothing/glasses/aerogelgoggles
whitelisted = SPECIES_TESHARI
sort_category = "Xenowear"
/datum/gear/utility/teshchair
display_name = "small electric wheelchair (Teshari)"
path = /obj/item/wheelchair/motor/small
whitelisted = SPECIES_TESHARI
sort_category = "Xenowear"
cost = 4
@@ -149,6 +149,9 @@
var/available_in_hours = job.available_in_playhours(user.client)
. += "<del>[rank]</del></td></a><td> \[IN [round(available_in_hours, 0.1)] DEPTHOURS]</td></tr>"
continue
if(!is_job_whitelisted(user,rank))
. += "<del>[rank]</del></td></a><td><b> \[WHITELIST ONLY]</b></td></tr>"
continue
//VOREStation Add End
if(job.minimum_character_age && user.client && (user.client.prefs.age < job.minimum_character_age))
. += "<del>[rank]</del></td></a><td> \[MINIMUM CHARACTER AGE: [job.minimum_character_age]]</td></tr>"
@@ -299,7 +299,3 @@
if(PREF_FBP_SOFTWARE)
return 150
return S.max_age // welp
/datum/category_item/player_setup_item/proc/color_square(red, green, blue, hex)
var/color = hex ? hex : "#[num2hex(red, 2)][num2hex(green, 2)][num2hex(blue, 2)]"
return "<span style='font-face: fixedsys; font-size: 14px; background-color: [color]; color: [color]'>___</span>"
@@ -23,12 +23,8 @@
// Physical traits are what they sound like, and involve the character's physical body, as opposed to their mental state.
/datum/trait/modifier/physical
<<<<<<< HEAD
category = "Physical Quirks" //VOREStation Edit
=======
name = "Physical"
category = "Physical"
>>>>>>> b22a056... Sideports a couple of init unit tests from Neb. (#7893)
/datum/trait/modifier/physical/flimsy
@@ -10,6 +10,11 @@
var/custom_base // What to base the custom species on
var/blood_color = "#A10808"
var/custom_say = null
var/custom_whisper = null
var/custom_ask = null
var/custom_exclaim = null
var/list/pos_traits = list() // What traits they've selected for their custom species
var/list/neu_traits = list()
var/list/neg_traits = list()
@@ -33,10 +38,15 @@
S["neg_traits"] >> pref.neg_traits
S["blood_color"] >> pref.blood_color
S["traits_cheating"]>> pref.traits_cheating
S["traits_cheating"] >> pref.traits_cheating
S["max_traits"] >> pref.max_traits
S["trait_points"] >> pref.starting_trait_points
S["custom_say"] >> pref.custom_say
S["custom_whisper"] >> pref.custom_whisper
S["custom_ask"] >> pref.custom_ask
S["custom_exclaim"] >> pref.custom_exclaim
/datum/category_item/player_setup_item/vore/traits/save_character(var/savefile/S)
S["custom_species"] << pref.custom_species
S["custom_base"] << pref.custom_base
@@ -45,10 +55,15 @@
S["neg_traits"] << pref.neg_traits
S["blood_color"] << pref.blood_color
S["traits_cheating"]<< pref.traits_cheating
S["traits_cheating"] << pref.traits_cheating
S["max_traits"] << pref.max_traits
S["trait_points"] << pref.starting_trait_points
S["custom_say"] << pref.custom_say
S["custom_whisper"] << pref.custom_whisper
S["custom_ask"] << pref.custom_ask
S["custom_exclaim"] << pref.custom_exclaim
/datum/category_item/player_setup_item/vore/traits/sanitize_character()
if(!pref.pos_traits) pref.pos_traits = list()
if(!pref.neu_traits) pref.neu_traits = list()
@@ -86,6 +101,10 @@
/datum/category_item/player_setup_item/vore/traits/copy_to_mob(var/mob/living/carbon/human/character)
character.custom_species = pref.custom_species
character.custom_say = pref.custom_say
character.custom_ask = pref.custom_ask
character.custom_whisper = pref.custom_whisper
character.custom_exclaim = pref.custom_exclaim
if(character.isSynthetic()) //Checking if we have a synth on our hands, boys.
pref.dirty_synth = 1
@@ -122,14 +141,14 @@
. += "<a href='?src=\ref[src];custom_base=1'>[pref.custom_base ? pref.custom_base : "Human"]</a><br>"
var/traits_left = pref.max_traits
. += "<b>Traits Left:</b> [traits_left]<br>"
if(pref.species == SPECIES_CUSTOM)
var/points_left = pref.starting_trait_points
for(var/T in pref.pos_traits + pref.neg_traits)
points_left -= traits_costs[T]
traits_left--
. += "<b>Traits Left:</b> [traits_left]<br>"
. += "<b>Points Left:</b> [points_left]<br>"
if(points_left < 0 || traits_left < 0 || !pref.custom_species)
. += "<span style='color:red;'><b>^ Fix things! ^</b></span><br>"
@@ -156,6 +175,16 @@
. += "<b>Blood Color: </b>" //People that want to use a certain species to have that species traits (xenochimera/promethean/spider) should be able to set their own blood color.
. += "<a href='?src=\ref[src];blood_color=1'>Set Color</a>"
. += "<a href='?src=\ref[src];blood_reset=1'>R</a><br>"
. += "<br>"
. += "<b>Custom Say: </b>"
. += "<a href='?src=\ref[src];custom_say=1'>Set Say Verb</a><br>"
. += "<b>Custom Whisper: </b>"
. += "<a href='?src=\ref[src];custom_whisper=1'>Set Whisper Verb</a><br>"
. += "<b>Custom Ask: </b>"
. += "<a href='?src=\ref[src];custom_ask=1'>Set Ask Verb</a><br>"
. += "<b>Custom Exclaim: </b>"
. += "<a href='?src=\ref[src];custom_exclaim=1'>Set Exclaim Verb</a><br>"
/datum/category_item/player_setup_item/vore/traits/OnTopic(var/href,var/list/href_list, var/mob/user)
if(!CanUseTopic(user))
@@ -216,6 +245,30 @@
pref.neg_traits -= trait
return TOPIC_REFRESH
else if(href_list["custom_say"])
var/say_choice = sanitize(input(usr, "This word or phrase will appear instead of 'says': [pref.real_name] says, \"Hi.\"", "Custom Say", pref.custom_say) as null|text, 12)
if(say_choice)
pref.custom_say = say_choice
return TOPIC_REFRESH
else if(href_list["custom_whisper"])
var/whisper_choice = sanitize(input(usr, "This word or phrase will appear instead of 'whispers': [pref.real_name] whispers, \"Hi...\"", "Custom Whisper", pref.custom_whisper) as null|text, 12)
if(whisper_choice)
pref.custom_whisper = whisper_choice
return TOPIC_REFRESH
else if(href_list["custom_ask"])
var/ask_choice = sanitize(input(usr, "This word or phrase will appear instead of 'asks': [pref.real_name] asks, \"Hi?\"", "Custom Ask", pref.custom_ask) as null|text, 12)
if(ask_choice)
pref.custom_ask = ask_choice
return TOPIC_REFRESH
else if(href_list["custom_exclaim"])
var/exclaim_choice = sanitize(input(usr, "This word or phrase will appear instead of 'exclaims', 'shouts' or 'yells': [pref.real_name] exclaims, \"Hi!\"", "Custom Exclaim", pref.custom_exclaim) as null|text, 12)
if(exclaim_choice)
pref.custom_exclaim = exclaim_choice
return TOPIC_REFRESH
else if(href_list["add_trait"])
var/mode = text2num(href_list["add_trait"])
var/list/picklist
@@ -290,6 +343,10 @@
alert("The trait you've selected cannot be taken by the species you've chosen!","Error")
return TOPIC_REFRESH
if( LAZYLEN(instance.allowed_species) && !(pref.species in instance.allowed_species)) //Adding white list handling -shark
alert("The trait you've selected cannot be taken by the species you've chosen!","Error")
return TOPIC_REFRESH
if(trait_choice in pref.pos_traits + pref.neu_traits + pref.neg_traits)
conflict = instance.name
+97
View File
@@ -0,0 +1,97 @@
/client/verb/who_advanced()
set name = "Advanced Who"
set category = "OOC"
var/msg = "<b>Current Players:</b>\n"
var/list/Lines = list()
if(holder)
for(var/client/C in GLOB.clients)
var/entry = "<tr><td>[C.key]"
if(C.holder && C.holder.fakekey)
entry += " <i>(as [C.holder.fakekey])</i>"
entry += "</td><td>"
if(C.mob.real_name)
switch(C.mob.stat)
if(UNCONSCIOUS)
entry += "<span class='darkgray'><b>Unconscious</b></span>" // these are literally all spans so I can apply .inverted to them because black on dark grey isn't legible
if(DEAD)
if(isobserver(C.mob))
var/mob/observer/dead/O = C.mob
if(O.started_as_observer)
entry += "<span class='gray'>Observing</span>"
else
entry += "<span class='black'><b>Died</b></span>"
else
entry += "<span class='green'>Playing</span>"
entry += " as [C.mob.real_name]"
else if(isnewplayer(C.mob))
entry += "<span class='blue'><b>In lobby</b></span>"
entry += "</td><td>"
var/age
if(isnum(C.player_age))
age = C.player_age
else
age = 0
if(age <= 1)
age = "<span class='red'><b>[age]</b></span>"
else if(age < 10)
age = "<span class='orange'><b>[age]</b></span>"
entry += "Age: [age]"
entry += "</td><td>"
if(is_special_character(C.mob))
if(C.mob?.mind?.special_role)
entry += "<b><span class='red'>[C.mob.mind.special_role]</span></b>"
else
entry += "<b><span class='red'>Antagonist</span></b>"
entry += "</td><td>"
if(C.is_afk())
var/seconds = C.last_activity_seconds()
entry += " (AFK - "
entry += "[round(seconds / 60)] minutes, "
entry += "[seconds % 60] seconds)"
entry += "</td><td>"
entry += " (<A HREF='?_src_=holder;adminmoreinfo=\ref[C.mob]'>?</A>)"
entry += "</td></tr>"
Lines += entry
else
for(var/client/C in GLOB.clients)
var/entry = "\t"
if(C.holder && C.holder.fakekey)
entry += "[C.holder.fakekey]"
else
entry += "[C.key]"
var/mob/observer/dead/O = C.mob
if(isobserver(O))
entry += " - <span class='gray'>Observing</span><br>"
else if(istype(O,/mob/new_player))
entry += " - <span class='blue'>In Lobby</span><br>"
else
entry += " - <span class='green'>Playing</span><br>"
Lines += entry
msg += "<table>"
for(var/line in sortList(Lines))
msg += "[line]"
msg += "</table>"
msg += "<b>Total Players: [length(Lines)]</b>"
msg = "<span class='filter_info'>" + msg + "</span>"
to_chat(src, msg)
@@ -0,0 +1,139 @@
GLOBAL_DATUM(character_directory, /datum/character_directory)
/client/verb/show_character_directory()
set name = "Character Directory"
set category = "OOC"
set desc = "Shows a listing of all active characters, along with their associated OOC notes, flavor text, and more."
// This is primarily to stop malicious users from trying to lag the server by spamming this verb
if(!usr.checkMoveCooldown())
to_chat(usr, "<span class='warning'>Don't spam character directory refresh.</span>")
return
usr.setMoveCooldown(10)
if(!GLOB.character_directory)
GLOB.character_directory = new
GLOB.character_directory.tgui_interact(mob)
// This is a global singleton. Keep in mind that all operations should occur on usr, not src.
/datum/character_directory
/datum/character_directory/tgui_state(mob/user)
return GLOB.tgui_always_state
/datum/character_directory/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "CharacterDirectory", "Character Directory")
ui.open()
/datum/character_directory/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["personalVisibility"] = user?.client?.prefs?.show_in_directory
data["personalTag"] = user?.client?.prefs?.directory_tag || "Unset"
data["personalErpTag"] = user?.client?.prefs?.directory_erptag || "Unset"
return data
/datum/character_directory/tgui_static_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
var/list/directory_mobs = list()
for(var/client/C in GLOB.clients)
// Allow opt-out.
if(!C?.prefs?.show_in_directory)
continue
// These are the three vars we're trying to find
// The approach differs based on the mob the client is controlling
var/name = null
var/ooc_notes = null
var/flavor_text = null
var/tag = C.prefs.directory_tag || "Unset"
var/erptag = C.prefs.directory_erptag || "Unset"
var/character_ad = C.prefs.directory_ad
if(ishuman(C.mob))
var/mob/living/carbon/human/H = C.mob
if(data_core && data_core.general)
if(!find_general_record("name", H.real_name))
if(!find_record("name", H.real_name, data_core.hidden_general))
continue
name = H.real_name
ooc_notes = H.ooc_notes
flavor_text = H.flavor_texts["general"]
if(isAI(C.mob))
var/mob/living/silicon/ai/A = C.mob
name = "[A.name] (Artificial Intelligence)"
ooc_notes = A.ooc_notes
flavor_text = null // No flavor text for AIs :c
if(isrobot(C.mob))
var/mob/living/silicon/robot/R = C.mob
if(R.scrambledcodes || (R.module && R.module.hide_on_manifest))
continue
name = "[R.name] ([R.modtype] [R.braintype])"
ooc_notes = R.ooc_notes
flavor_text = R.flavor_text
// It's okay if we fail to find OOC notes and flavor text
// But if we can't find the name, they must be using a non-compatible mob type currently.
if(!name)
continue
directory_mobs.Add(list(list(
"name" = name,
"ooc_notes" = ooc_notes,
"tag" = tag,
"erptag" = erptag,
"character_ad" = character_ad,
"flavor_text" = flavor_text,
)))
data["directory"] = directory_mobs
return data
/datum/character_directory/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
switch(action)
if("refresh")
// This is primarily to stop malicious users from trying to lag the server by spamming this verb
if(!usr.checkMoveCooldown())
to_chat(usr, "<span class='warning'>Don't spam character directory refresh.</span>")
return
usr.setMoveCooldown(10)
update_tgui_static_data(usr, ui)
return TRUE
if("setTag")
var/list/new_tag = input(usr, "Pick a new Vore tag for the character directory", "Character Tag", usr?.client?.prefs?.directory_tag) as null|anything in GLOB.char_directory_tags
if(!new_tag)
return
usr?.client?.prefs?.directory_tag = new_tag
return TRUE
if("setErpTag")
var/list/new_erptag = input(usr, "Pick a new ERP tag for the character directory", "Character ERP Tag", usr?.client?.prefs?.directory_erptag) as null|anything in GLOB.char_directory_erptags
if(!new_erptag)
return
usr?.client?.prefs?.directory_erptag = new_erptag
return TRUE
if("setVisible")
usr?.client?.prefs?.show_in_directory = !usr?.client?.prefs?.show_in_directory
to_chat(usr, "<span class='notice'>You are now [usr.client.prefs.show_in_directory ? "shown" : "not shown"] in the directory.</span>")
return TRUE
if("editAd")
if(!usr?.client?.prefs)
return
var/current_ad = usr.client.prefs.directory_ad
var/new_ad = sanitize(input(usr, "Change your character ad", "Character Ad", current_ad) as message|null, extra = 0)
if(isnull(new_ad))
return
usr.client.prefs.directory_ad = new_ad
return TRUE
+48
View File
@@ -0,0 +1,48 @@
/client/verb/ignore(key_to_ignore as text)
set name = "Ignore"
set category = "OOC"
set desc = "Makes OOC and Deadchat messages from a specific player not appear to you."
if(!key_to_ignore)
return
key_to_ignore = ckey(sanitize(key_to_ignore))
if(prefs && prefs.ignored_players)
if(key_to_ignore in prefs.ignored_players)
to_chat(usr, "<span class='warning'>[key_to_ignore] is already being ignored.</span>")
return
if(key_to_ignore == usr.ckey)
to_chat(usr, "<span class='notice'>You can't ignore yourself.</span>")
return
prefs.ignored_players |= key_to_ignore
SScharacter_setup.queue_preferences_save(prefs)
to_chat(usr, "<span class='notice'>Now ignoring <b>[key_to_ignore]</b>.</span>")
/client/verb/unignore(key_to_unignore as text)
set name = "Unignore"
set category = "OOC"
set desc = "Reverts your ignoring of a specific player."
if(!key_to_unignore)
return
key_to_unignore = ckey(sanitize(key_to_unignore))
if(prefs && prefs.ignored_players)
if(!(key_to_unignore in prefs.ignored_players))
to_chat(usr, "<span class='warning'>[key_to_unignore] isn't being ignored.</span>")
return
prefs.ignored_players -= key_to_unignore
SScharacter_setup.queue_preferences_save(prefs)
to_chat(usr, "<span class='notice'>Reverted ignore on <b>[key_to_unignore]</b>.</span>")
/mob/proc/is_key_ignored(var/key_to_check)
if(client)
return client.is_key_ignored(key_to_check)
return 0
/client/proc/is_key_ignored(var/key_to_check)
key_to_check = ckey(key_to_check)
if(key_to_check in prefs.ignored_players)
if(GLOB.directory[key_to_check] in GLOB.admins) // This is here so this is only evaluated if someone is actually being blocked.
return 0
return 1
return 0
+193
View File
@@ -0,0 +1,193 @@
/client/verb/ooc(msg as text)
set name = "OOC"
set category = "OOC"
if(say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='warning'>Speech is currently admin-disabled.</span>")
return
if(!mob) return
if(IsGuestKey(key))
to_chat(src, "Guests may not use OOC.")
return
msg = sanitize(msg)
if(!msg) return
if(!is_preference_enabled(/datum/client_preference/show_ooc))
to_chat(src, "<span class='warning'>You have OOC muted.</span>")
return
if(!holder)
if(!config.ooc_allowed)
to_chat(src, "<span class='danger'>OOC is globally muted.</span>")
return
if(!config.dooc_allowed && (mob.stat == DEAD))
to_chat(usr, "<span class='danger'>OOC for dead mobs has been turned off.</span>")
return
if(prefs.muted & MUTE_OOC)
to_chat(src, "<span class='danger'>You cannot use OOC (muted).</span>")
return
if(findtext(msg, "byond://") && !config.allow_byond_links)
to_chat(src, "<B>Advertising other servers is not allowed.</B>")
log_admin("[key_name(src)] has attempted to advertise in OOC: [msg]")
message_admins("[key_name_admin(src)] has attempted to advertise in OOC: [msg]")
return
if(findtext(msg, "discord.gg") && !config.allow_discord_links)
to_chat(src, "<B>Advertising discords is not allowed.</B>")
log_admin("[key_name(src)] has attempted to advertise a discord server in OOC: [msg]")
message_admins("[key_name_admin(src)] has attempted to advertise a discord server in OOC: [msg]")
return
if((findtext(msg, "http://") || findtext(msg, "https://")) && !config.allow_url_links)
to_chat(src, "<B>Posting external links is not allowed.</B>")
log_admin("[key_name(src)] has attempted to post a link in OOC: [msg]")
message_admins("[key_name_admin(src)] has attempted to post a link in OOC: [msg]")
return
log_ooc(msg, src)
if(msg)
handle_spam_prevention(MUTE_OOC)
var/ooc_style = "everyone"
if(holder && !holder.fakekey)
ooc_style = "elevated"
//VOREStation Block Edit Start
if(holder.rights & R_EVENT) //Retired Admins
ooc_style = "event_manager"
if(holder.rights & R_ADMIN && !(holder.rights & R_BAN)) //Game Masters
ooc_style = "moderator"
if(holder.rights & R_SERVER && !(holder.rights & R_BAN)) //Developers
ooc_style = "developer"
if(holder.rights & R_ADMIN && holder.rights & R_BAN) //Admins
ooc_style = "admin"
//VOREStation Block Edit End
for(var/client/target in GLOB.clients)
if(target.is_preference_enabled(/datum/client_preference/show_ooc))
if(target.is_key_ignored(key)) // If we're ignored by this person, then do nothing.
continue
var/display_name = src.key
if(holder)
if(holder.fakekey)
if(target.holder)
display_name = "[holder.fakekey]/([src.key])"
else
display_name = holder.fakekey
if(holder && !holder.fakekey && (holder.rights & R_ADMIN|R_FUN|R_EVENT) && config.allow_admin_ooccolor && (src.prefs.ooccolor != initial(src.prefs.ooccolor))) // keeping this for the badmins
to_chat(target, "<font color='[src.prefs.ooccolor]'><span class='ooc'>" + create_text_tag("ooc", "OOC:", target) + " <EM>[display_name]:</EM> <span class='message'>[msg]</span></span></font>")
else
to_chat(target, "<span class='ooc'><span class='[ooc_style]'>" + create_text_tag("ooc", "OOC:", target) + " <EM>[display_name]:</EM> <span class='message'>[msg]</span></span></span>")
/client/verb/looc(msg as text)
set name = "LOOC"
set desc = "Local OOC, seen only by those in view."
set category = "OOC"
if(say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
if(!mob)
return
if(IsGuestKey(key))
to_chat(src, "Guests may not use OOC.")
return
msg = sanitize(msg)
if(!msg)
return
if(!is_preference_enabled(/datum/client_preference/show_looc))
to_chat(src, "<span class='danger'>You have LOOC muted.</span>")
return
if(!holder)
if(!config.looc_allowed)
to_chat(src, "<span class='danger'>LOOC is globally muted.</span>")
return
if(!config.dooc_allowed && (mob.stat == DEAD))
to_chat(usr, "<span class='danger'>OOC for dead mobs has been turned off.</span>")
return
if(prefs.muted & MUTE_OOC)
to_chat(src, "<span class='danger'>You cannot use OOC (muted).</span>")
return
if(findtext(msg, "byond://") && !config.allow_byond_links)
to_chat(src, "<B>Advertising other servers is not allowed.</B>")
log_admin("[key_name(src)] has attempted to advertise in OOC: [msg]")
message_admins("[key_name_admin(src)] has attempted to advertise in OOC: [msg]")
return
if(findtext(msg, "discord.gg") && !config.allow_discord_links)
to_chat(src, "<B>Advertising discords is not allowed.</B>")
log_admin("[key_name(src)] has attempted to advertise a discord server in OOC: [msg]")
message_admins("[key_name_admin(src)] has attempted to advertise a discord server in OOC: [msg]")
return
if((findtext(msg, "http://") || findtext(msg, "https://")) && !config.allow_url_links)
to_chat(src, "<B>Posting external links is not allowed.</B>")
log_admin("[key_name(src)] has attempted to post a link in OOC: [msg]")
message_admins("[key_name_admin(src)] has attempted to post a link in OOC: [msg]")
return
log_looc(msg,src)
if(msg)
handle_spam_prevention(MUTE_OOC)
var/mob/source = mob.get_looc_source()
var/turf/T = get_turf(source)
if(!T) return
var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0)
var/list/m_viewers = in_range["mobs"]
var/list/receivers = list() //Clients, not mobs.
var/list/r_receivers = list()
var/display_name = key
if(holder && holder.fakekey)
display_name = holder.fakekey
if(mob.stat != DEAD)
display_name = mob.name
//VOREStation Add - Resleeving shenanigan prevention
if(ishuman(mob))
var/mob/living/carbon/human/H = mob
if(H.original_player && H.original_player != H.ckey) //In a body not their own
display_name = "[H.mind.name] (as [H.name])"
//VOREStation Add End
// Everyone in normal viewing range of the LOOC
for(var/mob/viewer in m_viewers)
if(viewer.client && viewer.client.is_preference_enabled(/datum/client_preference/show_looc))
receivers |= viewer.client
else if(istype(viewer,/mob/observer/eye)) // For AI eyes and the like
var/mob/observer/eye/E = viewer
if(E.owner && E.owner.client)
receivers |= E.owner.client
// Admins with RLOOC displayed who weren't already in
for(var/client/admin in GLOB.admins)
if(!(admin in receivers) && admin.is_preference_enabled(/datum/client_preference/holder/show_rlooc))
r_receivers |= admin
// Send a message
for(var/client/target in receivers)
var/admin_stuff = ""
if(target in GLOB.admins)
admin_stuff += "/([key])"
to_chat(target, "<span class='ooc looc'>" + create_text_tag("looc", "LOOC:", target) + " <EM>[display_name][admin_stuff]:</EM> <span class='message'>[msg]</span></span>")
for(var/client/target in r_receivers)
var/admin_stuff = "/([key])([admin_jump_link(mob, target.holder)])"
to_chat(target, "<span class='ooc looc'>" + create_text_tag("looc", "LOOC:", target) + " <span class='prefix'>(R)</span><EM>[display_name][admin_stuff]:</EM> <span class='message'>[msg]</span></span>")
/mob/proc/get_looc_source()
return src
/mob/living/silicon/ai/get_looc_source()
if(eyeobj)
return eyeobj
return src
+12
View File
@@ -0,0 +1,12 @@
/client/proc/pingfromtime(time)
return ((world.time+world.tick_lag*world.tick_usage/100)-time)*100
/client/verb/display_ping(time as num)
set instant = TRUE
set name = ".display_ping"
to_chat(src, "<span class='notice'>Round trip ping took [round(pingfromtime(time),1)]ms</span>")
/client/verb/ping()
set name = "Ping"
set category = "OOC"
winset(src, null, "command=.display_ping+[world.time+world.tick_lag*world.tick_usage/100]")
+168
View File
@@ -0,0 +1,168 @@
/mob/var/suiciding = 0
/mob/living/carbon/human/verb/suicide()
set hidden = 1
if (stat == DEAD)
to_chat(src, "You're already dead!")
return
if (!ticker)
to_chat(src, "You can't commit suicide before the game starts!")
return
if(!player_is_antag(mind))
message_admins("[ckey] has tried to suicide, but they were not permitted due to not being antagonist as human.", 1)
to_chat(src, "No. Adminhelp if there is a legitimate reason.")
return
if (suiciding)
to_chat(src, "You're already committing suicide! Be patient!")
return
var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No")
if(confirm == "Yes")
if(!canmove || restrained()) //just while I finish up the new 'fun' suiciding verb. This is to prevent metagaming via suicide
to_chat(src, "You can't commit suicide whilst restrained! ((You can type Ghost instead however.))")
return
suiciding = 15
does_not_breathe = 0 //Prevents ling-suicide zombies, or something
var/obj/item/held_item = get_active_hand()
if(held_item)
var/damagetype = held_item.suicide_act(src)
if(damagetype)
log_and_message_admins("[key_name(src)] commited suicide using \a [held_item]")
var/damage_mod = 1
switch(damagetype) //Sorry about the magic numbers.
//brute = 1, burn = 2, tox = 4, oxy = 8
if(15) //4 damage types
damage_mod = 4
if(6, 11, 13, 14) //3 damage types
damage_mod = 3
if(3, 5, 7, 9, 10, 12) //2 damage types
damage_mod = 2
if(1, 2, 4, 8) //1 damage type
damage_mod = 1
else //This should not happen, but if it does, everything should still work
damage_mod = 1
//Do 175 damage divided by the number of damage types applied.
if(damagetype & BRUTELOSS)
adjustBruteLoss(30/damage_mod) //hack to prevent gibbing
adjustOxyLoss(145/damage_mod)
if(damagetype & FIRELOSS)
adjustFireLoss(175/damage_mod)
if(damagetype & TOXLOSS)
adjustToxLoss(175/damage_mod)
if(damagetype & OXYLOSS)
adjustOxyLoss(175/damage_mod)
//If something went wrong, just do normal oxyloss
if(!(damagetype | BRUTELOSS) && !(damagetype | FIRELOSS) && !(damagetype | TOXLOSS) && !(damagetype | OXYLOSS))
adjustOxyLoss(max(175 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
updatehealth()
return
log_and_message_admins("[key_name(src)] commited suicide")
var/datum/gender/T = gender_datums[get_visible_gender()]
var/suicidemsg
suicidemsg = pick("<span class='danger'>[src] is attempting to bite [T.his] tongue off! It looks like [T.he] [T.is] trying to commit suicide.</span>", \
"<span class='danger'>[src] is jamming [T.his] thumbs into [T.his] eye sockets! It looks like [T.he] [T.is] trying to commit suicide.</span>", \
"<span class='danger'>[src] is twisting [T.his] own neck! It looks like [T.he] [T.is] trying to commit suicide.</span>", \
"<span class='danger'>[src] is holding [T.his] breath! It looks like [T.he] [T.is] trying to commit suicide.</span>")
if(isSynthetic())
suicidemsg = "<span class='danger'>[src] is attempting to switch [T.his] power off! It looks like [T.he] [T.is] trying to commit suicide.</span>"
visible_message(suicidemsg)
adjustOxyLoss(max(175 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
updatehealth()
/mob/living/carbon/brain/verb/suicide()
set hidden = 1
if (stat == 2)
to_chat(src, "You're already dead!")
return
if (!ticker)
to_chat(src, "You can't commit suicide before the game starts!")
return
if (suiciding)
to_chat(src, "You're already committing suicide! Be patient!")
return
var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No")
if(confirm == "Yes")
suiciding = 1
to_chat(viewers(loc),"<span class='danger'>[src]'s brain is growing dull and lifeless. It looks like it's lost the will to live.</span>")
spawn(50)
death(0)
suiciding = 0
/mob/living/silicon/ai/verb/suicide()
set hidden = 1
if (stat == 2)
to_chat(src, "You're already dead!")
return
if (suiciding)
to_chat(src, "You're already committing suicide! Be patient!")
return
var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No")
if(confirm == "Yes")
suiciding = 1
to_chat(viewers(src),"<span class='danger'>[src] is powering down. It looks like they're trying to commit suicide.</span>")
//put em at -175
adjustOxyLoss(max(getMaxHealth() * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
updatehealth()
/mob/living/silicon/robot/verb/suicide()
set hidden = 1
if (stat == 2)
to_chat(src, "You're already dead!")
return
if (suiciding)
to_chat(src, "You're already committing suicide! Be patient!")
return
var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No")
if(confirm == "Yes")
suiciding = 1
to_chat(viewers(src),"<span class='danger'>[src] is powering down. It looks like they're trying to commit suicide.</span>")
//put em at -175
adjustOxyLoss(max(getMaxHealth() * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
updatehealth()
/mob/living/silicon/pai/verb/suicide()
set category = "pAI Commands"
set desc = "Kill yourself and become a ghost (You will receive a confirmation prompt)"
set name = "pAI Suicide"
var/answer = input("REALLY kill yourself? This action can't be undone.", "Suicide", "No") in list ("Yes", "No")
if(answer == "Yes")
var/obj/item/device/paicard/card = loc
card.removePersonality()
var/turf/T = get_turf_or_move(card.loc)
for (var/mob/M in viewers(T))
M.show_message("<span class='notice'>[src] flashes a message across its screen, \"Wiping core files. Please acquire a new personality to continue using pAI device functions.\"</span>", 3, "<span class='notice'>[src] bleeps electronically.</span>", 2)
death(0)
else
to_chat(src, "Aborting suicide attempt.")
+208
View File
@@ -0,0 +1,208 @@
/client/verb/who()
set name = "Who"
set category = "OOC"
var/msg = "<b>Current Players:</b>\n"
var/list/Lines = list()
if(holder && (R_ADMIN & holder.rights || R_MOD & holder.rights))
for(var/client/C in GLOB.clients)
var/entry = "\t[C.key]"
if(C.holder && C.holder.fakekey)
entry += " <i>(as [C.holder.fakekey])</i>"
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/observer/dead/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>"
var/age
if(isnum(C.player_age))
age = C.player_age
else
age = 0
if(age <= 1)
age = "<font color='#ff0000'><b>[age]</b></font>"
else if(age < 10)
age = "<font color='#ff8c00'><b>[age]</b></font>"
entry += " - [age]"
if(is_special_character(C.mob))
entry += " - <b><font color='red'>Antagonist</font></b>"
if(C.is_afk())
var/seconds = C.last_activity_seconds()
entry += " (AFK - "
entry += "[round(seconds / 60)] minutes, "
entry += "[seconds % 60] seconds)"
entry += " (<A HREF='?_src_=holder;adminmoreinfo=\ref[C.mob]'>?</A>)"
Lines += entry
else
for(var/client/C in GLOB.clients)
if(C.holder && C.holder.fakekey)
Lines += C.holder.fakekey
else
Lines += C.key
for(var/line in sortList(Lines))
msg += "[line]\n"
msg += "<b>Total Players: [length(Lines)]</b>"
to_chat(src,msg)
/client/verb/staffwho()
set category = "Admin"
set name = "Staffwho"
var/message = get_staffwho_message(holder)
to_chat(src, message)
// VOREStation Edit - This whole proc has various vorestation edits throughout. Practically every other line.
/proc/get_staffwho_message(datum/admins/holder)
var/msg = ""
var/modmsg = ""
var/devmsg = ""
var/eventMmsg = ""
var/num_mods_online = 0
var/num_admins_online = 0
var/num_devs_online = 0
var/num_event_managers_online = 0
if(holder)
for(var/client/C in GLOB.admins)
if(R_ADMIN & C.holder.rights && R_BAN & C.holder.rights)
if(C.holder.fakekey && (!R_ADMIN & holder.rights && !R_MOD & holder.rights)) //Event Managerss can't see stealthmins
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(istype(C.mob,/mob/new_player))
msg += " - Lobby"
else
msg += " - Playing"
if(C.is_afk())
var/seconds = C.last_activity_seconds()
msg += " (AFK - "
msg += "[round(seconds / 60)] minutes, "
msg += "[seconds % 60] seconds)"
msg += "\n"
num_admins_online++
else if(R_ADMIN & C.holder.rights && !(R_SERVER & C.holder.rights))
modmsg += "\t[C] is a [C.holder.rank]"
if(C.holder.fakekey && (!R_ADMIN & holder.rights && !R_MOD & holder.rights))
continue
if(C.holder.fakekey)
msg += " <i>(as [C.holder.fakekey])</i>"
if(isobserver(C.mob))
modmsg += " - Observing"
else if(istype(C.mob,/mob/new_player))
modmsg += " - Lobby"
else
modmsg += " - Playing"
if(C.is_afk())
var/seconds = C.last_activity_seconds()
modmsg += " (AFK - "
modmsg += "[round(seconds / 60)] minutes, "
modmsg += "[seconds % 60] seconds)"
modmsg += "\n"
num_mods_online++
else if(R_SERVER & C.holder.rights)
if(C.holder.fakekey && (!R_ADMIN & holder.rights && !R_MOD & holder.rights))
continue
devmsg += "\t[C] is a [C.holder.rank]"
if(C.holder.fakekey)
devmsg += " <i>(as [C.holder.fakekey])</i>"
if(isobserver(C.mob))
devmsg += " - Observing"
else if(istype(C.mob,/mob/new_player))
devmsg += " - Lobby"
else
devmsg += " - Playing"
if(C.is_afk())
var/seconds = C.last_activity_seconds()
devmsg += "(AFK - "
devmsg += "[round(seconds / 60)] minutes, "
devmsg += "[seconds % 60] seconds)"
devmsg += "\n"
num_devs_online++
else
if(C.holder.fakekey && (!R_ADMIN & holder.rights && !R_MOD & holder.rights))
continue
eventMmsg += "\t[C] is a [C.holder.rank]"
if(C.holder.fakekey)
eventMmsg += " <i>(as [C.holder.fakekey])</i>"
if(isobserver(C.mob))
eventMmsg += " - Observing"
else if(istype(C.mob,/mob/new_player))
eventMmsg += " - Lobby"
else
eventMmsg += " - Playing"
if(C.is_afk())
var/seconds = C.last_activity_seconds()
eventMmsg += " (AFK - "
eventMmsg += "[round(seconds / 60)] minutes, "
eventMmsg += "[seconds % 60] seconds)"
eventMmsg += "\n"
num_event_managers_online++
else
for(var/client/C in GLOB.admins)
if(R_ADMIN & C.holder.rights && R_BAN & C.holder.rights)
if(!C.holder.fakekey)
msg += "\t[C] is a [C.holder.rank]\n"
num_admins_online++
else if(R_ADMIN & C.holder.rights && !(R_SERVER & C.holder.rights))
if(!C.holder.fakekey)
modmsg += "\t[C] is a [C.holder.rank]\n"
num_mods_online++
else if(R_SERVER & C.holder.rights)
if(!C.holder.fakekey)
devmsg += "\t[C] is a [C.holder.rank]\n"
num_devs_online++
else
if(!C.holder.fakekey)
eventMmsg += "\t[C] is a [C.holder.rank]\n"
num_event_managers_online++
msg = "<b>Current Admins ([num_admins_online]):</b>\n" + msg
if(config.show_mods)
msg += "\n<b> Current Game Masters ([num_mods_online]):</b>\n" + modmsg
if(config.show_devs)
msg += "\n<b> Current Developers ([num_devs_online]):</b>\n" + devmsg
if(config.show_event_managers)
msg += "\n<b> Current Miscellaneous ([num_event_managers_online]):</b>\n" + eventMmsg
msg += "\n<span class='info'>Adminhelps are also sent to Discord. If no admins are available in game try anyway and an admin on Discord may see it and respond.</span>"
return msg