diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 73f3c1e451..73fc0d43f3 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -39,7 +39,7 @@
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect())
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
var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm
index c915816ed0..e4b552b6b5 100644
--- a/code/modules/admin/admin_ranks.dm
+++ b/code/modules/admin/admin_ranks.dm
@@ -1,13 +1,17 @@
GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums
GLOBAL_PROTECT(admin_ranks)
+GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt
+GLOBAL_PROTECT(protected_ranks)
+
/datum/admin_rank
var/name = "NoRank"
var/rights = R_DEFAULT
- var/list/adds
- var/list/subs
+ var/exclude_rights = 0
+ 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())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
@@ -17,19 +21,18 @@ GLOBAL_PROTECT(admin_ranks)
CRASH("Admin proc call creation of admin datum")
return
name = init_name
- switch(name)
- if("Removed",null,"")
- QDEL_IN(src, 0)
- throw EXCEPTION("invalid admin-rank name")
- return
+ if(!name)
+ qdel(src)
+ throw EXCEPTION("Admin rank created without name.")
+ return
if(init_rights)
rights = init_rights
- if(!init_adds)
- init_adds = list()
- if(!init_subs)
- init_subs = list()
- adds = init_adds
- subs = init_subs
+ include_rights = rights
+ if(init_exclude_rights)
+ exclude_rights = init_exclude_rights
+ rights &= ~exclude_rights
+ if(init_edit_rights)
+ can_edit_rights = init_edit_rights
/datum/admin_rank/Destroy()
if(IsAdminAdvancedProcCall())
@@ -75,13 +78,12 @@ GLOBAL_PROTECT(admin_ranks)
flag = R_SPAWN
if("autologin", "autoadmin")
flag = R_AUTOLOGIN
+ if("dbranks")
+ flag = R_DBRANKS
if("@","prev")
flag = previous_rights
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
/datum/admin_rank/proc/process_keyword(word, previous_rights=0)
if(IsAdminAdvancedProcCall())
@@ -94,157 +96,156 @@ GLOBAL_PROTECT(admin_ranks)
switch(text2ascii(word,1))
if(43)
rights |= flag //+
+ include_rights |= flag
if(45)
rights &= ~flag //-
- else
- //isn't a keyword so maybe it's a verbpath?
- var/path = admin_keyword_to_path(word)
- if(path)
- switch(text2ascii(word,1))
- if(43)
- if(!subs.Remove(path))
- adds += path //+
- if(45)
- if(!adds.Remove(path))
- subs += path //-
-
+ exclude_rights |= flag
+ if(42)
+ can_edit_rights |= flag //*
// Checks for (keyword-formatted) rights on this admin
/datum/admins/proc/check_keyword(word)
var/flag = admin_keyword_to_flag(word)
if(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
-/proc/load_admin_ranks()
+/proc/load_admin_ranks(dbfail)
if(IsAdminAdvancedProcCall())
to_chat(usr, "Admin Reload blocked: Advanced ProcCall detected.")
return
GLOB.admin_ranks.Cut()
-
- if(CONFIG_GET(flag/admin_legacy_system))
- var/previous_rights = 0
- //load text from file and process each line separately
- for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt"))
- if(!line)
- continue
- if(findtextEx(line,"#",1,2))
- continue
-
- var/next = findtext(line, "=")
- var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next)))
- if(!R)
- continue
- GLOB.admin_ranks += R
-
- var/prev = findchar(line, "+-", next, 0)
- while(prev)
- next = findchar(line, "+-", prev + 1, 0)
- R.process_keyword(copytext(line, prev, next), previous_rights)
- 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")]")
+ GLOB.protected_ranks.Cut()
+ var/previous_rights = 0
+ //load text from file and process each line separately
+ for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt"))
+ if(!line || findtextEx(line,"#",1,2))
+ continue
+ var/next = findtext(line, "=")
+ var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next)))
+ if(!R)
+ continue
+ GLOB.admin_ranks += R
+ GLOB.protected_ranks += R
+ var/prev = findchar(line, "+-*", next, 0)
+ while(prev)
+ next = findchar(line, "+-*", prev + 1, 0)
+ R.process_keyword(copytext(line, prev, next), previous_rights)
+ prev = next
+ previous_rights = R.rights
+ if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
+ var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
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
- while(query_load_admin_ranks.NextRow())
- var/rank_name = ckeyEx(query_load_admin_ranks.item[1])
- var/flags = query_load_admin_ranks.item[2]
- if(istext(flags))
- flags = text2num(flags)
- var/datum/admin_rank/R = new(rank_name, flags)
+ var/list/json = json_decode(file2text(backup_file))
+ for(var/J in json["ranks"])
+ for(var/datum/admin_rank/R in GLOB.admin_ranks)
+ if(R.name == "[J]") //this rank was already loaded from txt override
+ continue
+ 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)
continue
GLOB.admin_ranks += R
-
+ return 1
#ifdef TESTING
var/msg = "Permission Sets Built:\n"
for(var/datum/admin_rank/R in GLOB.admin_ranks)
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)
msg += "\t\t[rights]\n"
testing(msg)
#endif
-
/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
-
GLOB.admin_datums.Cut()
for(var/client/C in GLOB.admins)
C.remove_admin_verbs()
C.holder = null
GLOB.admins.Cut()
+ GLOB.protected_admins.Cut()
GLOB.deadmins.Cut()
- load_admin_ranks()
+ dbfail = load_admin_ranks(dbfail)
//Clear profile access
for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null)
-
var/list/rank_names = list()
for(var/datum/admin_rank/R in GLOB.admin_ranks)
rank_names[R.name] = R
-
- if(CONFIG_GET(flag/admin_legacy_system))
- //load text from file
- var/list/lines = world.file2list("[global.config.directory]/admins.txt")
-
- //process each line separately
- for(var/line in lines)
- if(!length(line))
- continue
- if(findtextEx(line, "#", 1, 2))
- continue
-
- var/list/entry = splittext(line, "=")
- if(entry.len < 2)
- 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
-
+ //ckeys listed in admins.txt are always made admins before sql loading is attempted
+ var/list/lines = world.file2list("[global.config.directory]/admins.txt")
+ for(var/line in lines)
+ if(!length(line) || findtextEx(line, "#", 1, 2))
+ continue
+ var/list/entry = splittext(line, "=")
+ if(entry.len < 2)
+ continue
+ var/ckey = ckey(entry[1])
+ var/rank = ckeyEx(entry[2])
+ if(!ckey || !rank)
+ continue
+ new /datum/admins(rank_names[rank], ckey, 0, 1)
+ if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
var/datum/DBQuery/query_load_admins = SSdbcore.NewQuery("SELECT ckey, rank FROM [format_table_name("admin")]")
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
- while(query_load_admins.NextRow())
- var/ckey = ckey(query_load_admins.item[1])
- var/rank = ckeyEx(query_load_admins.item[2])
-
- if(rank_names[rank] == null)
- WARNING("Admin rank ([rank]) does not exist.")
- continue
-
- new /datum/admins(rank_names[rank], ckey)
-
+ var/list/json = json_decode(file2text(backup_file))
+ for(var/J in json["admins"])
+ for(var/A in GLOB.admin_datums + GLOB.deadmins)
+ if(A == "[J]") //this admin was already loaded from txt override
+ continue
+ new /datum/admins(rank_names[json["admins"]["[J]"]], "[J]")
#ifdef TESTING
var/msg = "Admins Built:\n"
for(var/ckey in GLOB.admin_datums)
@@ -252,7 +253,7 @@ GLOBAL_PROTECT(admin_ranks)
msg += "\t[ckey] - [D.rank.name]\n"
testing(msg)
#endif
-
+ return dbfail
#ifdef TESTING
/client/verb/changerank(newrank in GLOB.admin_ranks)
@@ -271,149 +272,3 @@ GLOBAL_PROTECT(admin_ranks)
remove_admin_verbs()
holder.associate(src)
#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, "Admin Edit blocked: Advanced ProcCall detected.")
- 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, "Error: Topic 'editrights': [new_ckey] is already an admin")
- return
- adm_ckey = new_ckey
- task = "rank"
- else
- adm_ckey = ckey(href_list["ckey"])
- if(!adm_ckey)
- to_chat(usr, "Error: Topic 'editrights': No valid ckey")
- 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()
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 85811f693c..695d6775a5 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -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_panel, /*admin-pm list*/
/client/proc/cmd_admin_ticket_panel,
- /client/proc/panicbunker,
/client/proc/stop_sounds
)
GLOBAL_PROTECT(admin_verbs_admin)
@@ -117,6 +116,7 @@ GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
/client/proc/toggle_random_events,
/client/proc/forcerandomrotate,
/client/proc/adminchangemap,
+ /client/proc/panicbunker,
/client/proc/toggle_hub
)
GLOBAL_PROTECT(admin_verbs_debug)
@@ -157,7 +157,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
/client/proc/pump_random_event,
/client/proc/cmd_display_init_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_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)
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()
verbs.Remove(
GLOB.admin_verbs_default,
@@ -306,8 +301,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/cmd_admin_areatest_station,
/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
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."
var/ex_power = input("Explosive Power:") as null|num
+ if (isnull(ex_power))
+ return
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()
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."
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)
to_chat(usr, "Estimated Explosive Power: [power]")
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index 59d432574d..321142376a 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -1,5 +1,7 @@
GLOBAL_LIST_EMPTY(admin_datums)
GLOBAL_PROTECT(admin_datums)
+GLOBAL_LIST_EMPTY(protected_admins)
+GLOBAL_PROTECT(protected_admins)
GLOBAL_VAR_INIT(href_token, GenerateToken())
GLOBAL_PROTECT(href_token)
@@ -26,7 +28,7 @@ GLOBAL_PROTECT(href_token)
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())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
@@ -51,6 +53,8 @@ GLOBAL_PROTECT(href_token)
if(R.rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin")
//only admins with +ADMIN start admined
+ if(protected)
+ GLOB.protected_admins[target] = src
if (force_active || (R.rights & R_AUTOLOGIN))
activate()
else
diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm
new file mode 100644
index 0000000000..1173c9393c
--- /dev/null
+++ b/code/modules/admin/permissionedit.dm
@@ -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({"
+
+
+Permissions Panel
+
+
+
+
+
+Search:
+
+"}
+
+ 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, "Admin Edit blocked: Advanced ProcCall detected.")
+ 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, "Editing the rank of this admin is blocked by server configuration.")
+ 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, "Editing the flags of this rank is blocked by server configuration.")
+ return
+ if(check_rights(R_DBRANKS, 0))
+ if(!skip)
+ if(!SSdbcore.Connect())
+ to_chat(usr, "Unable to connect to database, changes are temporary only.")
+ 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, "[.] is already an admin.")
+ 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
[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
Flags enabled here will be removed from a rank.
Note these take precedence over included flags.
[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
These are the flags this rank is allowed to edit if they have access to the permissions panel.
They will be unable to modify admins to a rank that has a flag not included here.
[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"]")
diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm
index a758d295e2..b8176a4ce7 100644
--- a/code/modules/admin/secrets.dm
+++ b/code/modules/admin/secrets.dm
@@ -31,6 +31,7 @@
Reset Thunderdome to default state
Rename Station Name
Reset Station Name
+ Set Night Shift Mode
Shuttles
@@ -54,7 +55,6 @@
Power all SMES
Triple AI mode (needs to be used in the lobby)
Everyone is the traitor
- AK-47s For Everyone!
Summon Guns
Summon Magic
Summon Events (Toggle)
@@ -107,8 +107,6 @@
dat += "No-one has done anything this round!"
usr << browse(dat, "window=admin_log")
- if("mentor_log")
- CitadelMentorLogSecret()
if("list_job_debug")
var/dat = "Job Debug info.
"
for(var/line in SSjob.job_debug)
@@ -167,6 +165,23 @@
log_admin("[key_name(usr)] renamed the station to \"[new_name]\".")
message_admins("[key_name_admin(usr)] 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(!check_rights(R_ADMIN))
@@ -460,13 +475,6 @@
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')
- 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(!check_rights(R_FUN))
return
@@ -613,13 +621,13 @@
var/list/new_movement = list()
for(var/i in 1 to movement_keys.len)
var/key = movement_keys[i]
-
+
var/msg = "Please input the new movement direction when the user presses [key]. Ex. northeast"
var/title = "New direction for [key]"
var/new_direction = text2dir(input(usr, msg, title) as text|null)
if(!new_direction)
new_direction = movement_keys[key]
-
+
new_movement[key] = new_direction
SSinput.movement_keys = new_movement
message_admins("[key_name_admin(usr)] has configured all movement directions.")
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 91ed0a5128..3dc9b8fcd2 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -271,50 +271,6 @@
return
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, "this can be only used on instances of type /mob!")
- return
-
- if(!M.client)
- to_chat(usr, "No client.")
- return
-
- log_admin("[key_name(usr)] has granted [key_name(M)] mentor access")
- message_admins(" [key_name_admin(usr)] has granted [key_name_admin(M)] mentor access.")
-
- 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, " You've been granted mentor access! Help people who send mentor-pms.")
-
- 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(" [key_name_admin(usr)] has removed mentor access from [key_name_admin(M)].")
-
- 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, "Your mentor access has been revoked.")
- M.verbs -= /client/proc/cmd_mentor_say
- M.verbs -= /client/proc/show_mentor_memo
-
else if(href_list["editrights"])
edit_rights_topic(href_list)
@@ -916,12 +872,6 @@
else
dat += "Abductor | "
- //Borer
- if(jobban_isbanned(M, "borer") || isbanned_dept)
- dat += "Borer | "
- else
- dat += "Borer | "
-
//Alien
if(jobban_isbanned(M, ROLE_ALIEN) || isbanned_dept)
dat += "Alien | "
@@ -1709,7 +1659,7 @@
var/mob/living/L = M
var/status
switch (M.stat)
- if (CONSCIOUS)
+ if(CONSCIOUS)
status = "Alive"
if(SOFT_CRIT)
status = "Dying"
diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm
index 33fb3867d1..7d382af901 100644
--- a/code/modules/admin/verbs/playsound.dm
+++ b/code/modules/admin/verbs/playsound.dm
@@ -24,7 +24,7 @@
admin_sound.status = SOUND_STREAM
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)
if("Yes")
to_chat(world, "An admin played: [S]")
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index 1d694e4033..36ecef0925 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -4,6 +4,7 @@
//Admin-spawn or random event
#define INVISIBILITY_REVENANT 50
+#define REVENANT_NAME_FILE "revenant_names.json"
/mob/living/simple_animal/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/blight(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()
..()
diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index 5665ed5f69..138a7a2607 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -98,11 +98,11 @@
var/list/new_overlay_types = tile_graphic()
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
- #endif*/
+ #endif
- /*#if DM_VERSION >= 512
+ #if DM_VERSION >= 512
if (atmos_overlay_types)
for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
vars["vis_contents"] -= overlay
@@ -112,7 +112,7 @@
vars["vis_contents"] += new_overlay_types - atmos_overlay_types //don't add overlays that already exist
else
vars["vis_contents"] += new_overlay_types
- #else*/
+ #else
if (atmos_overlay_types)
for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
cut_overlay(overlay)
@@ -122,7 +122,7 @@
add_overlay(new_overlay_types - atmos_overlay_types) //don't add overlays that already exist
else
add_overlay(new_overlay_types)
- //#endif
+ #endif
UNSETEMPTY(new_overlay_types)
src.atmos_overlay_types = new_overlay_types
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
index 1a9c76cb4d..79578b290d 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
@@ -57,11 +57,6 @@ It's like a regular ol' straight pipe, but you can turn it on and off.
close()
return
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
name = "digital valve"
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index 6c4d691d0f..e5d07fd36b 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -57,7 +57,7 @@
continue // i'd be right happy to
meme_pack_data[P.group]["packs"] += list(list(
"name" = P.name,
- "cost" = P.cost * 2, //displays twice the normal cost
+ "cost" = P.cost,
"id" = pack
))
@@ -120,12 +120,12 @@
CHECK_TICK
if(empty_turfs && empty_turfs.len)
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)
. = TRUE
update_icon()
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
for(var/turf/open/floor/T in landingzone.contents)
if(is_blocked_turf(T))
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index 41dade9fe0..f0c8e0c2a5 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -988,8 +988,9 @@
/obj/item/reagent_containers/pill/insulin,
/obj/item/stack/medical/gauze,
/obj/item/storage/box/beakers,
+ /obj/item/storage/box/medsprays,
/obj/item/storage/box/syringes,
- /obj/item/storage/box/bodybags)
+ /obj/item/storage/box/bodybags)
crate_name = "medical supplies crate"
/datum/supply_pack/medical/vending
@@ -1983,4 +1984,4 @@
/obj/item/toy/redbutton,
/obj/item/toy/eightball,
/obj/item/vending_refill/donksoft)
- crate_name = "toy crate"
\ No newline at end of file
+ crate_name = "toy crate"
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index cccdb199bb..c821204993 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -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.
client.cache += unreceived
return 1
-
+
client.sending |= unreceived
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
concurrent_tracker++
send_asset(client, file, verify=FALSE)
-
+
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.
@@ -350,6 +350,11 @@ GLOBAL_LIST_EMPTY(asset_datums)
"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.
/datum/asset/language/register()
for(var/path in typesof(/datum/language))
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 9181945ebd..aa7f5ce82b 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -88,22 +88,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
cmd_admin_pm(href_list["priv_msg"],null)
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_"])
if("holder")
hsrc = holder
if("usr")
hsrc = mob
- if("mentor") // CITADEL
- hsrc = mentor_datum // CITADEL END
if("prefs")
if (inprefs)
return
@@ -239,16 +228,23 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
. = ..() //calls mob.Login()
#if DM_VERSION >= 512
- if (num2text(byond_build) in GLOB.blacklisted_builds)
- log_access("Failed login: blacklisted byond version")
- to_chat(src, "Your version of byond is blacklisted.")
- to_chat(src, "Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].")
- to_chat(src, "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.")
- 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
+ if (byond_version >= 512)
+ if (!byond_build || byond_build < 1386)
+ message_admins("[key_name(src)] has been detected as spoofing their byond version. Connection rejected.")
+ add_system_note("Spoofed-Byond-Version", "Detected as using a spoofed byond version.")
+ log_access("Failed Login: [key] - Spoofed byond version")
qdel(src)
- return
+
+ if (num2text(byond_build) in GLOB.blacklisted_builds)
+ log_access("Failed login: [key] - blacklisted byond version")
+ to_chat(src, "Your version of byond is blacklisted.")
+ to_chat(src, "Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].")
+ to_chat(src, "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.")
+ 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
if(SSinput.initialized)
set_macros()
@@ -380,8 +376,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if (menuitem)
menuitem.Load_checked(src)
- hook_vr("client_new",list(src)) // CIT CHANGE - hook for client/New() changes
-
Master.UpdateTickRate()
//////////////
@@ -409,21 +403,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
"Someone come hold me :(",\
"I need someone on me :(",\
"What happened? Where has everyone gone?",\
- "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"\
+ "Forever alone :("\
)
send2irc("Server", "[cheesy_message] (No admins online)")
@@ -623,10 +603,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
to_chat(src, {"You will be automatically taken to the game, if not, click here to be taken manually"})
/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)
//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())
return
if(query_get_notes.NextRow())
@@ -636,9 +619,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if(!query_get_notes.Execute())
return
if(query_get_notes.NextRow())
- if (query_get_notes.item[1] == adminckey)
+ if (query_get_notes.item[1] == system_ckey)
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()
@@ -730,12 +713,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if (isnull(new_size))
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
apply_clickcatcher()
if (isliving(mob))
@@ -754,4 +731,4 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
/client/proc/AnnouncePR(announcement)
if(prefs && prefs.chat_toggles & CHAT_PULLR)
- to_chat(src, announcement)
\ No newline at end of file
+ to_chat(src, announcement)
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index 52139fd7bc..255423a4fc 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -250,20 +250,6 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings, listen_ooc)()
/datum/verbs/menu/Settings/listen_ooc/Get_checked(client/C)
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", \
"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()
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!
-
diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm
index bb928c6fa5..3c49b113e9 100644
--- a/code/modules/hydroponics/grown/replicapod.dm
+++ b/code/modules/hydroponics/grown/replicapod.dm
@@ -20,6 +20,7 @@
var/blood_type = null
var/list/features = null
var/factions = null
+ var/list/traits = null
var/contains_sample = 0
/obj/item/seeds/replicapod/attackby(obj/item/W, mob/user, params)
@@ -34,6 +35,7 @@
blood_type = bloodSample.data["blood_type"]
features = bloodSample.data["features"]
factions = bloodSample.data["factions"]
+ traits = bloodSample.data["traits"]
W.reagents.clear_reagents()
to_chat(user, "You inject the contents of the syringe into the seeds.")
contains_sample = 1
@@ -99,6 +101,8 @@
podman.faction |= factions
if(!features["mcolor"])
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.set_cloned_appearance()
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index 054d97c9f1..1fd5e30424 100644
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -160,3 +160,4 @@ GLOBAL_LIST_EMPTY(z_is_planet)
. = ..()
var/turf/T = get_turf(src)
GLOB.z_is_planet["[T.z]"] = TRUE
+
diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm
index 3e241fbc72..e73349198b 100644
--- a/code/modules/mining/equipment/mining_tools.dm
+++ b/code/modules/mining/equipment/mining_tools.dm
@@ -125,4 +125,4 @@
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
force = 5
throwforce = 7
- w_class = WEIGHT_CLASS_SMALL
\ No newline at end of file
+ w_class = WEIGHT_CLASS_SMALL
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 25eb19b644..358867e6ad 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -379,9 +379,8 @@
if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergencyCallTime)*0.5)
SSticker.mode.make_antag_chance(humanc)
- for(var/V in character.roundstart_traits)
- var/datum/trait/T = V
- T.on_spawn() //so latejoins still get their correct traits
+ if(CONFIG_GET(flag/roundstart_traits))
+ SStraits.AssignTraits(humanc, humanc.client, TRUE)
log_manifest(character.mind.key,character.mind,character,latejoin = TRUE)
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index f7b3d15155..41715d650d 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -191,6 +191,10 @@
blood_data["real_name"] = real_name
blood_data["features"] = dna.features
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
//get the id of the substance this mob use as blood.
diff --git a/code/modules/mob/living/carbon/human/species_types/corporate.dm b/code/modules/mob/living/carbon/human/species_types/corporate.dm
index 552e413f4d..aa310723dd 100644
--- a/code/modules/mob/living/carbon/human/species_types/corporate.dm
+++ b/code/modules/mob/living/carbon/human/species_types/corporate.dm
@@ -17,4 +17,4 @@
use_skintones = 0
species_traits = list(SPECIES_ORGANIC,NOBLOOD,EYECOLOR)
inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOHUNGER)
- sexes = 0
+ sexes = 0
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index 2297fffa97..41af8cc865 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -4,15 +4,12 @@
id = "jelly"
default_color = "00FF90"
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)
- 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
exotic_blood = "slimejelly"
damage_overlay_type = ""
var/datum/action/innate/regenerate_limbs/regenerate_limbs
- var/datum/action/innate/slime_change/slime_change //CIT CHANGE
liked_food = MEAT
coldmod = 6 // = 3x cold damage
heatmod = 0.5 // = 1/4x heat damage
@@ -21,8 +18,6 @@
/datum/species/jelly/on_species_loss(mob/living/carbon/C)
if(regenerate_limbs)
regenerate_limbs.Remove(C)
- if(slime_change) //CIT CHANGE
- slime_change.Remove(C) //CIT CHANGE
C.remove_language(/datum/language/slime)
C.faction -= "slime"
..()
@@ -34,8 +29,6 @@
if(ishuman(C))
regenerate_limbs = new
regenerate_limbs.Grant(C)
- slime_change = new //CIT CHANGE
- slime_change.Grant(C) //CIT CHANGE
C.faction |= "slime"
/datum/species/jelly/spec_life(mob/living/carbon/human/H)
@@ -384,6 +377,7 @@
around.",
"...and move this one instead.")
+
///////////////////////////////////LUMINESCENTS//////////////////////////////////////////
//Luminescents are able to consume and use slime extracts, without them decaying.
@@ -542,7 +536,6 @@
if(species.current_extract)
species.extract_cooldown = world.time + 100
-
var/cooldown = species.current_extract.activate(H, species, activation_type)
species.extract_cooldown = world.time + cooldown
@@ -555,8 +548,6 @@
///////////////////////////////////STARGAZERS//////////////////////////////////////////
//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
name = "Stargazer"
@@ -725,5 +716,4 @@
to_chat(H, "You connect [target]'s mind to your slime link!")
else
to_chat(H, "You can't seem to link [target]'s mind...")
- to_chat(target, "The foreign presence leaves your mind.")
-
+ to_chat(target, "The foreign presence leaves your mind.")
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
index 7f0d8afe26..8735d6ceb6 100644
--- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
@@ -54,7 +54,7 @@
/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()
if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible
return TRUE
diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm
index 7f6206dafb..aa1f5aff65 100644
--- a/code/modules/mob/living/silicon/robot/laws.dm
+++ b/code/modules/mob/living/silicon/robot/laws.dm
@@ -66,4 +66,4 @@
temp = master.supplied[index]
if (length(temp) > 0)
laws.supplied[index] = temp
- return
+ return
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 131748042c..2b1ff82af7 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -159,8 +159,6 @@
toner = tonermax
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
/mob/living/silicon/robot/Destroy()
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))
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
if(!input_module || module.type != /obj/item/robot_module)
return
@@ -367,12 +363,8 @@
to_chat(user, "You start fixing yourself...")
if(!W.use_tool(src, user, 50))
return
- adjustBruteLoss(-10)
- else
- to_chat(user, "You start fixing [src]...")
- if(!do_after(user, 30, target = src))
- return
- adjustBruteLoss(-30)
+
+ adjustBruteLoss(-30)
updatehealth()
add_fingerprint(user)
visible_message("[user] has fixed some of the dents on [src].")
@@ -382,16 +374,11 @@
user.changeNext_move(CLICK_CD_MELEE)
var/obj/item/stack/cable_coil/coil = W
if (getFireLoss() > 0 || getToxLoss() > 0)
- if(src == user && coil.use(1))
+ if(src == user)
to_chat(user, "You start fixing yourself...")
if(!do_after(user, 50, target = src))
return
- adjustFireLoss(-10)
- adjustToxLoss(-10)
if (coil.use(1))
- to_chat(user, "You start fixing [src]...")
- if(!do_after(user, 30, target = src))
- return
adjustFireLoss(-30)
adjustToxLoss(-30)
updatehealth()
@@ -600,36 +587,6 @@
/mob/living/silicon/robot/update_icons()
cut_overlays()
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(!eye_lights)
eye_lights = new()
@@ -1023,7 +980,6 @@
designation = module.name
if(hands)
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)
status_flags |= CANPUSH
else
diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
index a3bc98f48a..646987b155 100644
--- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm
+++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
@@ -22,7 +22,7 @@
ventcrawler = VENTCRAWLER_ALWAYS
var/datum/mind/origin
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)
var/obj/item/organ/body_egg/changeling_egg/egg = new(victim)
diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm
index 480b49277c..106381bade 100644
--- a/code/modules/mob/living/status_procs.dm
+++ b/code/modules/mob/living/status_procs.dm
@@ -146,13 +146,13 @@
else
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))
return
if(!SStraits || !SStraits.traits[trait])
return
var/datum/trait/T = SStraits.traits[trait]
- new T (src)
+ new T (src, spawn_effects)
return TRUE
/mob/living/proc/remove_trait(trait, list/sources, force)
@@ -192,13 +192,14 @@
. = FALSE
+ if(sources && !islist(sources))
+ sources = list(sources)
if(LAZYLEN(sources))
for(var/S in sources)
if(S in status_traits[trait])
return TRUE
- else
- if(LAZYLEN(status_traits[trait]))
- return TRUE
+ else if(LAZYLEN(status_traits[trait]))
+ return TRUE
/mob/living/proc/has_trait_datum(trait)
return roundstart_traits[trait]
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index ce4e1f7ad2..3b06b4c71e 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -16,6 +16,8 @@
var/recharged = 0
var/recharge_delay = 5
var/mutable_appearance/beaker_overlay
+ var/working_state = "dispenser_working"
+ var/nopower_state = "dispenser_nopower"
var/obj/item/reagent_containers/beaker = null
var/list/dispensable_reagents = list(
"hydrogen",
@@ -60,6 +62,7 @@
cell = new cell_type
recharge()
dispensable_reagents = sortList(dispensable_reagents)
+ update_icon()
/obj/machinery/chem_dispenser/Destroy()
QDEL_NULL(beaker)
@@ -67,13 +70,36 @@
return ..()
/obj/machinery/chem_dispenser/process()
-
if(recharged < 0)
recharge()
recharged = recharge_delay
else
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()
if(stat & (BROKEN|NOPOWER))
return
@@ -163,6 +189,7 @@
var/target = text2num(params["target"])
if(target in beaker.possible_transfer_amounts)
amount = target
+ work_animation()
. = TRUE
if("dispense")
var/reagent = params["reagent"]
@@ -173,11 +200,13 @@
R.add_reagent(reagent, actual)
cell.use((actual / 10) / powerefficiency)
+ work_animation()
. = TRUE
if("remove")
var/amount = text2num(params["amount"])
if(beaker && amount in beaker.possible_transfer_amounts)
beaker.reagents.remove_all(amount)
+ work_animation()
. = TRUE
if("eject")
if(beaker)
@@ -185,7 +214,7 @@
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
- cut_overlays()
+ update_icon()
. = TRUE
if("dispense_recipe")
var/recipe_to_use = params["recipe"]
@@ -200,6 +229,7 @@
if(actual)
R.add_reagent(r_id, actual)
cell.use((actual / 10) / powerefficiency)
+ work_animation()
if("clear_recipes")
var/yesno = alert("Clear all recipes?",, "Yes","No")
if(yesno == "Yes")
@@ -226,23 +256,17 @@
/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params)
if(default_unfasten_wrench(user, I))
return
-
if(istype(I, /obj/item/reagent_containers) && !(I.flags_1 & ABSTRACT_1) && I.is_open_container())
var/obj/item/reagent_containers/B = I
. = 1 //no afterattack
if(beaker)
to_chat(user, "A container is already loaded into [src]!")
return
-
if(!user.transferItemToLoc(B, src))
return
-
beaker = B
to_chat(user, "You add [B] to [src].")
-
- beaker_overlay = beaker_overlay || mutable_appearance(icon, "disp_beaker")
- beaker_overlay.pixel_x = rand(-10, 5)//randomize beaker overlay position.
- add_overlay(beaker_overlay)
+ update_icon()
else if(user.a_intent != INTENT_HARM && !istype(I, /obj/item/card/emag))
to_chat(user, "You can't load [I] into [src]!")
return ..()
@@ -266,6 +290,7 @@
beaker.reagents.remove_all()
cell.use(total/powerefficiency)
cell.emp_act(severity)
+ work_animation()
visible_message("[src] malfunctions, spraying chemicals everywhere!")
..()
@@ -278,6 +303,8 @@
recharge_delay = 20
dispensable_reagents = list()
circuit = /obj/item/circuitboard/machine/chem_dispenser
+ working_state = "minidispenser_working"
+ nopower_state = "minidispenser_nopower"
var/static/list/dispensable_reagent_tiers = list(
list(
"hydrogen",
@@ -362,6 +389,29 @@
final_list += list(avoid_assoc_duplicate_keys(fuck[1],key_list) = text2num(fuck[2]))
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
name = "soda dispenser"
desc = "Contains a large reservoir of soft drinks."
@@ -369,6 +419,10 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "soda_dispenser"
amount = 10
+ pixel_y = 6
+ layer = WALL_OBJ_LAYER
+ working_state = null
+ nopower_state = null
dispensable_reagents = list(
"water",
"ice",
@@ -398,8 +452,6 @@
"tirizene"
)
-
-
/obj/machinery/chem_dispenser/drinks/beer
name = "booze dispenser"
desc = "Contains a large reservoir of the good stuff."
diff --git a/code/modules/reagents/chemistry/machinery/scp_294.dm b/code/modules/reagents/chemistry/machinery/scp_294.dm
index 2b63799547..5aa09d407b 100644
--- a/code/modules/reagents/chemistry/machinery/scp_294.dm
+++ b/code/modules/reagents/chemistry/machinery/scp_294.dm
@@ -14,6 +14,8 @@
icon_state = "294_bottom"
amount = 10
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
+ working_state = null
+ nopower_state = null
var/static/list/shortcuts = list(
"meth" = "methamphetamine",
"tricord" = "tricordrazine"
@@ -25,7 +27,7 @@
GLOB.poi_list += src
top_overlay = mutable_appearance(icon, "294_top", layer = ABOVE_ALL_MOB_LAYER)
update_icon()
-
+
/obj/machinery/chem_dispenser/scp_294/update_icon()
cut_overlays()
@@ -36,6 +38,9 @@
GLOB.poi_list -= src
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, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
diff --git a/code/modules/reagents/reagent_containers/medspray.dm b/code/modules/reagents/reagent_containers/medspray.dm
new file mode 100644
index 0000000000..2f715084ad
--- /dev/null
+++ b/code/modules/reagents/reagent_containers/medspray.dm
@@ -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, "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.")
+
+/obj/item/reagent_containers/medspray/attack(mob/M, mob/user, def_zone)
+ if(!reagents || !reagents.total_volume)
+ to_chat(user, "[src] is empty!")
+ return
+
+ if(M == user)
+ M.visible_message("[user] attempts to [apply_method] [src] on themselves.")
+ if(self_delay)
+ if(!do_mob(user, M, self_delay))
+ return
+ if(!reagents || !reagents.total_volume)
+ return
+ to_chat(M, "You [apply_method] yourself with [src].")
+
+ else
+ add_logs(user, M, "attempted to apply", src, reagents.log_list())
+ M.visible_message("[user] attempts to [apply_method] [src] on [M].", \
+ "[user] attempts to [apply_method] [src] on [M].")
+ if(!do_mob(user, M))
+ return
+ if(!reagents || !reagents.total_volume)
+ return
+ M.visible_message("[user] [apply_method]s [M] down with [src].", \
+ "[user] [apply_method]s [M] down with [src].")
+
+ 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)
diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm
index 68507d673a..655f6ffc7d 100644
--- a/code/modules/reagents/reagent_containers/spray.dm
+++ b/code/modules/reagents/reagent_containers/spray.dm
@@ -171,19 +171,6 @@
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
/obj/item/reagent_containers/spray/pepper
name = "pepperspray"
diff --git a/code/modules/security_levels/security_levels.dm b/code/modules/security_levels/security_levels.dm
index 61c3f8833f..282cfada7f 100644
--- a/code/modules/security_levels/security_levels.dm
+++ b/code/modules/security_levels/security_levels.dm
@@ -41,8 +41,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN)
if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
SSshuttle.emergency.modTimer(2)
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)
if(is_station_level(FA.z))
FA.update_icon()
@@ -57,7 +55,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN)
else
minor_announce(CONFIG_GET(string/alert_red_downto), "Attention! Code 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)
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)
SSshuttle.emergency.modTimer(0.5)
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)
if(is_station_level(FA.z))
FA.update_icon()
diff --git a/code/modules/spells/spell_types/emplosion.dm b/code/modules/spells/spell_types/emplosion.dm
index e6393e8584..8c45c06379 100644
--- a/code/modules/spells/spell_types/emplosion.dm
+++ b/code/modules/spells/spell_types/emplosion.dm
@@ -15,4 +15,4 @@
continue
empulse(target.loc, emp_heavy, emp_light)
- return
+ return
\ No newline at end of file
diff --git a/code/modules/spells/spell_types/inflict_handler.dm b/code/modules/spells/spell_types/inflict_handler.dm
index a1ba69b426..da0af7a601 100644
--- a/code/modules/spells/spell_types/inflict_handler.dm
+++ b/code/modules/spells/spell_types/inflict_handler.dm
@@ -49,4 +49,4 @@
target.blur_eyes(amt_eye_blurry)
//summoning
if(summon_type)
- new summon_type(target.loc, target)
+ new summon_type(target.loc, target)
\ No newline at end of file
diff --git a/modular_citadel/code/modules/client/preferences_toggles.dm b/modular_citadel/code/modules/client/preferences_toggles.dm
index 4b92a1e9c3..a475d65106 100644
--- a/modular_citadel/code/modules/client/preferences_toggles.dm
+++ b/modular_citadel/code/modules/client/preferences_toggles.dm
@@ -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.")
/datum/verbs/menu/Settings/Sound/toggledigestionnoise/Get_checked(client/C)
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
\ No newline at end of file