This commit is contained in:
Poojawa
2018-03-06 21:45:16 -06:00
parent 95cb6b23f2
commit 455ee7e687
36 changed files with 691 additions and 531 deletions

View File

@@ -39,7 +39,7 @@
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.") return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect()) if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect())
log_access("Failed Login: [key] - Guests not allowed during panic bunker") log_access("Failed Login: [key] - Guests not allowed during panic bunker")
return list("reason"="guest", "desc"="\nReason: You must first join the Discord to verify your account before joining this server. Please ping an admin once you've joined and read the rules. https://discord.gg/E6SQuhz") return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.")
//Population Cap Checking //Population Cap Checking
var/extreme_popcap = CONFIG_GET(number/extreme_popcap) var/extreme_popcap = CONFIG_GET(number/extreme_popcap)

View File

@@ -1,13 +1,17 @@
GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums
GLOBAL_PROTECT(admin_ranks) GLOBAL_PROTECT(admin_ranks)
GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt
GLOBAL_PROTECT(protected_ranks)
/datum/admin_rank /datum/admin_rank
var/name = "NoRank" var/name = "NoRank"
var/rights = R_DEFAULT var/rights = R_DEFAULT
var/list/adds var/exclude_rights = 0
var/list/subs var/include_rights = 0
var/can_edit_rights = 0
/datum/admin_rank/New(init_name, init_rights, list/init_adds, list/init_subs) /datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights)
if(IsAdminAdvancedProcCall()) if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!" var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]") message_admins("[key_name_admin(usr)][msg]")
@@ -17,19 +21,18 @@ GLOBAL_PROTECT(admin_ranks)
CRASH("Admin proc call creation of admin datum") CRASH("Admin proc call creation of admin datum")
return return
name = init_name name = init_name
switch(name) if(!name)
if("Removed",null,"") qdel(src)
QDEL_IN(src, 0) throw EXCEPTION("Admin rank created without name.")
throw EXCEPTION("invalid admin-rank name") return
return
if(init_rights) if(init_rights)
rights = init_rights rights = init_rights
if(!init_adds) include_rights = rights
init_adds = list() if(init_exclude_rights)
if(!init_subs) exclude_rights = init_exclude_rights
init_subs = list() rights &= ~exclude_rights
adds = init_adds if(init_edit_rights)
subs = init_subs can_edit_rights = init_edit_rights
/datum/admin_rank/Destroy() /datum/admin_rank/Destroy()
if(IsAdminAdvancedProcCall()) if(IsAdminAdvancedProcCall())
@@ -75,13 +78,12 @@ GLOBAL_PROTECT(admin_ranks)
flag = R_SPAWN flag = R_SPAWN
if("autologin", "autoadmin") if("autologin", "autoadmin")
flag = R_AUTOLOGIN flag = R_AUTOLOGIN
if("dbranks")
flag = R_DBRANKS
if("@","prev") if("@","prev")
flag = previous_rights flag = previous_rights
return flag return flag
/proc/admin_keyword_to_path(word) //use this with verb keywords eg +/client/proc/blah
return text2path(copytext(word, 2, findtext(word, " ", 2, 0)))
// Adds/removes rights to this admin_rank // Adds/removes rights to this admin_rank
/datum/admin_rank/proc/process_keyword(word, previous_rights=0) /datum/admin_rank/proc/process_keyword(word, previous_rights=0)
if(IsAdminAdvancedProcCall()) if(IsAdminAdvancedProcCall())
@@ -94,157 +96,156 @@ GLOBAL_PROTECT(admin_ranks)
switch(text2ascii(word,1)) switch(text2ascii(word,1))
if(43) if(43)
rights |= flag //+ rights |= flag //+
include_rights |= flag
if(45) if(45)
rights &= ~flag //- rights &= ~flag //-
else exclude_rights |= flag
//isn't a keyword so maybe it's a verbpath? if(42)
var/path = admin_keyword_to_path(word) can_edit_rights |= flag //*
if(path)
switch(text2ascii(word,1))
if(43)
if(!subs.Remove(path))
adds += path //+
if(45)
if(!adds.Remove(path))
subs += path //-
// Checks for (keyword-formatted) rights on this admin // Checks for (keyword-formatted) rights on this admin
/datum/admins/proc/check_keyword(word) /datum/admins/proc/check_keyword(word)
var/flag = admin_keyword_to_flag(word) var/flag = admin_keyword_to_flag(word)
if(flag) if(flag)
return ((rank.rights & flag) == flag) //true only if right has everything in flag return ((rank.rights & flag) == flag) //true only if right has everything in flag
else
var/path = admin_keyword_to_path(word)
for(var/i in owner.verbs) //this needs to be a foreach loop for some reason. in operator and verbs.Find() don't work
if(i == path)
return 1
return 0
//load our rank - > rights associations //load our rank - > rights associations
/proc/load_admin_ranks() /proc/load_admin_ranks(dbfail)
if(IsAdminAdvancedProcCall()) if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>") to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>")
return return
GLOB.admin_ranks.Cut() GLOB.admin_ranks.Cut()
GLOB.protected_ranks.Cut()
if(CONFIG_GET(flag/admin_legacy_system)) var/previous_rights = 0
var/previous_rights = 0 //load text from file and process each line separately
//load text from file and process each line separately for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt"))
for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt")) if(!line || findtextEx(line,"#",1,2))
if(!line) continue
continue var/next = findtext(line, "=")
if(findtextEx(line,"#",1,2)) var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next)))
continue if(!R)
continue
var/next = findtext(line, "=") GLOB.admin_ranks += R
var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next))) GLOB.protected_ranks += R
if(!R) var/prev = findchar(line, "+-*", next, 0)
continue while(prev)
GLOB.admin_ranks += R next = findchar(line, "+-*", prev + 1, 0)
R.process_keyword(copytext(line, prev, next), previous_rights)
var/prev = findchar(line, "+-", next, 0) prev = next
while(prev) previous_rights = R.rights
next = findchar(line, "+-", prev + 1, 0) if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
R.process_keyword(copytext(line, prev, next), previous_rights) var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
prev = next
previous_rights = R.rights
else
if(!SSdbcore.Connect())
if(CONFIG_GET(flag/sql_enabled))
var/msg = "Failed to connect to database in load_admin_ranks(). Reverting to legacy system."
log_world(msg)
WRITE_FILE(GLOB.world_game_log, msg)
CONFIG_SET(flag/admin_legacy_system, TRUE)
load_admin_ranks()
return
var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags FROM [format_table_name("admin_ranks")]")
if(!query_load_admin_ranks.Execute()) if(!query_load_admin_ranks.Execute())
message_admins("Error loading admin ranks from database. Loading from backup.")
log_sql("Error loading admin ranks from database. Loading from backup.")
dbfail = 1
else
while(query_load_admin_ranks.NextRow())
var/skip
var/rank_name = query_load_admin_ranks.item[1]
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == rank_name) //this rank was already loaded from txt override
skip = 1
break
if(!skip)
var/rank_flags = text2num(query_load_admin_ranks.item[2])
var/rank_exclude_flags = text2num(query_load_admin_ranks.item[3])
var/rank_can_edit_flags = text2num(query_load_admin_ranks.item[4])
var/datum/admin_rank/R = new(rank_name, rank_flags, rank_exclude_flags, rank_can_edit_flags)
if(!R)
continue
GLOB.admin_ranks += R
//load ranks from backup file
if(dbfail)
var/backup_file = file("data/admins_backup.json")
if(!fexists(backup_file))
log_world("Unable to locate admins backup file.")
return return
while(query_load_admin_ranks.NextRow()) var/list/json = json_decode(file2text(backup_file))
var/rank_name = ckeyEx(query_load_admin_ranks.item[1]) for(var/J in json["ranks"])
var/flags = query_load_admin_ranks.item[2] for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(istext(flags)) if(R.name == "[J]") //this rank was already loaded from txt override
flags = text2num(flags) continue
var/datum/admin_rank/R = new(rank_name, flags) var/datum/admin_rank/R = new("[J]", json["ranks"]["[J]"]["include rights"], json["ranks"]["[J]"]["exclude rights"], json["ranks"]["[J]"]["can edit rights"])
if(!R) if(!R)
continue continue
GLOB.admin_ranks += R GLOB.admin_ranks += R
return 1
#ifdef TESTING #ifdef TESTING
var/msg = "Permission Sets Built:\n" var/msg = "Permission Sets Built:\n"
for(var/datum/admin_rank/R in GLOB.admin_ranks) for(var/datum/admin_rank/R in GLOB.admin_ranks)
msg += "\t[R.name]" msg += "\t[R.name]"
var/rights = rights2text(R.rights,"\n\t\t",R.adds,R.subs) var/rights = rights2text(R.rights,"\n\t\t")
if(rights) if(rights)
msg += "\t\t[rights]\n" msg += "\t\t[rights]\n"
testing(msg) testing(msg)
#endif #endif
/proc/load_admins() /proc/load_admins()
var/dbfail
if(!CONFIG_GET(flag/admin_legacy_system) && !SSdbcore.Connect())
message_admins("Failed to connect to database while loading admins. Loading from backup.")
log_sql("Failed to connect to database while loading admins. Loading from backup.")
dbfail = 1
//clear the datums references //clear the datums references
GLOB.admin_datums.Cut() GLOB.admin_datums.Cut()
for(var/client/C in GLOB.admins) for(var/client/C in GLOB.admins)
C.remove_admin_verbs() C.remove_admin_verbs()
C.holder = null C.holder = null
GLOB.admins.Cut() GLOB.admins.Cut()
GLOB.protected_admins.Cut()
GLOB.deadmins.Cut() GLOB.deadmins.Cut()
load_admin_ranks() dbfail = load_admin_ranks(dbfail)
//Clear profile access //Clear profile access
for(var/A in world.GetConfig("admin")) for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null) world.SetConfig("APP/admin", A, null)
var/list/rank_names = list() var/list/rank_names = list()
for(var/datum/admin_rank/R in GLOB.admin_ranks) for(var/datum/admin_rank/R in GLOB.admin_ranks)
rank_names[R.name] = R rank_names[R.name] = R
//ckeys listed in admins.txt are always made admins before sql loading is attempted
if(CONFIG_GET(flag/admin_legacy_system)) var/list/lines = world.file2list("[global.config.directory]/admins.txt")
//load text from file for(var/line in lines)
var/list/lines = world.file2list("[global.config.directory]/admins.txt") if(!length(line) || findtextEx(line, "#", 1, 2))
continue
//process each line separately var/list/entry = splittext(line, "=")
for(var/line in lines) if(entry.len < 2)
if(!length(line)) continue
continue var/ckey = ckey(entry[1])
if(findtextEx(line, "#", 1, 2)) var/rank = ckeyEx(entry[2])
continue if(!ckey || !rank)
continue
var/list/entry = splittext(line, "=") new /datum/admins(rank_names[rank], ckey, 0, 1)
if(entry.len < 2) if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
continue
var/ckey = ckey(entry[1])
var/rank = ckeyEx(entry[2])
if(!ckey || !rank)
continue
new /datum/admins(rank_names[rank], ckey)
else
if(!SSdbcore.Connect())
log_world("Failed to connect to database in load_admins(). Reverting to legacy system.")
WRITE_FILE(GLOB.world_game_log, "Failed to connect to database in load_admins(). Reverting to legacy system.")
CONFIG_SET(flag/admin_legacy_system, TRUE)
load_admins()
return
var/datum/DBQuery/query_load_admins = SSdbcore.NewQuery("SELECT ckey, rank FROM [format_table_name("admin")]") var/datum/DBQuery/query_load_admins = SSdbcore.NewQuery("SELECT ckey, rank FROM [format_table_name("admin")]")
if(!query_load_admins.Execute()) if(!query_load_admins.Execute())
message_admins("Error loading admins from database. Loading from backup.")
log_sql("Error loading admins from database. Loading from backup.")
dbfail = 1
else
while(query_load_admins.NextRow())
var/admin_ckey = query_load_admins.item[1]
var/admin_rank = query_load_admins.item[2]
var/skip
if(rank_names[admin_rank] == null)
message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].")
log_sql("[admin_ckey] loaded with invalid admin rank [admin_rank].")
skip = 1
if(GLOB.admin_datums[admin_ckey] || GLOB.deadmins[admin_ckey])
skip = 1
if(!skip)
new /datum/admins(rank_names[admin_rank], admin_ckey)
//load admins from backup file
if(dbfail)
var/backup_file = file("data/admins_backup.json")
if(!fexists(backup_file))
log_world("Unable to locate admins backup file.")
return return
while(query_load_admins.NextRow()) var/list/json = json_decode(file2text(backup_file))
var/ckey = ckey(query_load_admins.item[1]) for(var/J in json["admins"])
var/rank = ckeyEx(query_load_admins.item[2]) for(var/A in GLOB.admin_datums + GLOB.deadmins)
if(A == "[J]") //this admin was already loaded from txt override
if(rank_names[rank] == null) continue
WARNING("Admin rank ([rank]) does not exist.") new /datum/admins(rank_names[json["admins"]["[J]"]], "[J]")
continue
new /datum/admins(rank_names[rank], ckey)
#ifdef TESTING #ifdef TESTING
var/msg = "Admins Built:\n" var/msg = "Admins Built:\n"
for(var/ckey in GLOB.admin_datums) for(var/ckey in GLOB.admin_datums)
@@ -252,7 +253,7 @@ GLOBAL_PROTECT(admin_ranks)
msg += "\t[ckey] - [D.rank.name]\n" msg += "\t[ckey] - [D.rank.name]\n"
testing(msg) testing(msg)
#endif #endif
return dbfail
#ifdef TESTING #ifdef TESTING
/client/verb/changerank(newrank in GLOB.admin_ranks) /client/verb/changerank(newrank in GLOB.admin_ranks)
@@ -271,149 +272,3 @@ GLOBAL_PROTECT(admin_ranks)
remove_admin_verbs() remove_admin_verbs()
holder.associate(src) holder.associate(src)
#endif #endif
/datum/admins/proc/edit_rights_topic(list/href_list)
if(!check_rights(R_PERMISSIONS))
message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.")
log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.")
return
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>")
return
var/adm_ckey
var/task = href_list["editrights"]
switch(task)
if("add")
var/new_ckey = ckey(input(usr,"New admin's ckey","Admin ckey", null) as text|null)
if(!new_ckey)
return
if(new_ckey in GLOB.admin_datums)
to_chat(usr, "<font color='red'>Error: Topic 'editrights': [new_ckey] is already an admin</font>")
return
adm_ckey = new_ckey
task = "rank"
else
adm_ckey = ckey(href_list["ckey"])
if(!adm_ckey)
to_chat(usr, "<font color='red'>Error: Topic 'editrights': No valid ckey</font>")
return
var/datum/admins/D = GLOB.admin_datums[adm_ckey]
if (!D)
D = GLOB.deadmins[adm_ckey]
switch(task)
if("remove")
if(alert("Are you sure you want to remove [adm_ckey]?","Message","Yes","Cancel") == "Yes")
if(!D)
return
if(!check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to remove [adm_ckey] from the admins list without sufficient rights.")
log_admin("[key_name(usr)] attempted to remove [adm_ckey] from the admins list without sufficient rights.")
return
GLOB.admin_datums -= adm_ckey
GLOB.deadmins -= adm_ckey
D.disassociate()
updateranktodb(adm_ckey, "player")
message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list")
log_admin("[key_name(usr)] removed [adm_ckey] from the admins list")
log_admin_rank_modification(adm_ckey, "Removed")
if("rank")
var/datum/admin_rank/R
var/list/rank_names = list("*New Rank*")
for(R in GLOB.admin_ranks)
rank_names[R.name] = R
var/new_rank = input("Please select a rank", "New rank", null, null) as null|anything in rank_names
switch(new_rank)
if(null)
return
if("*New Rank*")
new_rank = ckeyEx(input("Please input a new rank", "New custom rank", null, null) as null|text)
if(!new_rank)
return
if(D)
if(!check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to change the rank of [adm_ckey] to [new_rank] without sufficient rights.")
log_admin("[key_name(usr)] attempted to change the rank of [adm_ckey] to [new_rank] without sufficient rights.")
return
R = rank_names[new_rank]
if(!R) //rank with that name doesn't exist yet - make it
if(D)
R = new(new_rank, D.rank.rights, D.rank.adds, D.rank.subs) //duplicate our previous admin_rank but with a new name
else
R = new(new_rank) //blank new admin_rank
GLOB.admin_ranks += R
if(D) //they were previously an admin
D.disassociate() //existing admin needs to be disassociated
D.rank = R //set the admin_rank as our rank
D.associate()
else
D = new(R, adm_ckey, TRUE) //new admin
updateranktodb(adm_ckey, new_rank)
message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
log_admin_rank_modification(adm_ckey, new_rank)
if("permissions")
if(!D)
return //they're not an admin!
var/keyword = input("Input permission keyword (one at a time):\ne.g. +BAN or -FUN or +/client/proc/someverb", "Permission toggle", null, null) as null|text
if(!keyword)
return
if(!check_keyword(keyword) || !check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to give [adm_ckey] the keyword [keyword] without sufficient rights.")
log_admin("[key_name(usr)] attempted to give [adm_ckey] the keyword [keyword] without sufficient rights.")
return
D.disassociate()
if(!findtext(D.rank.name, "([adm_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too
D.rank = new("[D.rank.name]([adm_ckey])", D.rank.rights, D.rank.adds, D.rank.subs) //duplicate our previous admin_rank but with a new name
//we don't add this clone to the admin_ranks list, as it is unique to that ckey
D.rank.process_keyword(keyword)
var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in)
D.associate(C) //link up with the client and add verbs
message_admins("[key_name(usr)] added keyword [keyword] to permission of [adm_ckey]")
log_admin("[key_name(usr)] added keyword [keyword] to permission of [adm_ckey]")
log_admin_permission_modification(adm_ckey, D.rank.rights)
if("activate") //forcefully readmin
if(!D || !D.deadmined)
return
D.activate()
message_admins("[key_name_admin(usr)] forcefully readmined [adm_ckey]")
log_admin("[key_name(usr)] forcefully readmined [adm_ckey]")
if("deactivate") //forcefully deadmin
if(!D || D.deadmined)
return
message_admins("[key_name_admin(usr)] forcefully deadmined [adm_ckey]")
log_admin("[key_name(usr)] forcefully deadmined [adm_ckey]")
D.deactivate() //after logs so the deadmined admin can see the message.
edit_admin_permissions()
/datum/admins/proc/updateranktodb(ckey,newrank)
if(!SSdbcore.Connect())
return
var/sql_ckey = sanitizeSQL(ckey)
var/sql_admin_rank = sanitizeSQL(newrank)
var/datum/DBQuery/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'")
query_admin_rank_update.Execute()

View File

@@ -18,7 +18,6 @@ GLOBAL_LIST_INIT(admin_verbs_default, world.AVerbsDefault())
/client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/ /client/proc/cmd_admin_pm_panel, /*admin-pm list*/
/client/proc/cmd_admin_ticket_panel, /client/proc/cmd_admin_ticket_panel,
/client/proc/panicbunker,
/client/proc/stop_sounds /client/proc/stop_sounds
) )
GLOBAL_PROTECT(admin_verbs_admin) GLOBAL_PROTECT(admin_verbs_admin)
@@ -117,6 +116,7 @@ GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
/client/proc/toggle_random_events, /client/proc/toggle_random_events,
/client/proc/forcerandomrotate, /client/proc/forcerandomrotate,
/client/proc/adminchangemap, /client/proc/adminchangemap,
/client/proc/panicbunker,
/client/proc/toggle_hub /client/proc/toggle_hub
) )
GLOBAL_PROTECT(admin_verbs_debug) GLOBAL_PROTECT(admin_verbs_debug)
@@ -157,7 +157,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
/client/proc/pump_random_event, /client/proc/pump_random_event,
/client/proc/cmd_display_init_log, /client/proc/cmd_display_init_log,
/client/proc/cmd_display_overlay_log, /client/proc/cmd_display_overlay_log,
/datum/admins/proc/create_or_modify_area /datum/admins/proc/create_or_modify_area,
) )
GLOBAL_PROTECT(admin_verbs_possess) GLOBAL_PROTECT(admin_verbs_possess)
GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release)) GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release))
@@ -267,11 +267,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
if(rights & R_SPAWN) if(rights & R_SPAWN)
verbs += GLOB.admin_verbs_spawn verbs += GLOB.admin_verbs_spawn
for(var/path in holder.rank.adds)
verbs += path
for(var/path in holder.rank.subs)
verbs -= path
/client/proc/remove_admin_verbs() /client/proc/remove_admin_verbs()
verbs.Remove( verbs.Remove(
GLOB.admin_verbs_default, GLOB.admin_verbs_default,
@@ -306,8 +301,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/cmd_admin_areatest_station, /client/proc/cmd_admin_areatest_station,
/client/proc/readmin /client/proc/readmin
) )
if(holder)
verbs.Remove(holder.rank.adds)
/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs /client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs
set name = "Adminverbs - Hide Most" set name = "Adminverbs - Hide Most"
@@ -528,8 +521,10 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
set desc = "Get the estimated range of a bomb, using explosive power." set desc = "Get the estimated range of a bomb, using explosive power."
var/ex_power = input("Explosive Power:") as null|num var/ex_power = input("Explosive Power:") as null|num
if (isnull(ex_power))
return
var/range = round((2 * ex_power)**GLOB.DYN_EX_SCALE) var/range = round((2 * ex_power)**GLOB.DYN_EX_SCALE)
to_chat(usr, "Estimated Explosive Range: (Devestation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])") to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])")
/client/proc/get_dynex_power() /client/proc/get_dynex_power()
set category = "Debug" set category = "Debug"
@@ -537,6 +532,8 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
set desc = "Get the estimated required power of a bomb, to reach a specific range." set desc = "Get the estimated required power of a bomb, to reach a specific range."
var/ex_range = input("Light Explosion Range:") as null|num var/ex_range = input("Light Explosion Range:") as null|num
if (isnull(ex_range))
return
var/power = (0.5 * ex_range)**(1/GLOB.DYN_EX_SCALE) var/power = (0.5 * ex_range)**(1/GLOB.DYN_EX_SCALE)
to_chat(usr, "Estimated Explosive Power: [power]") to_chat(usr, "Estimated Explosive Power: [power]")

View File

@@ -1,5 +1,7 @@
GLOBAL_LIST_EMPTY(admin_datums) GLOBAL_LIST_EMPTY(admin_datums)
GLOBAL_PROTECT(admin_datums) GLOBAL_PROTECT(admin_datums)
GLOBAL_LIST_EMPTY(protected_admins)
GLOBAL_PROTECT(protected_admins)
GLOBAL_VAR_INIT(href_token, GenerateToken()) GLOBAL_VAR_INIT(href_token, GenerateToken())
GLOBAL_PROTECT(href_token) GLOBAL_PROTECT(href_token)
@@ -26,7 +28,7 @@ GLOBAL_PROTECT(href_token)
var/deadmined var/deadmined
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE) /datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected)
if(IsAdminAdvancedProcCall()) if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!" var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]") message_admins("[key_name_admin(usr)][msg]")
@@ -51,6 +53,8 @@ GLOBAL_PROTECT(href_token)
if(R.rights & R_DEBUG) //grant profile access if(R.rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin") world.SetConfig("APP/admin", ckey, "role=admin")
//only admins with +ADMIN start admined //only admins with +ADMIN start admined
if(protected)
GLOB.protected_admins[target] = src
if (force_active || (R.rights & R_AUTOLOGIN)) if (force_active || (R.rights & R_AUTOLOGIN))
activate() activate()
else else

View File

@@ -0,0 +1,274 @@
/client/proc/edit_admin_permissions()
set category = "Admin"
set name = "Permissions Panel"
set desc = "Edit admin permissions"
if(!check_rights(R_PERMISSIONS))
return
usr.client.holder.edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions()
if(!check_rights(R_PERMISSIONS))
return
var/list/output = list({"<!DOCTYPE html>
<html>
<head>
<title>Permissions Panel</title>
<script type='text/javascript' src='search.js'></script>
<link rel='stylesheet' type='text/css' href='panels.css'>
</head>
<body onload='selectTextField();updateSearch();'>
<div id='main'><table id='searchable' cellspacing='0'>
<tr class='title'>
<th style='width:150px;text-align:right;'>CKEY <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th>
<th style='width:40%;'>PERMISSIONS</th>
<th style='width:20%;'>DENIED</th>
<th style='width:40%;'>ALLOWED TO EDIT</th>
</tr>
"})
for(var/adm_ckey in GLOB.admin_datums+GLOB.deadmins)
var/datum/admins/D = GLOB.admin_datums[adm_ckey]
if(!D)
D = GLOB.deadmins[adm_ckey]
if (!D)
continue
var/deadminlink = ""
if (D.deadmined)
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=activate;ckey=[adm_ckey]'>\[RA\]</a>"
else
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=deactivate;ckey=[adm_ckey]'>\[DA\]</a>"
output += "<tr>"
output += "<td style='text-align:right;'>[adm_ckey] [deadminlink]<a class='small' href='?src=[REF(src)];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td><a href='?src=[REF(src)];[HrefToken()];editrights=rank;ckey=[adm_ckey]'>[D.rank.name]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.include_rights," ")]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.exclude_rights," ", "-")]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.can_edit_rights," ", "*")]</a></td>"
output += "</tr>"
output += {"
</table></div>
<div id='top'><b>Search:</b> <input type='text' id='filter' value='' style='width:70%;' onkeyup='updateSearch();'></div>
</body>
</html>"}
usr << browse(jointext(output, ""),"window=editrights;size=1000x650")
/datum/admins/proc/edit_rights_topic(list/href_list)
if(!check_rights(R_PERMISSIONS))
message_admins("[key_name_admin(usr)] attempted to edit admin permissions without sufficient rights.")
log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.")
return
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>")
return
var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/permissions)
permissions_assets.send(src)
var/admin_ckey = ckey(href_list["ckey"])
var/datum/admins/D = GLOB.admin_datums[admin_ckey]
var/use_db
var/task = href_list["editrights"]
var/skip
if(task == "activate" || task == "deactivate")
skip = 1
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_admins) && task == "rank")
if(admin_ckey in GLOB.protected_admins)
to_chat(usr, "<span class='admin prefix'>Editing the rank of this admin is blocked by server configuration.</span>")
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions")
if(D.rank in GLOB.protected_ranks)
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>")
return
if(check_rights(R_DBRANKS, 0))
if(!skip)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Unable to connect to database, changes are temporary only.</span>")
use_db = "Temporary"
if(!use_db)
use_db = alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel")
if(use_db == "Cancel")
return
if(use_db == "Permanent")
use_db = 1
admin_ckey = sanitizeSQL(admin_ckey)
else
use_db = 0
if(task != "add")
D = GLOB.admin_datums[admin_ckey]
if(!D)
D = GLOB.deadmins[admin_ckey]
if(!D)
return
if(!check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to change the rank of [admin_ckey] without sufficient rights.")
log_admin("[key_name(usr)] attempted to change the rank of [admin_ckey] without sufficient rights.")
switch(task)
if("add")
admin_ckey = add_admin(use_db)
if(!admin_ckey)
return
change_admin_rank(admin_ckey, use_db)
if("remove")
remove_admin(admin_ckey, use_db, D)
if("rank")
change_admin_rank(admin_ckey, use_db, D)
if("permissions")
change_admin_flags(admin_ckey, use_db, D)
if("activate")
force_readmin(admin_ckey, D)
if("deactivate")
force_deadmin(admin_ckey, D)
edit_admin_permissions()
/datum/admins/proc/add_admin(use_db)
. = sanitizeSQL(ckey(input("New admin's ckey","Admin ckey") as text|null))
if(!.)
return 0
if(. in GLOB.admin_datums+GLOB.deadmins)
to_chat(usr, "<span class='danger'>[.] is already an admin.</span>")
return 0
if(use_db)
var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin")] (ckey, rank) VALUES ('[.]', 'NEW ADMIN')")
if(!query_add_admin.warn_execute())
return 0
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add admin', 'New admin added: [.]')")
if(!query_add_admin_log.warn_execute())
return 0
/datum/admins/proc/remove_admin(admin_ckey, use_db, datum/admins/D)
if(alert("Are you sure you want to remove [admin_ckey]?","Confirm Removal","Do it","Cancel") == "Do it")
GLOB.admin_datums -= admin_ckey
GLOB.deadmins -= admin_ckey
D.disassociate()
if(use_db)
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("DELETE FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
if(!query_add_rank.warn_execute())
return
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove admin', 'Admin removed: [admin_ckey]')")
if(!query_add_rank_log.warn_execute())
return
message_admins("[key_name_admin(usr)] removed [admin_ckey] from the admins list [use_db ? "permanently" : "temporarily"]")
log_admin("[key_name(usr)] removed [admin_ckey] from the admins list [use_db ? "permanently" : "temporarily"]")
/datum/admins/proc/force_readmin(admin_ckey, datum/admins/D)
if(!D || !D.deadmined)
return
D.activate()
message_admins("[key_name_admin(usr)] forcefully readmined [admin_ckey]")
log_admin("[key_name(usr)] forcefully readmined [admin_ckey]")
/datum/admins/proc/force_deadmin(admin_ckey, datum/admins/D)
if(!D || D.deadmined)
return
message_admins("[key_name_admin(usr)] forcefully deadmined [admin_ckey]")
log_admin("[key_name(usr)] forcefully deadmined [admin_ckey]")
D.deactivate() //after logs so the deadmined admin can see the message.
/datum/admins/proc/change_admin_rank(admin_ckey, use_db, datum/admins/D)
var/datum/admin_rank/R
var/list/rank_names = list("*New Rank*")
for(R in GLOB.admin_ranks)
if((R.rights & usr.client.holder.rank.can_edit_rights) == R.rights)
rank_names[R.name] = R
var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names
if(new_rank == "*New Rank*")
new_rank = sanitizeSQL(ckeyEx(input("Please input a new rank", "New custom rank") as text|null))
if(!new_rank)
return
R = rank_names[new_rank]
if(!R) //rank with that name doesn't exist yet - make it
if(D)
R = new(new_rank, D.rank.rights) //duplicate our previous admin_rank but with a new name
else
R = new(new_rank) //blank new admin_rank
GLOB.admin_ranks += R
if(use_db)
if(!R)
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_ranks")] (rank, flags, exclude_flags, can_edit_rights) VALUES ('[new_rank]', '0', '0', '0')")
if(!query_add_rank.warn_execute())
return
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add rank', 'New rank added: [admin_ckey]')")
if(!query_add_rank_log.warn_execute())
return
var/old_rank
var/datum/DBQuery/query_get_rank = SSdbcore.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
if(!query_get_rank.warn_execute())
return
if(query_get_rank.NextRow())
old_rank = query_get_rank.item[1]
var/datum/DBQuery/query_change_rank = SSdbcore.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE ckey = '[admin_ckey]'")
if(!query_change_rank.warn_execute())
return
var/datum/DBQuery/query_change_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change admin rank', 'Rank of [admin_ckey] changed from [old_rank] to [new_rank]')")
if(!query_change_rank_log.warn_execute())
return
if(D) //they were previously an admin
D.disassociate() //existing admin needs to be disassociated
D.rank = R //set the admin_rank as our rank
D.associate()
else
D = new(R, admin_ckey, TRUE) //new admin
message_admins("[key_name_admin(usr)] edited the admin rank of [admin_ckey] to [new_rank] [use_db ? "permanently" : "temporarily"]")
log_admin("[key_name(usr)] edited the admin rank of [admin_ckey] to [new_rank] [use_db ? "permanently" : "temporarily"]")
/datum/admins/proc/change_admin_flags(admin_ckey, use_db, datum/admins/D)
var/new_flags = input_bitfield(usr, "Include permission flags<br>[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.include_rights, 350, 590, allowed_edit_list = usr.client.holder.rank.can_edit_rights)
if(isnull(new_flags))
return
var/new_exclude_flags = input_bitfield(usr, "Exclude permission flags<br>Flags enabled here will be removed from a rank.<br>Note these take precedence over included flags.<br>[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.exclude_rights, 350, 660, "red", usr.client.holder.rank.can_edit_rights)
if(isnull(new_exclude_flags))
return
var/new_can_edit_flags = input_bitfield(usr, "Editable permission flags<br>These are the flags this rank is allowed to edit if they have access to the permissions panel.<br>They will be unable to modify admins to a rank that has a flag not included here.<br>[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.can_edit_rights, 350, 710, allowed_edit_list = usr.client.holder.rank.can_edit_rights)
if(isnull(new_can_edit_flags))
return
if(use_db)
var/old_flags
var/old_exclude_flags
var/old_can_edit_flags
var/datum/DBQuery/query_get_rank_flags = SSdbcore.NewQuery("SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = '[D.rank.name]'")
if(!query_get_rank_flags.warn_execute())
return
if(query_get_rank_flags.NextRow())
old_flags = text2num(query_get_rank_flags.item[1])
old_exclude_flags = text2num(query_get_rank_flags.item[2])
old_can_edit_flags = text2num(query_get_rank_flags.item[3])
var/datum/DBQuery/query_change_rank_flags = SSdbcore.NewQuery("UPDATE [format_table_name("admin_ranks")] SET flags = '[new_flags]', exclude_flags = '[new_exclude_flags]', can_edit_flags = '[new_can_edit_flags]' WHERE rank = '[D.rank.name]'")
if(!query_change_rank_flags.warn_execute())
return
var/datum/DBQuery/query_change_rank_flags_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change rank flags', 'Permissions of [admin_ckey] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]')")
if(!query_change_rank_flags_log.warn_execute())
return
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name != D.rank.name)
continue
R.rights = new_flags &= ~new_exclude_flags
R.exclude_rights = new_exclude_flags
R.include_rights = new_flags
R.can_edit_rights = new_can_edit_flags
for(var/i in GLOB.admin_datums+GLOB.deadmins)
var/datum/admins/A = GLOB.admin_datums[i]
if(!A)
A = GLOB.deadmins[i]
if (!A)
continue
if(A.rank.name != D.rank.name)
continue
var/client/C = GLOB.directory[A.target]
A.disassociate()
A.associate(C)
else
D.disassociate()
if(!findtext(D.rank.name, "([admin_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too
D.rank = new("[D.rank.name]([admin_ckey])", new_flags, new_exclude_flags, new_can_edit_flags) //duplicate our previous admin_rank but with a new name
//we don't add this clone to the admin_ranks list, as it is unique to that ckey
else
D.rank.rights = new_flags &= ~new_exclude_flags
D.rank.include_rights = new_flags
D.rank.exclude_rights = new_exclude_flags
var/client/C = GLOB.directory[admin_ckey] //find the client with the specified ckey (if they are logged in)
D.associate(C) //link up with the client and add verbs
message_admins("[key_name_admin(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_ckey] temporarily"]")
log_admin("[key_name(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_ckey] temporarily"]")

View File

@@ -31,6 +31,7 @@
<A href='?src=[REF(src)];[HrefToken()];secrets=tdomereset'>Reset Thunderdome to default state</A><BR> <A href='?src=[REF(src)];[HrefToken()];secrets=tdomereset'>Reset Thunderdome to default state</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=set_name'>Rename Station Name</A><BR> <A href='?src=[REF(src)];[HrefToken()];secrets=set_name'>Rename Station Name</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=reset_name'>Reset Station Name</A><BR> <A href='?src=[REF(src)];[HrefToken()];secrets=reset_name'>Reset Station Name</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=night_shift_set'>Set Night Shift Mode</A><BR>
<BR> <BR>
<B>Shuttles</B><BR> <B>Shuttles</B><BR>
<BR> <BR>
@@ -54,7 +55,6 @@
<A href='?src=[REF(src)];[HrefToken()];secrets=quickpower'>Power all SMES</A><BR> <A href='?src=[REF(src)];[HrefToken()];secrets=quickpower'>Power all SMES</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=tripleAI'>Triple AI mode (needs to be used in the lobby)</A><BR> <A href='?src=[REF(src)];[HrefToken()];secrets=tripleAI'>Triple AI mode (needs to be used in the lobby)</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=traitor_all'>Everyone is the traitor</A><BR> <A href='?src=[REF(src)];[HrefToken()];secrets=traitor_all'>Everyone is the traitor</A><BR>
<A href='?src=\ref[src];[HrefToken()];secrets=ak47s'>AK-47s For Everyone!</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=guns'>Summon Guns</A><BR> <A href='?src=[REF(src)];[HrefToken()];secrets=guns'>Summon Guns</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=magic'>Summon Magic</A><BR> <A href='?src=[REF(src)];[HrefToken()];secrets=magic'>Summon Magic</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=events'>Summon Events (Toggle)</A><BR> <A href='?src=[REF(src)];[HrefToken()];secrets=events'>Summon Events (Toggle)</A><BR>
@@ -107,8 +107,6 @@
dat += "No-one has done anything this round!" dat += "No-one has done anything this round!"
usr << browse(dat, "window=admin_log") usr << browse(dat, "window=admin_log")
if("mentor_log")
CitadelMentorLogSecret()
if("list_job_debug") if("list_job_debug")
var/dat = "<B>Job Debug info.</B><HR>" var/dat = "<B>Job Debug info.</B><HR>"
for(var/line in SSjob.job_debug) for(var/line in SSjob.job_debug)
@@ -167,6 +165,23 @@
log_admin("[key_name(usr)] renamed the station to \"[new_name]\".") log_admin("[key_name(usr)] renamed the station to \"[new_name]\".")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] renamed the station to: [new_name].</span>") message_admins("<span class='adminnotice'>[key_name_admin(usr)] renamed the station to: [new_name].</span>")
priority_announce("[command_name()] has renamed the station to \"[new_name]\".") priority_announce("[command_name()] has renamed the station to \"[new_name]\".")
if("night_shift_set")
if(!check_rights(R_ADMIN))
return
var/val = alert(usr, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "On", "Off", "Automatic")
switch(val)
if("Automatic")
if(CONFIG_GET(flag/enable_night_shifts))
SSnightshift.can_fire = TRUE
SSnightshift.fire()
else
SSnightshift.update_nightshift(FALSE, TRUE)
if("On")
SSnightshift.can_fire = FALSE
SSnightshift.update_nightshift(TRUE, TRUE)
if("Off")
SSnightshift.can_fire = FALSE
SSnightshift.update_nightshift(FALSE, TRUE)
if("reset_name") if("reset_name")
if(!check_rights(R_ADMIN)) if(!check_rights(R_ADMIN))
@@ -460,13 +475,6 @@
message_admins("[key_name_admin(usr)] activated Egalitarian Station mode") message_admins("[key_name_admin(usr)] activated Egalitarian Station mode")
priority_announce("CentCom airlock control override activated. Please take this time to get acquainted with your coworkers.", null, 'sound/ai/commandreport.ogg') priority_announce("CentCom airlock control override activated. Please take this time to get acquainted with your coworkers.", null, 'sound/ai/commandreport.ogg')
if("ak47s")
if(!check_rights(R_FUN))
return
message_admins("[key_name_admin(usr)] activated AK-47s for Everyone!")
usr.client.ak47s()
sound_to_playing_players('sound/misc/ak47s.ogg')
if("guns") if("guns")
if(!check_rights(R_FUN)) if(!check_rights(R_FUN))
return return
@@ -613,13 +621,13 @@
var/list/new_movement = list() var/list/new_movement = list()
for(var/i in 1 to movement_keys.len) for(var/i in 1 to movement_keys.len)
var/key = movement_keys[i] var/key = movement_keys[i]
var/msg = "Please input the new movement direction when the user presses [key]. Ex. northeast" var/msg = "Please input the new movement direction when the user presses [key]. Ex. northeast"
var/title = "New direction for [key]" var/title = "New direction for [key]"
var/new_direction = text2dir(input(usr, msg, title) as text|null) var/new_direction = text2dir(input(usr, msg, title) as text|null)
if(!new_direction) if(!new_direction)
new_direction = movement_keys[key] new_direction = movement_keys[key]
new_movement[key] = new_direction new_movement[key] = new_direction
SSinput.movement_keys = new_movement SSinput.movement_keys = new_movement
message_admins("[key_name_admin(usr)] has configured all movement directions.") message_admins("[key_name_admin(usr)] has configured all movement directions.")

View File

@@ -271,50 +271,6 @@
return return
create_message("note", banckey, null, banreason, null, null, 0, 0) create_message("note", banckey, null, banreason, null, null, 0, 0)
else if(href_list["mentor"])
if(!check_rights(R_ADMIN)) return
var/mob/M = locate(href_list["mentor"])
if(!ismob(M))
to_chat(usr, "<span class='danger'>this can be only used on instances of type /mob!</span>")
return
if(!M.client)
to_chat(usr, "<span class='danger'>No client.</span>")
return
log_admin("[key_name(usr)] has granted [key_name(M)] mentor access")
message_admins("<span class='adminnotice'> [key_name_admin(usr)] has granted [key_name_admin(M)] mentor access.</span>")
var/datum/DBQuery/query_add_mentors = SSdbcore.NewQuery("INSERT INTO [format_table_name("mentor")] (ckey) VALUES ('[M.client.ckey]')")
if(!query_add_mentors.Execute())
var/err = query_add_mentors.ErrorMsg()
log_game("SQL ERROR during adding new mentor. Error : \[[err]\]\n")
load_mentors()
M.verbs += /client/proc/cmd_mentor_say
M.verbs += /client/proc/show_mentor_memo
to_chat(M, "<span class='adminnotice'> You've been granted mentor access! Help people who send mentor-pms.</span>")
else if(href_list["removementor"])
if(!check_rights(R_ADMIN)) return
var/mob/living/carbon/human/M = locate(href_list["removementor"])
if(!ismob(M))
usr << "this can be only used on instances of type /mob"
return
log_admin("[key_name(usr)] has removed mentor access from [key_name(M)]")
message_admins("<span class='adminnotice'> [key_name_admin(usr)] has removed mentor access from [key_name_admin(M)].</span>")
var/datum/DBQuery/query_remove_mentors = SSdbcore.NewQuery("DELETE FROM [format_table_name("mentor")] WHERE ckey = '[M.client.ckey]'")
if(!query_remove_mentors.Execute())
var/err = query_remove_mentors.ErrorMsg()
log_game("SQL ERROR during removing mentor. Error : \[[err]\]\n")
load_mentors()
to_chat(M, "<span class='adminnotice'>Your mentor access has been revoked.</span>")
M.verbs -= /client/proc/cmd_mentor_say
M.verbs -= /client/proc/show_mentor_memo
else if(href_list["editrights"]) else if(href_list["editrights"])
edit_rights_topic(href_list) edit_rights_topic(href_list)
@@ -916,12 +872,6 @@
else else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=abductor;jobban4=[REF(M)]'>Abductor</a></td>" dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=abductor;jobban4=[REF(M)]'>Abductor</a></td>"
//Borer
if(jobban_isbanned(M, "borer") || isbanned_dept)
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=borer;jobban4=[REF(M)]'><font color=red>Borer</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=borer;jobban4=[REF(M)]'>Borer</a></td>"
//Alien //Alien
if(jobban_isbanned(M, ROLE_ALIEN) || isbanned_dept) if(jobban_isbanned(M, ROLE_ALIEN) || isbanned_dept)
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=alien;jobban4=[REF(M)]'><font color=red>Alien</font></a></td>" dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=alien;jobban4=[REF(M)]'><font color=red>Alien</font></a></td>"
@@ -1709,7 +1659,7 @@
var/mob/living/L = M var/mob/living/L = M
var/status var/status
switch (M.stat) switch (M.stat)
if (CONSCIOUS) if(CONSCIOUS)
status = "Alive" status = "Alive"
if(SOFT_CRIT) if(SOFT_CRIT)
status = "<font color='orange'><b>Dying</b></font>" status = "<font color='orange'><b>Dying</b></font>"

View File

@@ -24,7 +24,7 @@
admin_sound.status = SOUND_STREAM admin_sound.status = SOUND_STREAM
admin_sound.volume = vol admin_sound.volume = vol
var/res = alert(usr, "Show the title of this song to the players?",, "No", "Yes", "Cancel") var/res = alert(usr, "Show the title of this song to the players?",, "Yes","No", "Cancel")
switch(res) switch(res)
if("Yes") if("Yes")
to_chat(world, "<span class='boldannounce'>An admin played: [S]</span>") to_chat(world, "<span class='boldannounce'>An admin played: [S]</span>")

View File

@@ -4,6 +4,7 @@
//Admin-spawn or random event //Admin-spawn or random event
#define INVISIBILITY_REVENANT 50 #define INVISIBILITY_REVENANT 50
#define REVENANT_NAME_FILE "revenant_names.json"
/mob/living/simple_animal/revenant /mob/living/simple_animal/revenant
name = "\a Revenant" name = "\a Revenant"
@@ -70,6 +71,15 @@
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/overload(null)) AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/overload(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/blight(null)) AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/blight(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction(null)) AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction(null))
random_revenant_name()
/mob/living/simple_animal/revenant/proc/random_revenant_name()
var/built_name = ""
built_name += pick(strings(REVENANT_NAME_FILE, "spirit_type"))
built_name += " of "
built_name += pick(strings(REVENANT_NAME_FILE, "adverb"))
built_name += pick(strings(REVENANT_NAME_FILE, "theme"))
name = built_name
/mob/living/simple_animal/revenant/Login() /mob/living/simple_animal/revenant/Login()
..() ..()

View File

@@ -98,11 +98,11 @@
var/list/new_overlay_types = tile_graphic() var/list/new_overlay_types = tile_graphic()
var/list/atmos_overlay_types = src.atmos_overlay_types // Cache for free performance var/list/atmos_overlay_types = src.atmos_overlay_types // Cache for free performance
/*#if DM_VERSION >= 513 #if DM_VERSION >= 513
#warning 512 is stable now for sure, remove the old code #warning 512 is stable now for sure, remove the old code
#endif*/ #endif
/*#if DM_VERSION >= 512 #if DM_VERSION >= 512
if (atmos_overlay_types) if (atmos_overlay_types)
for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
vars["vis_contents"] -= overlay vars["vis_contents"] -= overlay
@@ -112,7 +112,7 @@
vars["vis_contents"] += new_overlay_types - atmos_overlay_types //don't add overlays that already exist vars["vis_contents"] += new_overlay_types - atmos_overlay_types //don't add overlays that already exist
else else
vars["vis_contents"] += new_overlay_types vars["vis_contents"] += new_overlay_types
#else*/ #else
if (atmos_overlay_types) if (atmos_overlay_types)
for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
cut_overlay(overlay) cut_overlay(overlay)
@@ -122,7 +122,7 @@
add_overlay(new_overlay_types - atmos_overlay_types) //don't add overlays that already exist add_overlay(new_overlay_types - atmos_overlay_types) //don't add overlays that already exist
else else
add_overlay(new_overlay_types) add_overlay(new_overlay_types)
//#endif #endif
UNSETEMPTY(new_overlay_types) UNSETEMPTY(new_overlay_types)
src.atmos_overlay_types = new_overlay_types src.atmos_overlay_types = new_overlay_types

View File

@@ -57,11 +57,6 @@ It's like a regular ol' straight pipe, but you can turn it on and off.
close() close()
return return
open() open()
var/turf/T = get_turf(src)
var/area/A = get_area(src)
investigate_log("Valve, [src.name], was manipiulated by [key_name(usr)] at [x], [y], [z], [A]", "atmos")
message_admins("Valve, [src.name], was manipulated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_COORDJMP(T)], [A]")
/obj/machinery/atmospherics/components/binary/valve/digital // can be controlled by AI /obj/machinery/atmospherics/components/binary/valve/digital // can be controlled by AI
name = "digital valve" name = "digital valve"

View File

@@ -57,7 +57,7 @@
continue // i'd be right happy to continue // i'd be right happy to
meme_pack_data[P.group]["packs"] += list(list( meme_pack_data[P.group]["packs"] += list(list(
"name" = P.name, "name" = P.name,
"cost" = P.cost * 2, //displays twice the normal cost "cost" = P.cost,
"id" = pack "id" = pack
)) ))
@@ -120,12 +120,12 @@
CHECK_TICK CHECK_TICK
if(empty_turfs && empty_turfs.len) if(empty_turfs && empty_turfs.len)
var/LZ = empty_turfs[rand(empty_turfs.len-1)] var/LZ = empty_turfs[rand(empty_turfs.len-1)]
SSshuttle.points -= SO.pack.cost * 2 SSshuttle.points -= SO.pack.cost
new /obj/effect/DPtarget(LZ, SO, podID) new /obj/effect/DPtarget(LZ, SO, podID)
. = TRUE . = TRUE
update_icon() update_icon()
else else
if(SO.pack.cost * (1.2*MAX_EMAG_ROCKETS) <= SSshuttle.points) // bulk discount :^) if(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS) <= SSshuttle.points) // bulk discount :^)
landingzone = locate(pick(GLOB.the_station_areas)) in GLOB.sortedAreas landingzone = locate(pick(GLOB.the_station_areas)) in GLOB.sortedAreas
for(var/turf/open/floor/T in landingzone.contents) for(var/turf/open/floor/T in landingzone.contents)
if(is_blocked_turf(T)) if(is_blocked_turf(T))

View File

@@ -988,8 +988,9 @@
/obj/item/reagent_containers/pill/insulin, /obj/item/reagent_containers/pill/insulin,
/obj/item/stack/medical/gauze, /obj/item/stack/medical/gauze,
/obj/item/storage/box/beakers, /obj/item/storage/box/beakers,
/obj/item/storage/box/medsprays,
/obj/item/storage/box/syringes, /obj/item/storage/box/syringes,
/obj/item/storage/box/bodybags) /obj/item/storage/box/bodybags)
crate_name = "medical supplies crate" crate_name = "medical supplies crate"
/datum/supply_pack/medical/vending /datum/supply_pack/medical/vending
@@ -1983,4 +1984,4 @@
/obj/item/toy/redbutton, /obj/item/toy/redbutton,
/obj/item/toy/eightball, /obj/item/toy/eightball,
/obj/item/vending_refill/donksoft) /obj/item/vending_refill/donksoft)
crate_name = "toy crate" crate_name = "toy crate"

View File

@@ -97,7 +97,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
if(!verify) // Can't access the asset cache browser, rip. if(!verify) // Can't access the asset cache browser, rip.
client.cache += unreceived client.cache += unreceived
return 1 return 1
client.sending |= unreceived client.sending |= unreceived
var/job = ++client.last_asset_job var/job = ++client.last_asset_job
@@ -135,7 +135,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
else else
concurrent_tracker++ concurrent_tracker++
send_asset(client, file, verify=FALSE) send_asset(client, file, verify=FALSE)
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. //This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
@@ -350,6 +350,11 @@ GLOBAL_LIST_EMPTY(asset_datums)
"browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css', "browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
) )
/datum/asset/simple/permissions
assets = list(
"padlock.png" = 'html/padlock.png'
)
//this exists purely to avoid meta by pre-loading all language icons. //this exists purely to avoid meta by pre-loading all language icons.
/datum/asset/language/register() /datum/asset/language/register()
for(var/path in typesof(/datum/language)) for(var/path in typesof(/datum/language))

View File

@@ -88,22 +88,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
cmd_admin_pm(href_list["priv_msg"],null) cmd_admin_pm(href_list["priv_msg"],null)
return return
// Mentor PM
if(href_list["mentor_msg"])
if(CONFIG_GET(flag.mentors_mobname_only))
var/mob/M = locate(href_list["mentor_msg"])
cmd_mentor_pm(M,null)
else
cmd_mentor_pm(href_list["mentor_msg"],null)
return
switch(href_list["_src_"]) switch(href_list["_src_"])
if("holder") if("holder")
hsrc = holder hsrc = holder
if("usr") if("usr")
hsrc = mob hsrc = mob
if("mentor") // CITADEL
hsrc = mentor_datum // CITADEL END
if("prefs") if("prefs")
if (inprefs) if (inprefs)
return return
@@ -239,16 +228,23 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
. = ..() //calls mob.Login() . = ..() //calls mob.Login()
#if DM_VERSION >= 512 #if DM_VERSION >= 512
if (num2text(byond_build) in GLOB.blacklisted_builds) if (byond_version >= 512)
log_access("Failed login: blacklisted byond version") if (!byond_build || byond_build < 1386)
to_chat(src, "<span class='userdanger'>Your version of byond is blacklisted.</span>") message_admins("<span class='adminnotice'>[key_name(src)] has been detected as spoofing their byond version. Connection rejected.</span>")
to_chat(src, "<span class='danger'>Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].</span>") add_system_note("Spoofed-Byond-Version", "Detected as using a spoofed byond version.")
to_chat(src, "<span class='danger'>Please download a new version of byond. if [byond_build] is the latest, you can go to http://www.byond.com/download/build/ to download other versions.</span>") log_access("Failed Login: [key] - Spoofed byond version")
if(connecting_admin)
to_chat(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions")
else
qdel(src) qdel(src)
return
if (num2text(byond_build) in GLOB.blacklisted_builds)
log_access("Failed login: [key] - blacklisted byond version")
to_chat(src, "<span class='userdanger'>Your version of byond is blacklisted.</span>")
to_chat(src, "<span class='danger'>Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].</span>")
to_chat(src, "<span class='danger'>Please download a new version of byond. if [byond_build] is the latest, you can go to http://www.byond.com/download/build/ to download other versions.</span>")
if(connecting_admin)
to_chat(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions")
else
qdel(src)
return
#endif #endif
if(SSinput.initialized) if(SSinput.initialized)
set_macros() set_macros()
@@ -380,8 +376,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if (menuitem) if (menuitem)
menuitem.Load_checked(src) menuitem.Load_checked(src)
hook_vr("client_new",list(src)) // CIT CHANGE - hook for client/New() changes
Master.UpdateTickRate() Master.UpdateTickRate()
////////////// //////////////
@@ -409,21 +403,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
"Someone come hold me :(",\ "Someone come hold me :(",\
"I need someone on me :(",\ "I need someone on me :(",\
"What happened? Where has everyone gone?",\ "What happened? Where has everyone gone?",\
"Forever alone :(",\ "Forever alone :("\
"My nipples are so stiff, but Zelda ain't here. :(",\
"Leon senpai, play more Spessmans. :(",\
"If only Serdy were here...",\
"Panic bunker can't keep my love for you out.",\
"Cebu needs to Awoo herself back into my heart.",\
"I don't even have a Turry to snuggle viciously here.",\
"MOM, WHERE ARE YOU??? D:",\
"It's a beautiful day outside. Birds are singing, flowers are blooming. On days like this...kids like you...SHOULD BE BURNING IN HELL.",\
"Sometimes when I have sex, I think about putting an entire peanut butter and jelly sandwich in the VCR.",\
"Oh good, no-one around to watch me lick Goofball's nipples. :D",\
"I've replaced Beepsky with a fidget spinner, glory be autism abuse.",\
"i shure hop dere are no PRED arund!!!!",\
"NO PRED CAN eVER CATCH MI",\
"help, the clown is honking his horn in front of dorms and its interrupting everyones erp"\
) )
send2irc("Server", "[cheesy_message] (No admins online)") send2irc("Server", "[cheesy_message] (No admins online)")
@@ -623,10 +603,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
to_chat(src, {"<a href="byond://[url]?token=[token]">You will be automatically taken to the game, if not, click here to be taken manually</a>"}) to_chat(src, {"<a href="byond://[url]?token=[token]">You will be automatically taken to the game, if not, click here to be taken manually</a>"})
/client/proc/note_randomizer_user() /client/proc/note_randomizer_user()
var/const/adminckey = "CID-Error" add_system_note("CID-Error", "Detected as using a cid randomizer.")
/client/proc/add_system_note(system_ckey, message)
var/sql_system_ckey = sanitizeSQL(system_ckey)
var/sql_ckey = sanitizeSQL(ckey) var/sql_ckey = sanitizeSQL(ckey)
//check to see if we noted them in the last day. //check to see if we noted them in the last day.
var/datum/DBQuery/query_get_notes = SSdbcore.NewQuery("SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = '[sql_ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW() AND deleted = 0") var/datum/DBQuery/query_get_notes = SSdbcore.NewQuery("SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = '[sql_ckey]' AND adminckey = '[sql_system_ckey]' AND timestamp + INTERVAL 1 DAY < NOW() AND deleted = 0")
if(!query_get_notes.Execute()) if(!query_get_notes.Execute())
return return
if(query_get_notes.NextRow()) if(query_get_notes.NextRow())
@@ -636,9 +619,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if(!query_get_notes.Execute()) if(!query_get_notes.Execute())
return return
if(query_get_notes.NextRow()) if(query_get_notes.NextRow())
if (query_get_notes.item[1] == adminckey) if (query_get_notes.item[1] == system_ckey)
return return
create_message("note", sql_ckey, adminckey, "Detected as using a cid randomizer.", null, null, 0, 0) create_message("note", ckey, system_ckey, message, null, null, 0, 0)
/client/proc/check_ip_intel() /client/proc/check_ip_intel()
@@ -730,12 +713,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if (isnull(new_size)) if (isnull(new_size))
CRASH("change_view called without argument.") CRASH("change_view called without argument.")
//CIT CHANGES START HERE - makes change_view change DEFAULT_VIEW to 15x15 depending on preferences
if(prefs && CONFIG_GET(string/default_view))
if(!prefs.widescreenpref && new_size == CONFIG_GET(string/default_view))
new_size = "15x15"
//END OF CIT CHANGES
view = new_size view = new_size
apply_clickcatcher() apply_clickcatcher()
if (isliving(mob)) if (isliving(mob))
@@ -754,4 +731,4 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
/client/proc/AnnouncePR(announcement) /client/proc/AnnouncePR(announcement)
if(prefs && prefs.chat_toggles & CHAT_PULLR) if(prefs && prefs.chat_toggles & CHAT_PULLR)
to_chat(src, announcement) to_chat(src, announcement)

View File

@@ -250,20 +250,6 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings, listen_ooc)()
/datum/verbs/menu/Settings/listen_ooc/Get_checked(client/C) /datum/verbs/menu/Settings/listen_ooc/Get_checked(client/C)
return C.prefs.chat_toggles & CHAT_OOC return C.prefs.chat_toggles & CHAT_OOC
TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglehoundsleeper)()
set name = "Allow/Deny Hound Sleeper"
set category = "Preferences"
set desc = "Allow MediHound Sleepers"
usr.client.prefs.toggles ^= MEDIHOUND_SLEEPER
usr.client.prefs.save_preferences()
if(usr.client.prefs.toggles & MEDIHOUND_SLEEPER)
to_chat(usr, "You will now allow MediHounds to place you in their sleeper.")
else
to_chat(usr, "You will no longer allow MediHounds to place you in their sleeper.")
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle MediHound Sleeper", "[usr.client.prefs.toggles & MEDIHOUND_SLEEPER ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/verbs/menu/Settings/Sound/togglehoundsleeper/Get_checked(client/C)
return C.prefs.toggles & MEDIHOUND_SLEEPER
GLOBAL_LIST_INIT(ghost_forms, list("ghost","ghostking","ghostian2","skeleghost","ghost_red","ghost_black", \ GLOBAL_LIST_INIT(ghost_forms, list("ghost","ghostking","ghostian2","skeleghost","ghost_red","ghost_black", \
"ghost_blue","ghost_yellow","ghost_green","ghost_pink", \ "ghost_blue","ghost_yellow","ghost_green","ghost_pink", \
@@ -429,4 +415,3 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS
prefs.save_preferences() prefs.save_preferences()
to_chat(src, "You will [(prefs.chat_toggles & CHAT_PRAYER) ? "now" : "no longer"] see prayerchat.") to_chat(src, "You will [(prefs.chat_toggles & CHAT_PRAYER) ? "now" : "no longer"] see prayerchat.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Prayer Visibility", "[prefs.chat_toggles & CHAT_PRAYER ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Prayer Visibility", "[prefs.chat_toggles & CHAT_PRAYER ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

View File

@@ -20,6 +20,7 @@
var/blood_type = null var/blood_type = null
var/list/features = null var/list/features = null
var/factions = null var/factions = null
var/list/traits = null
var/contains_sample = 0 var/contains_sample = 0
/obj/item/seeds/replicapod/attackby(obj/item/W, mob/user, params) /obj/item/seeds/replicapod/attackby(obj/item/W, mob/user, params)
@@ -34,6 +35,7 @@
blood_type = bloodSample.data["blood_type"] blood_type = bloodSample.data["blood_type"]
features = bloodSample.data["features"] features = bloodSample.data["features"]
factions = bloodSample.data["factions"] factions = bloodSample.data["factions"]
traits = bloodSample.data["traits"]
W.reagents.clear_reagents() W.reagents.clear_reagents()
to_chat(user, "<span class='notice'>You inject the contents of the syringe into the seeds.</span>") to_chat(user, "<span class='notice'>You inject the contents of the syringe into the seeds.</span>")
contains_sample = 1 contains_sample = 1
@@ -99,6 +101,8 @@
podman.faction |= factions podman.faction |= factions
if(!features["mcolor"]) if(!features["mcolor"])
features["mcolor"] = "#59CE00" features["mcolor"] = "#59CE00"
for(var/V in traits)
new V(podman)
podman.hardset_dna(null,null,podman.real_name,blood_type, new /datum/species/pod,features)//Discard SE's and UI's, podman cloning is inaccurate, and always make them a podman podman.hardset_dna(null,null,podman.real_name,blood_type, new /datum/species/pod,features)//Discard SE's and UI's, podman cloning is inaccurate, and always make them a podman
podman.set_cloned_appearance() podman.set_cloned_appearance()

View File

@@ -160,3 +160,4 @@ GLOBAL_LIST_EMPTY(z_is_planet)
. = ..() . = ..()
var/turf/T = get_turf(src) var/turf/T = get_turf(src)
GLOB.z_is_planet["[T.z]"] = TRUE GLOB.z_is_planet["[T.z]"] = TRUE

View File

@@ -125,4 +125,4 @@
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi' righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
force = 5 force = 5
throwforce = 7 throwforce = 7
w_class = WEIGHT_CLASS_SMALL w_class = WEIGHT_CLASS_SMALL

View File

@@ -379,9 +379,8 @@
if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergencyCallTime)*0.5) if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergencyCallTime)*0.5)
SSticker.mode.make_antag_chance(humanc) SSticker.mode.make_antag_chance(humanc)
for(var/V in character.roundstart_traits) if(CONFIG_GET(flag/roundstart_traits))
var/datum/trait/T = V SStraits.AssignTraits(humanc, humanc.client, TRUE)
T.on_spawn() //so latejoins still get their correct traits
log_manifest(character.mind.key,character.mind,character,latejoin = TRUE) log_manifest(character.mind.key,character.mind,character,latejoin = TRUE)

View File

@@ -191,6 +191,10 @@
blood_data["real_name"] = real_name blood_data["real_name"] = real_name
blood_data["features"] = dna.features blood_data["features"] = dna.features
blood_data["factions"] = faction blood_data["factions"] = faction
blood_data["traits"] = list()
for(var/V in roundstart_traits)
var/datum/trait/T = V
blood_data["traits"] += T.type
return blood_data return blood_data
//get the id of the substance this mob use as blood. //get the id of the substance this mob use as blood.

View File

@@ -17,4 +17,4 @@
use_skintones = 0 use_skintones = 0
species_traits = list(SPECIES_ORGANIC,NOBLOOD,EYECOLOR) species_traits = list(SPECIES_ORGANIC,NOBLOOD,EYECOLOR)
inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOHUNGER) inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOHUNGER)
sexes = 0 sexes = 0

View File

@@ -4,15 +4,12 @@
id = "jelly" id = "jelly"
default_color = "00FF90" default_color = "00FF90"
say_mod = "chirps" say_mod = "chirps"
species_traits = list(SPECIES_ORGANIC,MUTCOLORS,EYECOLOR,,HAIR,FACEHAIR,NOBLOOD) species_traits = list(SPECIES_ORGANIC,MUTCOLORS,EYECOLOR,NOBLOOD)
inherent_traits = list(TRAIT_TOXINLOVER) inherent_traits = list(TRAIT_TOXINLOVER)
mutant_bodyparts = list("mam_tail", "mam_ears", "taur") //CIT CHANGE
default_features = list("mcolor" = "FFF", "mam_tail" = "None", "mam_ears" = "None") //CIT CHANGE
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime
exotic_blood = "slimejelly" exotic_blood = "slimejelly"
damage_overlay_type = "" damage_overlay_type = ""
var/datum/action/innate/regenerate_limbs/regenerate_limbs var/datum/action/innate/regenerate_limbs/regenerate_limbs
var/datum/action/innate/slime_change/slime_change //CIT CHANGE
liked_food = MEAT liked_food = MEAT
coldmod = 6 // = 3x cold damage coldmod = 6 // = 3x cold damage
heatmod = 0.5 // = 1/4x heat damage heatmod = 0.5 // = 1/4x heat damage
@@ -21,8 +18,6 @@
/datum/species/jelly/on_species_loss(mob/living/carbon/C) /datum/species/jelly/on_species_loss(mob/living/carbon/C)
if(regenerate_limbs) if(regenerate_limbs)
regenerate_limbs.Remove(C) regenerate_limbs.Remove(C)
if(slime_change) //CIT CHANGE
slime_change.Remove(C) //CIT CHANGE
C.remove_language(/datum/language/slime) C.remove_language(/datum/language/slime)
C.faction -= "slime" C.faction -= "slime"
..() ..()
@@ -34,8 +29,6 @@
if(ishuman(C)) if(ishuman(C))
regenerate_limbs = new regenerate_limbs = new
regenerate_limbs.Grant(C) regenerate_limbs.Grant(C)
slime_change = new //CIT CHANGE
slime_change.Grant(C) //CIT CHANGE
C.faction |= "slime" C.faction |= "slime"
/datum/species/jelly/spec_life(mob/living/carbon/human/H) /datum/species/jelly/spec_life(mob/living/carbon/human/H)
@@ -384,6 +377,7 @@
around.</span>", around.</span>",
"<span class='notice'>...and move this one instead.</span>") "<span class='notice'>...and move this one instead.</span>")
///////////////////////////////////LUMINESCENTS////////////////////////////////////////// ///////////////////////////////////LUMINESCENTS//////////////////////////////////////////
//Luminescents are able to consume and use slime extracts, without them decaying. //Luminescents are able to consume and use slime extracts, without them decaying.
@@ -542,7 +536,6 @@
if(species.current_extract) if(species.current_extract)
species.extract_cooldown = world.time + 100 species.extract_cooldown = world.time + 100
var/cooldown = species.current_extract.activate(H, species, activation_type) var/cooldown = species.current_extract.activate(H, species, activation_type)
species.extract_cooldown = world.time + cooldown species.extract_cooldown = world.time + cooldown
@@ -555,8 +548,6 @@
///////////////////////////////////STARGAZERS////////////////////////////////////////// ///////////////////////////////////STARGAZERS//////////////////////////////////////////
//Stargazers are the telepathic branch of jellypeople, able to project psychic messages and to link minds with willing participants. //Stargazers are the telepathic branch of jellypeople, able to project psychic messages and to link minds with willing participants.
//Admin spawn only
/datum/species/jelly/stargazer /datum/species/jelly/stargazer
name = "Stargazer" name = "Stargazer"
@@ -725,5 +716,4 @@
to_chat(H, "<span class='notice'>You connect [target]'s mind to your slime link!</span>") to_chat(H, "<span class='notice'>You connect [target]'s mind to your slime link!</span>")
else else
to_chat(H, "<span class='warning'>You can't seem to link [target]'s mind...</span>") to_chat(H, "<span class='warning'>You can't seem to link [target]'s mind...</span>")
to_chat(target, "<span class='warning'>The foreign presence leaves your mind.</span>") to_chat(target, "<span class='warning'>The foreign presence leaves your mind.</span>")

View File

@@ -54,7 +54,7 @@
/datum/species/moth/space_move(mob/living/carbon/human/H) /datum/species/moth/space_move(mob/living/carbon/human/H)
. = ..() . = ..()
if(H.loc && !isspaceturf(H.loc) && H.dna.features["moth_wings"] != "Burnt Off" || "None") if(H.loc && !isspaceturf(H.loc) && H.dna.features["moth_wings"] != "Burnt Off")
var/datum/gas_mixture/current = H.loc.return_air() var/datum/gas_mixture/current = H.loc.return_air()
if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible
return TRUE return TRUE

View File

@@ -66,4 +66,4 @@
temp = master.supplied[index] temp = master.supplied[index]
if (length(temp) > 0) if (length(temp) > 0)
laws.supplied[index] = temp laws.supplied[index] = temp
return return

View File

@@ -159,8 +159,6 @@
toner = tonermax toner = tonermax
diag_hud_set_borgcell() diag_hud_set_borgcell()
verbs += /mob/living/proc/lay_down //CITADEL EDIT borgs have rest verb now for snowflake reasons
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO //If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
/mob/living/silicon/robot/Destroy() /mob/living/silicon/robot/Destroy()
if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside.
@@ -212,8 +210,6 @@
if(!CONFIG_GET(flag/disable_secborg)) if(!CONFIG_GET(flag/disable_secborg))
modulelist["Security"] = /obj/item/robot_module/security modulelist["Security"] = /obj/item/robot_module/security
modulelist += get_cit_modules() //Citadel change - adds Citadel's borg modules.
var/input_module = input("Please, select a module!", "Robot", null, null) as null|anything in modulelist var/input_module = input("Please, select a module!", "Robot", null, null) as null|anything in modulelist
if(!input_module || module.type != /obj/item/robot_module) if(!input_module || module.type != /obj/item/robot_module)
return return
@@ -367,12 +363,8 @@
to_chat(user, "<span class='notice'>You start fixing yourself...</span>") to_chat(user, "<span class='notice'>You start fixing yourself...</span>")
if(!W.use_tool(src, user, 50)) if(!W.use_tool(src, user, 50))
return return
adjustBruteLoss(-10)
else adjustBruteLoss(-30)
to_chat(user, "<span class='notice'>You start fixing [src]...</span>")
if(!do_after(user, 30, target = src))
return
adjustBruteLoss(-30)
updatehealth() updatehealth()
add_fingerprint(user) add_fingerprint(user)
visible_message("<span class='notice'>[user] has fixed some of the dents on [src].</span>") visible_message("<span class='notice'>[user] has fixed some of the dents on [src].</span>")
@@ -382,16 +374,11 @@
user.changeNext_move(CLICK_CD_MELEE) user.changeNext_move(CLICK_CD_MELEE)
var/obj/item/stack/cable_coil/coil = W var/obj/item/stack/cable_coil/coil = W
if (getFireLoss() > 0 || getToxLoss() > 0) if (getFireLoss() > 0 || getToxLoss() > 0)
if(src == user && coil.use(1)) if(src == user)
to_chat(user, "<span class='notice'>You start fixing yourself...</span>") to_chat(user, "<span class='notice'>You start fixing yourself...</span>")
if(!do_after(user, 50, target = src)) if(!do_after(user, 50, target = src))
return return
adjustFireLoss(-10)
adjustToxLoss(-10)
if (coil.use(1)) if (coil.use(1))
to_chat(user, "<span class='notice'>You start fixing [src]...</span>")
if(!do_after(user, 30, target = src))
return
adjustFireLoss(-30) adjustFireLoss(-30)
adjustToxLoss(-30) adjustToxLoss(-30)
updatehealth() updatehealth()
@@ -600,36 +587,6 @@
/mob/living/silicon/robot/update_icons() /mob/living/silicon/robot/update_icons()
cut_overlays() cut_overlays()
icon_state = module.cyborg_base_icon icon_state = module.cyborg_base_icon
//Citadel changes start here - Allows modules to use different icon files, and allows modules to specify a pixel offset
icon = (module.cyborg_icon_override ? module.cyborg_icon_override : initial(icon))
if(laser)
add_overlay("laser")//Is this even used??? - Yes borg/inventory.dm
if(disabler)
add_overlay("disabler")//ditto
if(sleeper_g && module.sleeper_overlay)
add_overlay("[module.sleeper_overlay]_g")
if(sleeper_r && module.sleeper_overlay)
add_overlay("[module.sleeper_overlay]_r")
if(module.dogborg == TRUE)
if(resting)
cut_overlays()
icon_state = "[module.cyborg_base_icon]-rest"
else
icon_state = "[module.cyborg_base_icon]"
if(stat == DEAD && module.has_snowflake_deadsprite)
icon_state = "[module.cyborg_base_icon]-wreck"
if(module.cyborg_pixel_offset)
pixel_x = module.cyborg_pixel_offset
//End of citadel changes
if(module.cyborg_base_icon == "robot")
icon = 'icons/mob/robots.dmi'
pixel_x = initial(pixel_x)
if(stat != DEAD && !(IsUnconscious() || IsStun() || IsKnockdown() || low_power_mode)) //Not dead, not stunned. if(stat != DEAD && !(IsUnconscious() || IsStun() || IsKnockdown() || low_power_mode)) //Not dead, not stunned.
if(!eye_lights) if(!eye_lights)
eye_lights = new() eye_lights = new()
@@ -1023,7 +980,6 @@
designation = module.name designation = module.name
if(hands) if(hands)
hands.icon_state = module.moduleselect_icon hands.icon_state = module.moduleselect_icon
hands.icon = (module.moduleselect_alternate_icon ? module.moduleselect_alternate_icon : initial(hands.icon)) //CITADEL CHANGE - allows module select icons to use a different icon file
if(module.can_be_pushed) if(module.can_be_pushed)
status_flags |= CANPUSH status_flags |= CANPUSH
else else

View File

@@ -22,7 +22,7 @@
ventcrawler = VENTCRAWLER_ALWAYS ventcrawler = VENTCRAWLER_ALWAYS
var/datum/mind/origin var/datum/mind/origin
var/egg_lain = 0 var/egg_lain = 0
//gold_core_spawnable = HOSTILE_SPAWN //are you sure about this?? gold_core_spawnable = NO_SPAWN //are you sure about this?? // CITADEL CHANGE, Yes.
/mob/living/simple_animal/hostile/headcrab/proc/Infect(mob/living/carbon/victim) /mob/living/simple_animal/hostile/headcrab/proc/Infect(mob/living/carbon/victim)
var/obj/item/organ/body_egg/changeling_egg/egg = new(victim) var/obj/item/organ/body_egg/changeling_egg/egg = new(victim)

View File

@@ -146,13 +146,13 @@
else else
status_traits[trait] |= list(source) status_traits[trait] |= list(source)
/mob/living/proc/add_trait_datum(trait) //separate proc due to the way these ones are handled /mob/living/proc/add_trait_datum(trait, spawn_effects) //separate proc due to the way these ones are handled
if(has_trait(trait)) if(has_trait(trait))
return return
if(!SStraits || !SStraits.traits[trait]) if(!SStraits || !SStraits.traits[trait])
return return
var/datum/trait/T = SStraits.traits[trait] var/datum/trait/T = SStraits.traits[trait]
new T (src) new T (src, spawn_effects)
return TRUE return TRUE
/mob/living/proc/remove_trait(trait, list/sources, force) /mob/living/proc/remove_trait(trait, list/sources, force)
@@ -192,13 +192,14 @@
. = FALSE . = FALSE
if(sources && !islist(sources))
sources = list(sources)
if(LAZYLEN(sources)) if(LAZYLEN(sources))
for(var/S in sources) for(var/S in sources)
if(S in status_traits[trait]) if(S in status_traits[trait])
return TRUE return TRUE
else else if(LAZYLEN(status_traits[trait]))
if(LAZYLEN(status_traits[trait])) return TRUE
return TRUE
/mob/living/proc/has_trait_datum(trait) /mob/living/proc/has_trait_datum(trait)
return roundstart_traits[trait] return roundstart_traits[trait]

View File

@@ -16,6 +16,8 @@
var/recharged = 0 var/recharged = 0
var/recharge_delay = 5 var/recharge_delay = 5
var/mutable_appearance/beaker_overlay var/mutable_appearance/beaker_overlay
var/working_state = "dispenser_working"
var/nopower_state = "dispenser_nopower"
var/obj/item/reagent_containers/beaker = null var/obj/item/reagent_containers/beaker = null
var/list/dispensable_reagents = list( var/list/dispensable_reagents = list(
"hydrogen", "hydrogen",
@@ -60,6 +62,7 @@
cell = new cell_type cell = new cell_type
recharge() recharge()
dispensable_reagents = sortList(dispensable_reagents) dispensable_reagents = sortList(dispensable_reagents)
update_icon()
/obj/machinery/chem_dispenser/Destroy() /obj/machinery/chem_dispenser/Destroy()
QDEL_NULL(beaker) QDEL_NULL(beaker)
@@ -67,13 +70,36 @@
return ..() return ..()
/obj/machinery/chem_dispenser/process() /obj/machinery/chem_dispenser/process()
if(recharged < 0) if(recharged < 0)
recharge() recharge()
recharged = recharge_delay recharged = recharge_delay
else else
recharged -= 1 recharged -= 1
/obj/machinery/chem_dispenser/proc/display_beaker()
..()
var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker")
b_o.pixel_y = -4
b_o.pixel_x = -7
return b_o
obj/machinery/chem_dispenser/proc/work_animation()
if(working_state)
flick(working_state,src)
/obj/machinery/chem_dispenser/power_change()
..()
if(!powered() && nopower_state)
icon_state = nopower_state
else
icon_state = initial(icon_state)
obj/machinery/chem_dispenser/update_icon()
cut_overlays()
if(beaker)
beaker_overlay = display_beaker()
add_overlay(beaker_overlay)
/obj/machinery/chem_dispenser/proc/recharge() /obj/machinery/chem_dispenser/proc/recharge()
if(stat & (BROKEN|NOPOWER)) if(stat & (BROKEN|NOPOWER))
return return
@@ -163,6 +189,7 @@
var/target = text2num(params["target"]) var/target = text2num(params["target"])
if(target in beaker.possible_transfer_amounts) if(target in beaker.possible_transfer_amounts)
amount = target amount = target
work_animation()
. = TRUE . = TRUE
if("dispense") if("dispense")
var/reagent = params["reagent"] var/reagent = params["reagent"]
@@ -173,11 +200,13 @@
R.add_reagent(reagent, actual) R.add_reagent(reagent, actual)
cell.use((actual / 10) / powerefficiency) cell.use((actual / 10) / powerefficiency)
work_animation()
. = TRUE . = TRUE
if("remove") if("remove")
var/amount = text2num(params["amount"]) var/amount = text2num(params["amount"])
if(beaker && amount in beaker.possible_transfer_amounts) if(beaker && amount in beaker.possible_transfer_amounts)
beaker.reagents.remove_all(amount) beaker.reagents.remove_all(amount)
work_animation()
. = TRUE . = TRUE
if("eject") if("eject")
if(beaker) if(beaker)
@@ -185,7 +214,7 @@
if(Adjacent(usr) && !issilicon(usr)) if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker) usr.put_in_hands(beaker)
beaker = null beaker = null
cut_overlays() update_icon()
. = TRUE . = TRUE
if("dispense_recipe") if("dispense_recipe")
var/recipe_to_use = params["recipe"] var/recipe_to_use = params["recipe"]
@@ -200,6 +229,7 @@
if(actual) if(actual)
R.add_reagent(r_id, actual) R.add_reagent(r_id, actual)
cell.use((actual / 10) / powerefficiency) cell.use((actual / 10) / powerefficiency)
work_animation()
if("clear_recipes") if("clear_recipes")
var/yesno = alert("Clear all recipes?",, "Yes","No") var/yesno = alert("Clear all recipes?",, "Yes","No")
if(yesno == "Yes") if(yesno == "Yes")
@@ -226,23 +256,17 @@
/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params) /obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params)
if(default_unfasten_wrench(user, I)) if(default_unfasten_wrench(user, I))
return return
if(istype(I, /obj/item/reagent_containers) && !(I.flags_1 & ABSTRACT_1) && I.is_open_container()) if(istype(I, /obj/item/reagent_containers) && !(I.flags_1 & ABSTRACT_1) && I.is_open_container())
var/obj/item/reagent_containers/B = I var/obj/item/reagent_containers/B = I
. = 1 //no afterattack . = 1 //no afterattack
if(beaker) if(beaker)
to_chat(user, "<span class='warning'>A container is already loaded into [src]!</span>") to_chat(user, "<span class='warning'>A container is already loaded into [src]!</span>")
return return
if(!user.transferItemToLoc(B, src)) if(!user.transferItemToLoc(B, src))
return return
beaker = B beaker = B
to_chat(user, "<span class='notice'>You add [B] to [src].</span>") to_chat(user, "<span class='notice'>You add [B] to [src].</span>")
update_icon()
beaker_overlay = beaker_overlay || mutable_appearance(icon, "disp_beaker")
beaker_overlay.pixel_x = rand(-10, 5)//randomize beaker overlay position.
add_overlay(beaker_overlay)
else if(user.a_intent != INTENT_HARM && !istype(I, /obj/item/card/emag)) else if(user.a_intent != INTENT_HARM && !istype(I, /obj/item/card/emag))
to_chat(user, "<span class='warning'>You can't load [I] into [src]!</span>") to_chat(user, "<span class='warning'>You can't load [I] into [src]!</span>")
return ..() return ..()
@@ -266,6 +290,7 @@
beaker.reagents.remove_all() beaker.reagents.remove_all()
cell.use(total/powerefficiency) cell.use(total/powerefficiency)
cell.emp_act(severity) cell.emp_act(severity)
work_animation()
visible_message("<span class='danger'>[src] malfunctions, spraying chemicals everywhere!</span>") visible_message("<span class='danger'>[src] malfunctions, spraying chemicals everywhere!</span>")
..() ..()
@@ -278,6 +303,8 @@
recharge_delay = 20 recharge_delay = 20
dispensable_reagents = list() dispensable_reagents = list()
circuit = /obj/item/circuitboard/machine/chem_dispenser circuit = /obj/item/circuitboard/machine/chem_dispenser
working_state = "minidispenser_working"
nopower_state = "minidispenser_nopower"
var/static/list/dispensable_reagent_tiers = list( var/static/list/dispensable_reagent_tiers = list(
list( list(
"hydrogen", "hydrogen",
@@ -362,6 +389,29 @@
final_list += list(avoid_assoc_duplicate_keys(fuck[1],key_list) = text2num(fuck[2])) final_list += list(avoid_assoc_duplicate_keys(fuck[1],key_list) = text2num(fuck[2]))
return final_list return final_list
/obj/machinery/chem_dispenser/constructable/display_beaker()
var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker")
b_o.pixel_y = -4
b_o.pixel_x = -4
return b_o
/obj/machinery/chem_dispenser/drinks/display_beaker()
var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker")
switch(dir)
if(NORTH)
b_o.pixel_y = 7
b_o.pixel_x = rand(-9, 9)
if(EAST)
b_o.pixel_x = 4
b_o.pixel_y = rand(-5, 7)
if(WEST)
b_o.pixel_x = -5
b_o.pixel_y = rand(-5, 7)
else//SOUTH
b_o.pixel_y = -7
b_o.pixel_x = rand(-9, 9)
return b_o
/obj/machinery/chem_dispenser/drinks /obj/machinery/chem_dispenser/drinks
name = "soda dispenser" name = "soda dispenser"
desc = "Contains a large reservoir of soft drinks." desc = "Contains a large reservoir of soft drinks."
@@ -369,6 +419,10 @@
icon = 'icons/obj/chemical.dmi' icon = 'icons/obj/chemical.dmi'
icon_state = "soda_dispenser" icon_state = "soda_dispenser"
amount = 10 amount = 10
pixel_y = 6
layer = WALL_OBJ_LAYER
working_state = null
nopower_state = null
dispensable_reagents = list( dispensable_reagents = list(
"water", "water",
"ice", "ice",
@@ -398,8 +452,6 @@
"tirizene" "tirizene"
) )
/obj/machinery/chem_dispenser/drinks/beer /obj/machinery/chem_dispenser/drinks/beer
name = "booze dispenser" name = "booze dispenser"
desc = "Contains a large reservoir of the good stuff." desc = "Contains a large reservoir of the good stuff."

View File

@@ -14,6 +14,8 @@
icon_state = "294_bottom" icon_state = "294_bottom"
amount = 10 amount = 10
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
working_state = null
nopower_state = null
var/static/list/shortcuts = list( var/static/list/shortcuts = list(
"meth" = "methamphetamine", "meth" = "methamphetamine",
"tricord" = "tricordrazine" "tricord" = "tricordrazine"
@@ -25,7 +27,7 @@
GLOB.poi_list += src GLOB.poi_list += src
top_overlay = mutable_appearance(icon, "294_top", layer = ABOVE_ALL_MOB_LAYER) top_overlay = mutable_appearance(icon, "294_top", layer = ABOVE_ALL_MOB_LAYER)
update_icon() update_icon()
/obj/machinery/chem_dispenser/scp_294/update_icon() /obj/machinery/chem_dispenser/scp_294/update_icon()
cut_overlays() cut_overlays()
@@ -36,6 +38,9 @@
GLOB.poi_list -= src GLOB.poi_list -= src
QDEL_NULL(top_overlay) QDEL_NULL(top_overlay)
/obj/machinery/chem_dispenser/scp_294/display_beaker()
return
/obj/machinery/chem_dispenser/scp_294/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ /obj/machinery/chem_dispenser/scp_294/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)

View File

@@ -0,0 +1,91 @@
/obj/item/reagent_containers/medspray
name = "medical spray"
desc = "A medical spray bottle, designed for precision application, with an unscrewable cap."
icon = 'icons/obj/chemical.dmi'
icon_state = "medspray"
item_state = "spraycan"
lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
flags_1 = NOBLUDGEON_1
obj_flags = UNIQUE_RENAME
container_type = OPENCONTAINER
slot_flags = SLOT_BELT
throwforce = 0
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
throw_range = 7
amount_per_transfer_from_this = 10
volume = 60
var/can_fill_from_container = TRUE
var/apply_type = PATCH
var/apply_method = "spray"
var/self_delay = 30
var/squirt_mode = 0
var/squirt_amount = 5
/obj/item/reagent_containers/medspray/attack_self(mob/user)
squirt_mode = !squirt_mode
if(squirt_mode)
amount_per_transfer_from_this = squirt_amount
else
amount_per_transfer_from_this = initial(amount_per_transfer_from_this)
to_chat(user, "<span class='notice'>You will now apply the medspray's contents in [squirt_mode ? "short bursts":"extended sprays"]. You'll now use [amount_per_transfer_from_this] units per use.</span>")
/obj/item/reagent_containers/medspray/attack(mob/M, mob/user, def_zone)
if(!reagents || !reagents.total_volume)
to_chat(user, "<span class='warning'>[src] is empty!</span>")
return
if(M == user)
M.visible_message("<span class='notice'>[user] attempts to [apply_method] [src] on themselves.</span>")
if(self_delay)
if(!do_mob(user, M, self_delay))
return
if(!reagents || !reagents.total_volume)
return
to_chat(M, "<span class='notice'>You [apply_method] yourself with [src].</span>")
else
add_logs(user, M, "attempted to apply", src, reagents.log_list())
M.visible_message("<span class='danger'>[user] attempts to [apply_method] [src] on [M].</span>", \
"<span class='userdanger'>[user] attempts to [apply_method] [src] on [M].</span>")
if(!do_mob(user, M))
return
if(!reagents || !reagents.total_volume)
return
M.visible_message("<span class='danger'>[user] [apply_method]s [M] down with [src].</span>", \
"<span class='userdanger'>[user] [apply_method]s [M] down with [src].</span>")
if(!reagents || !reagents.total_volume)
return
else
add_logs(user, M, "applied", src, reagents.log_list())
playsound(src, 'sound/effects/spray2.ogg', 50, 1, -6)
var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1)
reagents.reaction(M, apply_type, fraction)
reagents.trans_to(M, amount_per_transfer_from_this)
return
/obj/item/reagent_containers/medspray/styptic
name = "medical spray (styptic powder)"
desc = "A medical spray bottle, designed for precision application, with an unscrewable cap. This one contains styptic powder, for treating cuts and bruises."
icon_state = "brutespray"
list_reagents = list("styptic_powder" = 60)
/obj/item/reagent_containers/medspray/silver_sulf
name = "medical spray (silver sulfadiazine)"
desc = "A medical spray bottle, designed for precision application, with an unscrewable cap. This one contains silver sulfadiazine, useful for treating burns."
icon_state = "burnspray"
list_reagents = list("silver_sulfadiazine" = 60)
/obj/item/reagent_containers/medspray/synthflesh
name = "medical spray (synthflesh)"
desc = "A medical spray bottle, designed for precision application, with an unscrewable cap. This one contains synthflesh, an apex brute and burn healing agent."
icon_state = "synthspray"
list_reagents = list("synthflesh" = 60)
/obj/item/reagent_containers/medspray/sterilizine
name = "sterilizer spray"
desc = "Spray bottle loaded with non-toxic sterilizer. Useful in preparation for surgery."
list_reagents = list("sterilizine" = 60)

View File

@@ -171,19 +171,6 @@
list_reagents = list("spraytan" = 50) list_reagents = list("spraytan" = 50)
/obj/item/reagent_containers/spray/medical
name = "medical spray"
icon = 'icons/obj/chemical.dmi'
icon_state = "medspray"
volume = 100
/obj/item/reagent_containers/spray/medical/sterilizer
name = "sterilizer spray"
desc = "Spray bottle loaded with non-toxic sterilizer. Useful in preparation for surgery."
list_reagents = list("sterilizine" = 100)
//pepperspray //pepperspray
/obj/item/reagent_containers/spray/pepper /obj/item/reagent_containers/spray/pepper
name = "pepperspray" name = "pepperspray"

View File

@@ -41,8 +41,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN)
if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL) if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
SSshuttle.emergency.modTimer(2) SSshuttle.emergency.modTimer(2)
GLOB.security_level = SEC_LEVEL_BLUE GLOB.security_level = SEC_LEVEL_BLUE
sound_to_playing_players('sound/misc/voybluealert.ogg') // Citadel change - Makes alerts play a sound
for(var/obj/machinery/firealarm/FA in GLOB.machines) for(var/obj/machinery/firealarm/FA in GLOB.machines)
if(is_station_level(FA.z)) if(is_station_level(FA.z))
FA.update_icon() FA.update_icon()
@@ -57,7 +55,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN)
else else
minor_announce(CONFIG_GET(string/alert_red_downto), "Attention! Code red!") minor_announce(CONFIG_GET(string/alert_red_downto), "Attention! Code red!")
GLOB.security_level = SEC_LEVEL_RED GLOB.security_level = SEC_LEVEL_RED
sound_to_playing_players('sound/misc/voyalert.ogg') // Citadel change - Makes alerts play a sound
for(var/obj/machinery/firealarm/FA in GLOB.machines) for(var/obj/machinery/firealarm/FA in GLOB.machines)
if(is_station_level(FA.z)) if(is_station_level(FA.z))
@@ -72,8 +69,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN)
else if(GLOB.security_level == SEC_LEVEL_BLUE) else if(GLOB.security_level == SEC_LEVEL_BLUE)
SSshuttle.emergency.modTimer(0.5) SSshuttle.emergency.modTimer(0.5)
GLOB.security_level = SEC_LEVEL_DELTA GLOB.security_level = SEC_LEVEL_DELTA
sound_to_playing_players('sound/misc/deltakalaxon.ogg') // Citadel change - Makes alerts play a sound
for(var/obj/machinery/firealarm/FA in GLOB.machines) for(var/obj/machinery/firealarm/FA in GLOB.machines)
if(is_station_level(FA.z)) if(is_station_level(FA.z))
FA.update_icon() FA.update_icon()

View File

@@ -15,4 +15,4 @@
continue continue
empulse(target.loc, emp_heavy, emp_light) empulse(target.loc, emp_heavy, emp_light)
return return

View File

@@ -49,4 +49,4 @@
target.blur_eyes(amt_eye_blurry) target.blur_eyes(amt_eye_blurry)
//summoning //summoning
if(summon_type) if(summon_type)
new summon_type(target.loc, target) new summon_type(target.loc, target)

View File

@@ -20,3 +20,17 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggledigestionnoise)()
to_chat(usr, "You will [(usr.client.prefs.toggles & DIGESTION_NOISES) ? "now" : "no longer"] hear digestion noises.") to_chat(usr, "You will [(usr.client.prefs.toggles & DIGESTION_NOISES) ? "now" : "no longer"] hear digestion noises.")
/datum/verbs/menu/Settings/Sound/toggledigestionnoise/Get_checked(client/C) /datum/verbs/menu/Settings/Sound/toggledigestionnoise/Get_checked(client/C)
return !(C.prefs.toggles & DIGESTION_NOISES) return !(C.prefs.toggles & DIGESTION_NOISES)
TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglehoundsleeper)()
set name = "Allow/Deny Hound Sleeper"
set category = "Preferences"
set desc = "Allow MediHound Sleepers"
usr.client.prefs.toggles ^= MEDIHOUND_SLEEPER
usr.client.prefs.save_preferences()
if(usr.client.prefs.toggles & MEDIHOUND_SLEEPER)
to_chat(usr, "You will now allow MediHounds to place you in their sleeper.")
else
to_chat(usr, "You will no longer allow MediHounds to place you in their sleeper.")
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle MediHound Sleeper", "[usr.client.prefs.toggles & MEDIHOUND_SLEEPER ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/verbs/menu/Settings/Sound/togglehoundsleeper/Get_checked(client/C)
return C.prefs.toggles & MEDIHOUND_SLEEPER