Merge branch 'master' into upstream-merge-37486

This commit is contained in:
LetterJay
2018-05-21 08:59:32 -05:00
committed by GitHub
403 changed files with 10034 additions and 7488 deletions
+1 -1
View File
@@ -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: You must first sign into your BYOND account or 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")
//Population Cap Checking
var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
+2 -2
View File
@@ -434,7 +434,7 @@
return
var/list/options = list("Regular Restart", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
if(SERVER_TOOLS_PRESENT)
if(world.TgsAvailable())
options += "Server Restart (Kill and restart DD)";
var/rebootconfirm
@@ -459,7 +459,7 @@
world.Reboot(fast_track = TRUE)
if("Server Restart (Kill and restart DD)")
to_chat(world, "Server restart - [init_by]")
SERVER_TOOLS_REBOOT_BYOND
world.TgsEndProcess()
/datum/admins/proc/end_round()
set category = "Server"
-1
View File
@@ -242,7 +242,6 @@ GLOBAL_PROTECT(protected_ranks)
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
+1
View File
@@ -93,6 +93,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/forceEvent,
/client/proc/admin_change_sec_level,
/client/proc/toggle_nuke,
/client/proc/run_weather,
/client/proc/mass_zombie_infection,
/client/proc/mass_zombie_cure,
/client/proc/polymorph_all,
+31 -28
View File
@@ -1,12 +1,12 @@
#define IRC_STATUS_THROTTLE 5
/datum/server_tools_command/ircstatus
/datum/tgs_chat_command/ircstatus
name = "status"
help_text = "Gets the admincount, playercount, gamemode, and true game mode of the server"
admin_only = TRUE
var/static/last_irc_status = 0
var/last_irc_status = 0
/datum/server_tools_command/ircstatus/Run(sender, params)
/datum/tgs_chat_command/ircstatus/Run(datum/tgs_chat_user/sender, params)
var/rtod = REALTIMEOFDAY
if(rtod - last_irc_status < IRC_STATUS_THROTTLE)
return
@@ -17,12 +17,12 @@
status += "Players: [GLOB.clients.len] (Active: [get_active_player_count(0,1,0)]). Mode: [SSticker.mode ? SSticker.mode.name : "Not started"]."
return status
/datum/server_tools_command/irccheck
/datum/tgs_chat_command/irccheck
name = "check"
help_text = "Gets the playercount, gamemode, and address of the server"
var/static/last_irc_check = 0
var/last_irc_check = 0
/datum/server_tools_command/irccheck/Run(sender, params)
/datum/tgs_chat_command/irccheck/Run(datum/tgs_chat_user/sender, params)
var/rtod = REALTIMEOFDAY
if(rtod - last_irc_check < IRC_STATUS_THROTTLE)
return
@@ -30,14 +30,15 @@
var/server = CONFIG_GET(string/server)
return "[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]" //CIT CHANGE - obfuscates the current gamemode from players
/datum/server_tools_command/ahelp
/datum/tgs_chat_command/ahelp
name = "ahelp"
help_text = "<ckey|ticket #> <message|ticket <close|resolve|icissue|reject|reopen <ticket #>|list>>"
required_parameters = 2
admin_only = TRUE
/datum/server_tools_command/ahelp/Run(sender, params)
/datum/tgs_chat_command/ahelp/Run(datum/tgs_chat_user/sender, params)
var/list/all_params = splittext(params, " ")
if(all_params.len < 2)
return "Insufficient parameters"
var/target = all_params[1]
all_params.Cut(1, 2)
var/id = text2num(target)
@@ -47,52 +48,54 @@
target = AH.initiator_ckey
else
return "Ticket #[id] not found!"
var/res = IrcPm(target, all_params.Join(" "), sender)
var/res = IrcPm(target, all_params.Join(" "), sender.friendly_name)
if(res != "Message Successful")
return res
/datum/server_tools_command/namecheck
/datum/tgs_chat_command/namecheck
name = "namecheck"
help_text = "Returns info on the specified target"
required_parameters = 1
admin_only = TRUE
/datum/server_tools_command/namecheck/Run(sender, params)
log_admin("Chat Name Check: [sender] on [params]")
message_admins("Name checking [params] from [sender]")
/datum/tgs_chat_command/namecheck/Run(datum/tgs_chat_user/sender, params)
params = trim(params)
if(!params)
return "Insufficient parameters"
log_admin("Chat Name Check: [sender.friendly_name] on [params]")
message_admins("Name checking [params] from [sender.friendly_name]")
return keywords_lookup(params, 1)
/datum/server_tools_command/adminwho
/datum/tgs_chat_command/adminwho
name = "adminwho"
help_text = "Lists administrators currently on the server"
admin_only = TRUE
/datum/server_tools_command/adminwho/Run(sender, params)
/datum/tgs_chat_command/adminwho/Run(datum/tgs_chat_user/sender, params)
return ircadminwho()
GLOBAL_LIST(round_end_notifiees)
/datum/server_tools_command/notify
/datum/tgs_chat_command/notify
name = "notify"
help_text = "Pings the invoker when the round ends"
admin_only = TRUE
/datum/server_tools_command/notify/Run(sender, params)
/datum/tgs_chat_command/notify/Run(datum/tgs_chat_user/sender, params)
if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted())
return "[sender], the round has already ended!"
return "[sender.mention], the round has already ended!"
LAZYINITLIST(GLOB.round_end_notifiees)
GLOB.round_end_notifiees[sender] = TRUE
return "I will notify [sender] when the round ends."
return "I will notify [sender.mention] when the round ends."
/datum/server_tools_command/sdql
/datum/tgs_chat_command/sdql
name = "sdql"
help_text = "Runs an SDQL query"
admin_only = TRUE
/datum/server_tools_command/sdql/Run(sender, params)
/datum/tgs_chat_command/sdql/Run(datum/tgs_chat_user/sender, params)
if(GLOB.AdminProcCaller)
return "Unable to run query, another admin proc call is in progress. Try again later."
GLOB.AdminProcCaller = "CHAT_[sender]" //_ won't show up in ckeys so it'll never match with a real admin
GLOB.AdminProcCaller = "CHAT_[sender.friendly_name]" //_ won't show up in ckeys so it'll never match with a real admin
var/list/results = world.SDQL2_query(params, GLOB.AdminProcCaller, GLOB.AdminProcCaller)
GLOB.AdminProcCaller = null
if(!results)
@@ -100,13 +103,13 @@ GLOBAL_LIST(round_end_notifiees)
var/list/text_res = results.Copy(1, 3)
var/list/refs = results.len > 3 ? results.Copy(4) : null
. = "[text_res.Join("\n")][refs ? "\nRefs: [refs.Join(" ")]" : ""]"
/datum/server_tools_command/reload_admins
/datum/tgs_chat_command/reload_admins
name = "reload_admins"
help_text = "Forces the server to reload admins."
admin_only = TRUE
/datum/server_tools_command/reload_admins/Run(sender, params)
/datum/tgs_chat_command/reload_admins/Run(datum/tgs_chat_user/sender, params)
load_admins()
log_admin("[sender] reloaded admins via chat command.")
log_admin("[sender.friendly_name] reloaded admins via chat command.")
return "Admins reloaded."
+11
View File
@@ -22,6 +22,17 @@
H.facial_hair_color = H.hair_color
H.eye_color = random_eye_color()
H.dna.blood_type = random_blood_type()
// Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant.
H.dna.features["mcolor"] = random_short_color()
H.dna.features["tail_lizard"] = pick(GLOB.tails_list_lizard)
H.dna.features["snout"] = pick(GLOB.snouts_list)
H.dna.features["horns"] = pick(GLOB.horns_list)
H.dna.features["frills"] = pick(GLOB.frills_list)
H.dna.features["spines"] = pick(GLOB.spines_list)
H.dna.features["body_markings"] = pick(GLOB.body_markings_list)
H.dna.features["moth_wings"] = pick(GLOB.moth_wings_list)
H.update_body()
H.update_hair()
H.update_body_parts()
+142 -54
View File
@@ -6,56 +6,106 @@
return
usr.client.holder.edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions(action, target, operation, page)
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:center;'>[adm_ckey]<br>[deadminlink]<a class='small' href='?src=[REF(src)];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=sync;ckey=[adm_ckey]'>\[SYNC TGDB\]</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")
var/list/output = list("<link rel='stylesheet' type='text/css' href='panels.css'><a href='?_src_=holder;[HrefToken()];editrightsbrowser=1'>\[Permissions\]</a>")
if(action)
output += " | <a href='?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightspage=0'>\[Log\]</a> | <a href='?_src_=holder;[HrefToken()];editrightsbrowsermanage=1'>\[Management\]</a><hr style='background:#000000; border:0; height:3px'>"
else
output += "<br><a href='?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightspage=0'>\[Log\]</a><br><a href='?_src_=holder;[HrefToken()];editrightsbrowsermanage=1'>\[Management\]</a>"
if(action == 1)
var/list/searchlist = list(" WHERE ")
if(target)
searchlist += "ckey = '[sanitizeSQL(target)]'"
if(operation)
if(target)
searchlist += " AND "
searchlist += "operation = '[sanitizeSQL(operation)]'"
var/search
if(searchlist.len > 1)
search = searchlist.Join("")
var/logcount = 0
var/logssperpage = 20
var/pagecount = 0
page = text2num(page)
var/datum/DBQuery/query_count_admin_logs = SSdbcore.NewQuery("SELECT COUNT(id) FROM [format_table_name("admin_log")][search]")
if(!query_count_admin_logs.warn_execute())
return
if(query_count_admin_logs.NextRow())
logcount = text2num(query_count_admin_logs.item[1])
if(logcount > logssperpage)
output += "<br><b>Page: </b>"
while(logcount > 0)
output += "|<a href='?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightstarget=[target];editrightsoperation=[operation];editrightspage=[pagecount]'>[pagecount == page ? "<b>\[[pagecount]\]</b>" : "\[[pagecount]\]"]</a>"
logcount -= logssperpage
pagecount++
output += "|"
var/limit = " LIMIT [logssperpage * page], [logssperpage]"
var/datum/DBQuery/query_search_admin_logs = SSdbcore.NewQuery("SELECT datetime, round_id, adminckey, operation, target, log FROM [format_table_name("admin_log")][search] ORDER BY datetime DESC[limit]")
if(!query_search_admin_logs.warn_execute())
return
while(query_search_admin_logs.NextRow())
var/datetime = query_search_admin_logs.item[1]
var/round_id = query_search_admin_logs.item[2]
var/admin_ckey = query_search_admin_logs.item[3]
operation = query_search_admin_logs.item[4]
target = query_search_admin_logs.item[5]
var/log = query_search_admin_logs.item[6]
output += "<p style='margin:0px'><b>[datetime] | Round ID [round_id] | Admin [admin_ckey] | Operation [operation] on [target]</b><br>[log]</p><hr style='background:#000000; border:0; height:3px'>"
if(action == 2)
output += "<h3>Admin ckeys with invalid ranks</h3>"
var/datum/DBQuery/query_check_admin_errors = SSdbcore.NewQuery("SELECT ckey, [format_table_name("admin")].rank FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].rank = [format_table_name("admin")].rank WHERE [format_table_name("admin_ranks")].rank IS NULL")
if(!query_check_admin_errors.warn_execute())
return
while(query_check_admin_errors.NextRow())
var/admin_ckey = query_check_admin_errors.item[1]
var/admin_rank = query_check_admin_errors.item[2]
output += "[admin_ckey] has non-existant rank [admin_rank] | <a href='?_src_=holder;[HrefToken()];editrightsbrowsermanage=1;editrightschange=[admin_ckey]'>\[Change Rank\]</a> | <a href='?_src_=holder;[HrefToken()];editrightsbrowsermanage=1;editrightsremove=[admin_ckey]'>\[Remove\]</a>"
output += "<hr style='background:#000000; border:0; height:1px'>"
output += "<h3>Unused ranks</h3>"
var/datum/DBQuery/query_check_unused_rank = SSdbcore.NewQuery("SELECT [format_table_name("admin_ranks")].rank FROM [format_table_name("admin_ranks")] LEFT JOIN [format_table_name("admin")] ON [format_table_name("admin")].rank = [format_table_name("admin_ranks")].rank WHERE [format_table_name("admin")].rank IS NULL")
if(!query_check_unused_rank.warn_execute())
return
while(query_check_unused_rank.NextRow())
var/admin_rank = query_check_unused_rank.item[1]
output += "Rank [admin_rank] is not held by any admin | <a href='?_src_=holder;[HrefToken()];editrightsbrowsermanage=1;editrightsremoverank=[admin_rank]'>\[Remove\]</a>"
output += "<hr style='background:#000000; border:0; height:1px'>"
else if(!action)
output += {"<head>
<title>Permissions Panel</title>
<script type='text/javascript' src='search.js'></script>
</head>
<body onload='selectTextField();updateSearch();'>
<div id='main'><table id='searchable' cellspacing='0'>
<tr class='title'>
<th style='width:150px;'>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:center;'>[adm_ckey]<br>[deadminlink]<a class='small' href='?src=[REF(src)];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=sync;ckey=[adm_ckey]'>\[SYNC TGDB\]</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>"
usr << browse("<!DOCTYPE html><html>[jointext(output, "")]</html>","window=editrights;size=1000x650")
/datum/admins/proc/edit_rights_topic(list/href_list)
if(!check_rights(R_PERMISSIONS))
@@ -142,10 +192,17 @@
return FALSE
if(use_db)
. = sanitizeSQL(.)
//if an admin exists without a datum they won't be caught by the above
var/datum/DBQuery/query_admin_in_db = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("admin_ranks")] WHERE ckey = '[.]'")
if(!query_admin_in_db.warn_execute())
return FALSE
if(query_admin_in_db.NextRow())
to_chat(usr, "<span class='danger'>[.] already listed in admin database. Check the Management tab if they don't appear in the list of admins.</span>")
return FALSE
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 FALSE
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: [.]')")
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add admin', '[.]', 'New admin added: [.]')")
if(!query_add_admin_log.warn_execute())
return FALSE
@@ -153,12 +210,13 @@
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(D)
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]')")
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove admin', '[admin_ckey]', '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"]")
@@ -216,13 +274,13 @@
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_ranks")] (rank, flags, exclude_flags, can_edit_flags) 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]')")
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add rank', '[new_rank]', 'New rank added: [new_rank]')")
if(!query_add_rank_log.warn_execute())
return
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]')")
var/datum/DBQuery/query_change_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change admin rank', '[admin_ckey]', '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
@@ -259,7 +317,7 @@
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," ", "*")]')")
var/datum/DBQuery/query_change_rank_flags_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change rank flags', '[D.rank.name]', 'Permissions of [D.rank.name] 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)
@@ -294,6 +352,36 @@
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"]")
/datum/admins/proc/remove_rank(admin_rank)
if(!admin_rank)
return
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == admin_rank && (!(R.rights & usr.client.holder.rank.can_edit_rights) == R.rights))
to_chat(usr, "<span class='admin prefix'>You don't have edit rights to all the rights this rank has, rank deletion not permitted.</span>")
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && (admin_rank in GLOB.protected_ranks))
to_chat(usr, "<span class='admin prefix'>Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.</span>")
return
if(CONFIG_GET(flag/load_legacy_ranks_only))
to_chat(usr, "<span class='admin prefix'>Rank deletion not permitted while database rank loading is disabled.</span>")
return
admin_rank = sanitizeSQL(admin_rank)
var/datum/DBQuery/query_admins_with_rank = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("admin")] WHERE rank = '[admin_rank]'")
if(!query_admins_with_rank.warn_execute())
return
if(query_admins_with_rank.NextRow())
to_chat(usr, "<span class='danger'>Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen.</span>")
return
if(alert("Are you sure you want to remove [admin_rank]?","Confirm Removal","Do it","Cancel") == "Do it")
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("DELETE FROM [format_table_name("admin_ranks")] WHERE rank = '[admin_rank]'")
if(!query_add_rank.warn_execute())
return
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove rank', '[admin_rank]', 'Rank removed: [admin_rank]')")
if(!query_add_rank_log.warn_execute())
return
message_admins("[key_name_admin(usr)] removed rank [admin_rank] permanently")
log_admin("[key_name(usr)] removed rank [admin_rank] permanently")
/datum/admins/proc/sync_lastadminrank(admin_ckey, datum/admins/D)
var/sqlrank = sanitizeSQL(D.rank.name)
admin_ckey = sanitizeSQL(admin_ckey)
+28
View File
@@ -273,6 +273,21 @@
return
create_message("note", banckey, null, banreason, null, null, 0, 0)
else if(href_list["editrightsbrowser"])
edit_admin_permissions(0)
else if(href_list["editrightsbrowserlog"])
edit_admin_permissions(1, href_list["editrightstarget"], href_list["editrightsoperation"], href_list["editrightspage"])
if(href_list["editrightsbrowsermanage"])
if(href_list["editrightschange"])
change_admin_rank(href_list["editrightschange"], TRUE)
else if(href_list["editrightsremove"])
remove_admin(href_list["editrightsremove"], TRUE)
else if(href_list["editrightsremoverank"])
remove_rank(href_list["editrightsremoverank"])
edit_admin_permissions(2)
else if(href_list["editrights"])
edit_rights_topic(href_list)
@@ -2448,6 +2463,19 @@
usr.client.cmd_admin_mod_antag_rep(C, href_list["modantagrep"])
show_player_panel(M)
else if(href_list["slowquery"])
if(!check_rights(R_ADMIN))
return
var/answer = href_list["slowquery"]
if(answer == "yes")
log_query_debug("[usr.key] | Reported a server hang")
if(alert(usr, "Had you just press any admin buttons?", "Query server hang report", "Yes", "No") == "Yes")
var/response = input(usr,"What were you just doing?","Query server hang report") as null|text
if(response)
log_query_debug("[usr.key] | [response]")
else if(answer == "no")
log_query_debug("[usr.key] | Reported no server hang")
/datum/admins/proc/HandleCMode()
if(!check_rights(R_ADMIN))
return
+1 -1
View File
@@ -594,7 +594,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/proc/send2irc(msg,msg2)
msg = replacetext(replacetext(msg, "\proper", ""), "\improper", "")
msg2 = replacetext(replacetext(msg2, "\proper", ""), "\improper", "")
SERVER_TOOLS_RELAY_BROADCAST("[msg] | [msg2]")
world.TgsTargetedChatBroadcast("[msg] | [msg2]", TRUE)
/proc/send2otherserver(source,msg,type = "Ahelp")
var/comms_key = CONFIG_GET(string/comms_key)
+1 -1
View File
@@ -95,7 +95,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Key") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/Getmob(mob/M in GLOB.mob_list)
/client/proc/Getmob(mob/M in GLOB.mob_list - GLOB.dummy_mob_list)
set category = "Admin"
set name = "Get Mob"
set desc = "Mob to teleport"
+1 -1
View File
@@ -27,7 +27,7 @@
if(isnewplayer(M))
continue
if (M.stat == DEAD || (M.client && M.client.holder && (M.client.prefs.chat_toggles & CHAT_DEAD))) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
M.show_message(rendered, 2)
to_chat(M, rendered)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Dsay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+6 -2
View File
@@ -110,10 +110,14 @@ GLOBAL_PROTECT(LastAdminCalledProc)
GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention)
GLOBAL_PROTECT(AdminProcCallSpamPrevention)
/proc/WrapAdminProcCall(target, procname, list/arguments)
/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target && procname == "Del")
to_chat(usr, "Calling Del() is not allowed")
return
if(!target.CanProcCall(procname))
to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!")
return
var/current_caller = GLOB.AdminProcCaller
var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller
if(!ckey)
@@ -137,7 +141,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
GLOB.AdminProcCaller = null
//adv proc call this, ya nerds
/world/proc/WrapAdminProcCall(target, procname, list/arguments)
/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target == GLOBAL_PROC)
return call(procname)(arglist(arguments))
else if(target != world)
+24
View File
@@ -1043,6 +1043,30 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
for(var/obj/machinery/shuttle_manipulator/M in GLOB.machines)
M.ui_interact(usr)
/client/proc/run_weather()
set category = "Fun"
set name = "Run Weather"
set desc = "Triggers a weather on the z-level you choose."
if(!holder)
return
var/weather_type = input("Choose a weather", "Weather") as null|anything in subtypesof(/datum/weather)
if(!weather_type)
return
var/z_level = input("Z-Level to target? Leave blank to target current Z-Level.", "Z-Level") as num|null
if(!isnum(z_level))
if(!src.mob)
return
z_level = src.mob.z
SSweather.run_weather(weather_type, z_level)
message_admins("[key_name_admin(usr)] started weather of type [weather_type] on the z-level [z_level].")
log_admin("[key_name(usr)] started weather of type [weather_type] on the z-level [z_level].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Run Weather")
/client/proc/mass_zombie_infection()
set category = "Fun"
set name = "Mass Zombie Infection"
@@ -122,7 +122,7 @@
owner.grant_language(/datum/language/slime)
/obj/item/organ/heart/gland/slime/activate()
to_chat(owner, "<span class='warning'>You feel nauseous!</span>")
to_chat(owner, "<span class='warning'>You feel nauseated!</span>")
owner.vomit(20)
var/mob/living/simple_animal/slime/Slime = new(get_turf(owner), "grey")
@@ -153,7 +153,7 @@
H.confused += 15
H.adjustBrainLoss(10, 160)
if(3)
H.hallucination += 80
H.hallucination += 60
/obj/item/organ/heart/gland/pop
cooldown_low = 900
@@ -272,10 +272,10 @@
/obj/item/organ/heart/gland/electric/Insert(mob/living/carbon/M, special = 0)
..()
owner.add_trait(TRAIT_SHOCKIMMUNE, "abductor_gland")
owner.add_trait(TRAIT_SHOCKIMMUNE, ORGAN_TRAIT)
/obj/item/organ/heart/gland/electric/Remove(mob/living/carbon/M, special = 0)
owner.remove_trait(TRAIT_SHOCKIMMUNE, "abductor_gland")
owner.remove_trait(TRAIT_SHOCKIMMUNE, ORGAN_TRAIT)
..()
/obj/item/organ/heart/gland/electric/activate()
@@ -353,7 +353,10 @@
if(GLOB.changeling_team_objective_type)
var/datum/objective/changeling_team_objective/team_objective = new GLOB.changeling_team_objective_type
team_objective.owner = owner
objectives += team_objective
if(team_objective.prepare())//Setting up succeeded
objectives += team_objective
else
qdel(team_objective)
return
/datum/antagonist/changeling/proc/forge_objectives()
@@ -72,10 +72,10 @@
//used in /mob/Stat()
/obj/effect/proc_holder/changeling/proc/can_be_used_by(mob/user)
if(!user || QDELETED(user))
return 0
if(QDELETED(user))
return FALSE
if(!ishuman(user) && !ismonkey(user))
return 0
return FALSE
if(req_human && !ishuman(user))
return 0
return 1
return FALSE
return TRUE
@@ -29,8 +29,12 @@
return TRUE
/obj/effect/proc_holder/changeling/revive/can_be_used_by(mob/living/user)
. = ..()
if(!.)
return
if(user.has_trait(CHANGELING_DRAIN) || ((user.stat != DEAD) && !(user.has_trait(TRAIT_FAKEDEATH))))
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.purchasedpowers -= src
return 0
. = ..()
return FALSE
@@ -214,19 +214,19 @@
/obj/effect/proc_holder/changeling/sting/LSD
name = "Hallucination Sting"
desc = "Causes terror in the target."
helptext = "We evolve the ability to sting a target with a powerful hallucinogenic chemical. The target does not notice they have been stung, and the effect occurs after 30 to 60 seconds."
helptext = "We evolve the ability to sting a target with a powerful hallucinogenic chemical. The target does not notice they have been stung, and the effect begins after a few seconds."
sting_icon = "sting_lsd"
chemical_cost = 10
dna_cost = 1
/obj/effect/proc_holder/changeling/sting/LSD/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "LSD sting")
addtimer(CALLBACK(src, .proc/hallucination_time, target), rand(300,600))
addtimer(CALLBACK(src, .proc/hallucination_time, target), rand(100,200))
return TRUE
/obj/effect/proc_holder/changeling/sting/LSD/proc/hallucination_time(mob/living/carbon/target)
if(target)
target.hallucination = max(400, target.hallucination)
target.hallucination = max(90, target.hallucination)
/obj/effect/proc_holder/changeling/sting/cryo
name = "Cryogenic Sting"
@@ -151,6 +151,7 @@
desc = "You get the feeling that you shouldn't be standing here."
clockwork_desc = "A sigil that will soon erupt and smite any unenlightened nearby."
icon = 'icons/effects/96x96.dmi'
icon_state = ""
pixel_x = -32
pixel_y = -32
layer = BELOW_MOB_LAYER
@@ -159,7 +159,7 @@
SSticker.mode.servants_of_ratvar -= owner
SSticker.mode.update_servant_icons_removed(owner)
if(!silent)
owner.current.visible_message("<span class='deconversion_message'>[owner] seems to have remembered their true allegiance!</span>", null, null, null, owner.current)
owner.current.visible_message("<span class='deconversion_message'>[owner.current] seems to have remembered [owner.current.p_their()] true allegiance!</span>", null, null, null, owner.current)
to_chat(owner, "<span class='userdanger'>A cold, cold darkness flows through your mind, extinguishing the Justiciar's light and all of your memories as his servant.</span>")
owner.current.log_message("<font color=#BE8700>Has renounced the cult of Ratvar!</font>", INDIVIDUAL_ATTACK_LOG)
owner.special_role = null
+1 -1
View File
@@ -260,7 +260,7 @@
if(!ishuman(target) || iscultist(target))
return
var/mob/living/carbon/human/H = target
H.hallucination = max(H.hallucination, 240)
H.hallucination = max(H.hallucination, 120)
SEND_SOUND(ranged_ability_user, sound('sound/effects/ghost.ogg',0,1,50))
var/image/C = image('icons/effects/cult_effects.dmi',H,"bloodsparkles", ABOVE_MOB_LAYER)
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/cult, "cult_apoc", C, FALSE)
+1 -1
View File
@@ -128,7 +128,7 @@
SSticker.mode.cult -= owner
SSticker.mode.update_cult_icons_removed(owner)
if(!silent)
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_they()] just reverted to their old faith!</span>", null, null, null, owner.current)
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just reverted to [owner.current.p_their()] old faith!</span>", null, null, null, owner.current)
to_chat(owner.current, "<span class='userdanger'>An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant.</span>")
owner.current.log_message("<font color=#960000>Has renounced the cult of Nar'Sie!</font>", INDIVIDUAL_ATTACK_LOG)
if(cult_team.blood_target && cult_team.blood_target_image && owner.current.client)
+4 -10
View File
@@ -33,10 +33,8 @@
desc = "A sword humming with unholy energy. It glows with a dim red light."
icon_state = "cultblade"
item_state = "cultblade"
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
inhand_y_dimension = 64
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
flags_1 = CONDUCT_1
sharpness = IS_SHARP
w_class = WEIGHT_CLASS_BULKY
@@ -73,8 +71,6 @@
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='cultlarge'>\"I wouldn't advise that.\"</span>")
to_chat(user, "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>")
user.Dizzy(120)
else
to_chat(user, "<span class='cultlarge'>\"One of Ratvar's toys is trying to play with things [user.p_they()] shouldn't. Cute.\"</span>")
to_chat(user, "<span class='userdanger'>A horrible force yanks at your arm!</span>")
@@ -138,10 +134,7 @@
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='cultlarge'>\"I wouldn't advise that.\"</span>")
to_chat(user, "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>")
user.Dizzy(80)
user.dropItemToGround(src, TRUE)
user.Knockdown(30)
force = 5
return
else
to_chat(user, "<span class='cultlarge'>\"One of Ratvar's toys is trying to play with things [user.p_they()] shouldn't. Cute.\"</span>")
@@ -151,6 +144,7 @@
user.dropItemToGround(src, TRUE)
user.Knockdown(50)
return
force = initial(force)
jaunt.Grant(user, src)
linked_action.Grant(user, src)
user.update_icons()
+2 -1
View File
@@ -144,7 +144,8 @@ This file contains the cult dagger and rune list code
if(locate(/obj/effect/rune) in T)
to_chat(user, "<span class='cult'>There is already a rune here.</span>")
return FALSE
if(!is_station_level(T.z) && !is_mining_level(T.z))
var/area/A = get_area(T)
if((!is_station_level(T.z) && !is_mining_level(T.z)) || (A && !A.blob_allowed))
to_chat(user, "<span class='warning'>The veil is not weak enough here.</span>")
return FALSE
return TRUE
+5
View File
@@ -708,6 +708,11 @@ structure_check() searches for nearby cultist structures required for the invoca
fail_invoke()
log_game("Summon Cultist rune failed - target died")
return
if(cultist_to_summon.pulledby || cultist_to_summon.buckled)
to_chat(user, "<span class='cult italic'>[cultist_to_summon] is being held in place!</span>")
fail_invoke()
log_game("Summon Cultist rune failed - target restrained")
return
if(!iscultist(cultist_to_summon))
to_chat(user, "<span class='cult italic'>[cultist_to_summon] is not a follower of the Geometer!</span>")
fail_invoke()
+1 -1
View File
@@ -190,7 +190,7 @@
return ..()
/mob/living/simple_animal/hostile/morph/AttackingTarget()
if(!melee_damage_disguised)
if(morphed && !melee_damage_disguised)
to_chat(src, "<span class='warning'>You can not attack while disguised!</span>")
return
if(isliving(target)) //Eat Corpses to regen health
@@ -501,16 +501,37 @@ This is here to make the tiles around the station mininuke change when it's arme
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/fake = FALSE
var/turf/lastlocation
var/last_disk_move
/obj/item/disk/nuclear/Initialize()
. = ..()
if(!fake)
GLOB.poi_list |= src
last_disk_move = world.time
START_PROCESSING(SSobj, src)
/obj/item/disk/nuclear/ComponentInitialize()
. = ..()
AddComponent(/datum/component/stationloving, !fake)
/obj/item/disk/nuclear/process()
if(fake)
STOP_PROCESSING(SSobj, src)
CRASH("A fake nuke disk tried to call process(). Who the fuck and how the fuck")
var/turf/newturf = get_turf(src)
if(newturf && lastlocation == newturf)
if(last_disk_move < world.time - 5000 && prob((world.time - 5000 - last_disk_move)*0.00001))
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop))
loneop.weight += 1
else
lastlocation = newturf
last_disk_move = world.time
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop) && prob(loneop.weight))
loneop.weight = max(loneop.weight - 1, 0)
/obj/item/disk/nuclear/examine(mob/user)
. = ..()
if(!fake)
@@ -90,7 +90,7 @@
to_chat(src, "<b>You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable.</b>")
to_chat(src, "<b>To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities.</b>")
to_chat(src, "<b><i>You do not remember anything of your past lives, nor will you remember anything about this one after your death.</i></b>")
to_chat(src, "<b>Be sure to read the wiki page at https://tgstation13.org/wiki/Revenant to learn more.</b>")
to_chat(src, "<b>Be sure to read <a href=\"https://tgstation13.org/wiki/Revenant\">the wiki page</a> to learn more.</b>")
if(!generated_objectives_and_spells)
generated_objectives_and_spells = TRUE
mind.assigned_role = ROLE_REVENANT
@@ -201,12 +201,13 @@
/datum/antagonist/rev/farewell()
if(ishuman(owner.current))
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like they just remembered their real allegiance!</span>", null, null, null, owner.current)
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!</span>", null, null, null, owner.current)
to_chat(owner, "<span class='userdanger'>You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...</span>")
else if(issilicon(owner.current))
owner.current.visible_message("<span class='deconversion_message'>The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.</span>", null, null, null, owner.current)
to_chat(owner, "<span class='userdanger'>The frame's firmware detects and deletes your neural reprogramming! You remember nothing but the name of the one who flashed you.</span>")
//blunt trauma deconversions call this through species.dm spec_attacked_by()
/datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter)
log_attack("[owner.current] (Key: [key_name(owner.current)]) has been deconverted from the revolution by [deconverter] (Key: [key_name(deconverter)])!")
if(borged)
@@ -66,7 +66,7 @@
name = "pile of viscera"
desc = "A repulsive pile of guts and gore."
gender = NEUTER
random_icon_states = list("innards")
icon_state = "innards"
/mob/living/simple_animal/slaughter/phasein()
. = ..()
+4 -4
View File
@@ -493,10 +493,10 @@
D.pixel_z = target.pixel_z
if(do_mob(src, target, 100))
to_chat(src, "<span class='info'>Dismantling complete.</span>")
var/obj/item/stack/sheet/metal/M = new /obj/item/stack/sheet/metal(target.loc)
M.amount = 5
var/atom/Tsec = target.drop_location()
new /obj/item/stack/sheet/metal(Tsec, 5)
for(var/obj/item/I in target.component_parts)
I.forceMove(M.drop_location())
I.forceMove(Tsec)
var/obj/effect/temp_visual/swarmer/disintegration/N = new /obj/effect/temp_visual/swarmer/disintegration(get_turf(target))
N.pixel_x = target.pixel_x
N.pixel_y = target.pixel_y
@@ -505,7 +505,7 @@
if(istype(target, /obj/machinery/computer))
var/obj/machinery/computer/C = target
if(C.circuit)
C.circuit.forceMove(M.drop_location())
C.circuit.forceMove(Tsec)
qdel(target)
@@ -33,7 +33,6 @@
..()
if(!iscultist(user) && !iswizard(user) && !usability)
to_chat(user, "<span class='danger'>An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly.</span>")
user.Dizzy(120)
/obj/item/soulstone/examine(mob/user)
..()
+23 -18
View File
@@ -3,6 +3,7 @@
#define WIRE_PULSE_SPECIAL (1<<2)
#define WIRE_RADIO_RECEIVE (1<<3)
#define WIRE_RADIO_PULSE (1<<4)
#define ASSEMBLY_BEEP_VOLUME 5
/obj/item/assembly
name = "assembly"
@@ -16,6 +17,8 @@
throw_speed = 3
throw_range = 7
var/is_position_sensitive = FALSE //set to true if the device has different icons for each position.
//This will prevent things such as visible lasers from facing the incorrect direction when transformed by assembly_holder's update_icon()
var/secured = TRUE
var/list/attached_overlays = null
var/obj/item/assembly_holder/holder = null
@@ -30,14 +33,18 @@
/obj/item/assembly/proc/on_attach()
/obj/item/assembly/proc/on_detach()
/obj/item/assembly/proc/on_detach() //call this when detaching it from a device. handles any special functions that need to be updated ex post facto
if(!holder)
return FALSE
forceMove(holder.drop_location())
holder = null
return TRUE
/obj/item/assembly/proc/holder_movement() //Called when the holder is moved
return
/obj/item/assembly/proc/describe() // Called by grenades to describe the state of the trigger (time left, etc)
return "The trigger assembly looks broken!"
if(!holder)
return FALSE
setDir(holder.dir)
return TRUE
/obj/item/assembly/proc/is_secured(mob/user)
if(!secured)
@@ -47,7 +54,7 @@
//Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs
/obj/item/assembly/proc/pulsed(radio = 0)
/obj/item/assembly/proc/pulsed(radio = FALSE)
if(wire_type & WIRE_RECEIVE)
INVOKE_ASYNC(src, .proc/activate)
if(radio && (wire_type & WIRE_RADIO_RECEIVE))
@@ -56,7 +63,7 @@
//Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
/obj/item/assembly/proc/pulse(radio = 0)
/obj/item/assembly/proc/pulse(radio = FALSE)
if(connected && wire_type)
connected.pulse_assembly(src)
return TRUE
@@ -91,21 +98,19 @@
else
to_chat(user, "<span class='warning'>Both devices must be in attachable mode to be attached together.</span>")
return
if(istype(W, /obj/item/screwdriver))
if(toggle_secure())
to_chat(user, "<span class='notice'>\The [src] is ready!</span>")
else
to_chat(user, "<span class='notice'>\The [src] can now be attached!</span>")
return
..()
/obj/item/assembly/screwdriver_act(mob/living/user, obj/item/I)
if(toggle_secure())
to_chat(user, "<span class='notice'>\The [src] is ready!</span>")
else
to_chat(user, "<span class='notice'>\The [src] can now be attached!</span>")
add_fingerprint(user)
return TRUE
/obj/item/assembly/examine(mob/user)
..()
if(secured)
to_chat(user, "\The [src] is secured and ready to be used.")
else
to_chat(user, "\The [src] can be attached to other things.")
to_chat(user, "<span class='notice'>\The [src] [secured? "is secured and ready to be used!" : "can be attached to other things."]</span>")
/obj/item/assembly/attack_self(mob/user)
+51 -16
View File
@@ -13,11 +13,14 @@
var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device
var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank
/obj/item/onetankbomb/IsSpecialAssembly()
return TRUE
/obj/item/onetankbomb/examine(mob/user)
bombtank.examine(user)
/obj/item/onetankbomb/update_icon()
cut_overlays()
if(bombtank)
icon = bombtank.icon
icon_state = bombtank.icon_state
@@ -30,29 +33,37 @@
if(istype(W, /obj/item/analyzer))
bombtank.attackby(W, user)
return
if(istype(W, /obj/item/wrench) && !status) //This is basically bomb assembly code inverted. apparently it works.
to_chat(user, "<span class='notice'>You disassemble [src].</span>")
add_fingerprint(user)
..()
/obj/item/onetankbomb/wrench_act(mob/living/user, obj/item/I)
to_chat(user, "<span class='notice'>You disassemble [src]!</span>")
if(bombassembly)
bombassembly.forceMove(drop_location())
bombassembly.master = null
bombassembly = null
if(bombtank)
bombtank.forceMove(drop_location())
bombtank.master = null
bombtank = null
qdel(src)
return TRUE
qdel(src)
/obj/item/onetankbomb/welder_act(mob/living/user, obj/item/I)
. = FALSE
if(status)
to_chat(user, "<span class='notice'>[bombtank] already has a pressure hole!</span>")
return
var/obj/item/weldingtool/WT = W
if((istype(WT) && WT.welding))
if(!status)
status = TRUE
GLOB.bombers += "[key_name(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]"
message_admins("[key_name_admin(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]")
to_chat(user, "<span class='notice'>A pressure hole has been bored to [bombtank] valve. \The [bombtank] can now be ignited.</span>")
add_fingerprint(user)
..()
if(!I.tool_start_check(user, amount=0))
return
if(I.use_tool(src, user, 0, volume=40))
status = TRUE
GLOB.bombers += "[key_name(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]"
message_admins("[key_name_admin(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]")
to_chat(user, "<span class='notice'>A pressure hole has been bored to [bombtank] valve. \The [bombtank] can now be ignited.</span>")
add_fingerprint(user)
return TRUE
/obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly
bombassembly.attack_self(user, TRUE)
@@ -60,16 +71,20 @@
return
/obj/item/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here.
visible_message("[icon2html(src, viewers(src))] *beep* *beep*", "*beep* *beep*")
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*")
playsound(src, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
sleep(10)
if(!src)
if(QDELETED(src))
return
if(status)
bombtank.ignite() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file
else
bombtank.release()
//Assembly / attached device memes
/obj/item/onetankbomb/Crossed(atom/movable/AM as mob|obj) //for mousetraps
. = ..()
if(bombassembly)
bombassembly.Crossed(AM)
@@ -77,6 +92,26 @@
if(bombassembly)
bombassembly.on_found(finder)
/obj/item/onetankbomb/attack_hand() //also for mousetraps
. = ..()
if(.)
return
if(bombassembly)
bombassembly.attack_hand()
/obj/item/onetankbomb/Move()
. = ..()
if(bombassembly)
bombassembly.setDir(dir)
bombassembly.Move()
/obj/item/onetankbomb/dropped()
. = ..()
if(bombassembly)
bombassembly.dropped()
// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ----------
+14 -21
View File
@@ -2,10 +2,10 @@
name = "blast door controller"
desc = "A small electronic device able to control a blast door remotely."
icon_state = "control"
attachable = 1
attachable = TRUE
var/id = null
var/can_change_id = 0
var/cooldown = 0//Door cooldowns
var/cooldown = FALSE //Door cooldowns
/obj/item/assembly/control/examine(mob/user)
..()
@@ -13,15 +13,14 @@
to_chat(user, "<span class='notice'>Its channel ID is '[id]'.</span>")
/obj/item/assembly/control/activate()
cooldown = 1
cooldown = TRUE
var/openclose
for(var/obj/machinery/door/poddoor/M in GLOB.machines)
if(M.id == src.id)
if(openclose == null)
openclose = M.density
INVOKE_ASYNC(M, openclose ? /obj/machinery/door/poddoor.proc/open : /obj/machinery/door/poddoor.proc/close)
sleep(10)
cooldown = 0
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 10)
/obj/item/assembly/control/airlock
@@ -38,7 +37,7 @@
*/
/obj/item/assembly/control/airlock/activate()
cooldown = 1
cooldown = TRUE
var/doors_need_closing = FALSE
var/list/obj/machinery/door/airlock/open_or_close = list()
for(var/obj/machinery/door/airlock/D in GLOB.airlocks)
@@ -66,8 +65,7 @@
for(var/D in open_or_close)
INVOKE_ASYNC(D, doors_need_closing ? /obj/machinery/door/airlock.proc/close : /obj/machinery/door/airlock.proc/open)
sleep(10)
cooldown = 0
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 10)
/obj/item/assembly/control/massdriver
@@ -75,7 +73,7 @@
desc = "A small electronic device able to control a mass driver."
/obj/item/assembly/control/massdriver/activate()
cooldown = 1
cooldown = TRUE
for(var/obj/machinery/door/poddoor/M in GLOB.machines)
if (M.id == src.id)
INVOKE_ASYNC(M, /obj/machinery/door/poddoor.proc/open)
@@ -92,8 +90,7 @@
if (M.id == src.id)
INVOKE_ASYNC(M, /obj/machinery/door/poddoor.proc/close)
sleep(10)
cooldown = 0
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 10)
/obj/item/assembly/control/igniter
@@ -101,7 +98,7 @@
desc = "A remote controller for a mounted igniter."
/obj/item/assembly/control/igniter/activate()
cooldown = 1
cooldown = TRUE
for(var/obj/machinery/sparker/M in GLOB.machines)
if (M.id == src.id)
INVOKE_ASYNC(M, /obj/machinery/sparker.proc/ignite)
@@ -112,22 +109,19 @@
M.on = !M.on
M.icon_state = "igniter[M.on]"
sleep(30)
cooldown = 0
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 30)
/obj/item/assembly/control/flasher
name = "flasher controller"
desc = "A remote controller for a mounted flasher."
/obj/item/assembly/control/flasher/activate()
cooldown = 1
cooldown = TRUE
for(var/obj/machinery/flasher/M in GLOB.machines)
if(M.id == src.id)
INVOKE_ASYNC(M, /obj/machinery/flasher.proc/flash)
sleep(50)
cooldown = 0
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 50)
/obj/item/assembly/control/crematorium
@@ -135,10 +129,9 @@
desc = "An evil-looking remote controller for a crematorium."
/obj/item/assembly/control/crematorium/activate()
cooldown = 1
cooldown = TRUE
for (var/obj/structure/bodycontainer/crematorium/C in GLOB.crematoriums)
if (C.id == id)
C.cremate(usr)
sleep(50)
cooldown = 0
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 50)
+5 -3
View File
@@ -93,7 +93,7 @@
if(crit_fail || (world.time < last_trigger + cooldown))
return FALSE
last_trigger = world.time
playsound(src, 'sound/weapons/flash.ogg', 100, 1)
playsound(src, 'sound/weapons/flash.ogg', 100, TRUE)
times_used++
flash_recharge()
update_icon(TRUE)
@@ -195,6 +195,8 @@
/obj/item/assembly/flash/cyborg/attackby(obj/item/W, mob/user, params)
return
/obj/item/assembly/flash/cyborg/screwdriver_act(mob/living/user, obj/item/I)
return
/obj/item/assembly/flash/memorizer
name = "memorizer"
@@ -226,7 +228,7 @@
return FALSE
overheat = TRUE
addtimer(CALLBACK(src, .proc/cooldown), flashcd)
playsound(src, 'sound/weapons/flash.ogg', 100, 1)
playsound(src, 'sound/weapons/flash.ogg', 100, TRUE)
update_icon(1)
return TRUE
@@ -272,7 +274,7 @@
return
crit_fail = FALSE
times_used = 0
playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE)
update_icon()
flash.crit_fail = TRUE
flash.update_icon()
+18 -19
View File
@@ -3,10 +3,10 @@
desc = "Used for scanning and monitoring health."
icon_state = "health"
materials = list(MAT_METAL=800, MAT_GLASS=200)
attachable = 1
secured = 0
attachable = TRUE
secured = FALSE
var/scanning = 0
var/scanning = FALSE
var/health_scan
var/alarm_health = 0
@@ -16,31 +16,28 @@
/obj/item/assembly/health/activate()
if(!..())
return 0//Cooldown check
return FALSE//Cooldown check
toggle_scan()
return 0
return TRUE
/obj/item/assembly/health/toggle_secure()
secured = !secured
if(secured && scanning)
START_PROCESSING(SSobj, src)
else
scanning = 0
scanning = FALSE
STOP_PROCESSING(SSobj, src)
update_icon()
return secured
/obj/item/assembly/health/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/multitool))
if(alarm_health == 0)
alarm_health = -90
user.show_message("You toggle [src] to \"detect death\" mode.")
else
alarm_health = 0
user.show_message("You toggle [src] to \"detect critical state\" mode.")
return
/obj/item/assembly/health/multitool_act(mob/living/user, obj/item/I)
if(alarm_health == 0)
alarm_health = -90
to_chat(user, "<span class='notice'>You toggle [src] to \"detect death\" mode.</span>")
else
return ..()
alarm_health = 0
to_chat(user, "<span class='notice'>You toggle [src] to \"detect critical state\" mode.</span>")
return TRUE
/obj/item/assembly/health/process()
if(!scanning || !secured)
@@ -58,7 +55,8 @@
health_scan = M.health
if(health_scan <= alarm_health)
pulse()
audible_message("[icon2html(src, hearers(src))] *beep* *beep*", "*beep* *beep*")
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*")
playsound(src, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
toggle_scan()
return
return
@@ -77,8 +75,9 @@
. = ..()
if(!secured)
user.show_message("<span class='warning'>The [name] is unsecured!</span>")
return 0
var/dat = "<TT><B>Health Sensor</B> <A href='?src=[REF(src)];scanning=1'>[scanning?"On":"Off"]</A>"
return FALSE
var/dat = "<TT><B>Health Sensor</B></TT>"
dat += "<BR><A href='?src=[REF(src)];scanning=1'>[scanning?"On":"Off"]</A>"
if(scanning && health_scan)
dat += "<BR>Health: [health_scan]"
user << browse(dat, "window=hscan")
+16 -16
View File
@@ -1,16 +1,16 @@
// See _DEFINES/is_helpers.dm for type helpers
/*
Name: IsSpecialAssembly
Desc: If true is an object that can be attached to an assembly holder but is a special thing like a plasma can or door
*/
/obj/proc/IsSpecialAssembly()
return 0
/*
Name: IsAssemblyHolder
Desc: If true is an object that can hold an assemblyholder object
*/
/obj/proc/IsAssemblyHolder()
return 0
// See _DEFINES/is_helpers.dm for type helpers
/*
Name: IsSpecialAssembly
Desc: If true is an object that can be attached to an assembly holder but is a special thing like a plasma can or door
*/
/obj/proc/IsSpecialAssembly()
return FALSE
/*
Name: IsAssemblyHolder
Desc: If true is an object that can hold an assemblyholder object
*/
/obj/proc/IsAssemblyHolder()
return FALSE
+39 -28
View File
@@ -15,7 +15,7 @@
var/obj/item/assembly/a_right = null
/obj/item/assembly_holder/IsAssemblyHolder()
return 1
return TRUE
/obj/item/assembly_holder/proc/assemble(obj/item/assembly/A, obj/item/assembly/A2, mob/user)
@@ -37,6 +37,7 @@
a_left = A
else
a_right = A
A.holder_movement()
/obj/item/assembly_holder/update_icon()
cut_overlays()
@@ -46,11 +47,16 @@
add_overlay("[O]_l")
if(a_right)
var/mutable_appearance/right = mutable_appearance(icon, "[a_right.icon_state]_left")
right.transform = matrix(-1, 0, 0, 0, 1, 0)
for(var/O in a_right.attached_overlays)
right.add_overlay("[O]_l")
add_overlay(right)
if(a_right.is_position_sensitive)
add_overlay("[a_right.icon_state]_right")
for(var/O in a_right.attached_overlays)
add_overlay("[O]_r")
else
var/mutable_appearance/right = mutable_appearance(icon, "[a_right.icon_state]_left")
right.transform = matrix(-1, 0, 0, 0, 1, 0)
for(var/O in a_right.attached_overlays)
right.add_overlay("[O]_l")
add_overlay(right)
if(master)
master.update_icon()
@@ -69,32 +75,37 @@
/obj/item/assembly_holder/Move()
. = ..()
if(a_left && a_right)
if(a_left)
a_left.holder_movement()
if(a_right)
a_right.holder_movement()
/obj/item/assembly_holder/dropped(mob/user)
. = ..()
if(a_left)
a_left.dropped()
if(a_right)
a_right.dropped()
/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess
. = ..()
if(.)
return
if(a_left && a_right)
a_left.holder_movement()
a_right.holder_movement()
if(a_left)
a_left.attack_hand()
if(a_right)
a_right.attack_hand()
/obj/item/assembly_holder/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/screwdriver))
var/turf/T = get_turf(src)
if(!T)
return 0
if(a_left)
a_left.holder = null
a_left.forceMove(T)
if(a_right)
a_right.holder = null
a_right.forceMove(T)
qdel(src)
else
..()
/obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/tool)
to_chat(user, "<span class='notice'>You disassemble [src]!</span>")
if(a_left)
a_left.on_detach()
a_left = null
if(a_right)
a_right.on_detach()
a_right = null
qdel(src)
return TRUE
/obj/item/assembly_holder/attack_self(mob/user)
src.add_fingerprint(user)
@@ -115,12 +126,12 @@
/obj/item/assembly_holder/proc/process_activation(obj/D, normal = 1, special = 1)
if(!D)
return 0
return FALSE
if((normal) && (a_right) && (a_left))
if(a_right != D)
a_right.pulsed(0)
a_right.pulsed(FALSE)
if(a_left != D)
a_left.pulsed(0)
a_left.pulsed(FALSE)
if(master)
master.receive_signal()
return 1
return TRUE
+2 -2
View File
@@ -23,12 +23,12 @@
/obj/item/assembly/igniter/activate()
if(!..())
return 0//Cooldown check
return FALSE//Cooldown check
var/turf/location = get_turf(loc)
if(location)
location.hotspot_expose(1000,1000)
sparks.start()
return 1
return TRUE
/obj/item/assembly/igniter/attack_self(mob/user)
activate()
+49 -16
View File
@@ -3,6 +3,7 @@
desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted."
icon_state = "infrared"
materials = list(MAT_METAL=1000, MAT_GLASS=500)
is_position_sensitive = TRUE
var/on = FALSE
var/visible = FALSE
@@ -10,6 +11,7 @@
var/list/obj/effect/beam/i_beam/beams
var/olddir = 0
var/datum/component/redirect/listener
var/hearing_range = 3
/obj/item/assembly/infra/Initialize()
. = ..()
@@ -30,23 +32,27 @@
refreshBeam()
/obj/item/assembly/infra/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_LIST(beams)
return ..()
. = ..()
/obj/item/assembly/infra/describe()
return "The infrared trigger is [on?"on":"off"]."
/obj/item/assembly/infra/examine(mob/user)
..()
to_chat(user, "<span class='notice'>The infrared trigger is [on?"on":"off"].</span>")
/obj/item/assembly/infra/activate()
if(!..())
return 0//Cooldown check
return FALSE//Cooldown check
on = !on
refreshBeam()
update_icon()
return 1
return TRUE
/obj/item/assembly/infra/toggle_secure()
secured = !secured
if(secured)
START_PROCESSING(SSobj, src)
refreshBeam()
else
QDEL_LIST(beams)
STOP_PROCESSING(SSobj, src)
@@ -59,13 +65,20 @@
if(on)
add_overlay("infrared_on")
attached_overlays += "infrared_on"
if(visible && secured)
add_overlay("infrared_visible")
attached_overlays += "infrared_visible"
if(holder)
holder.update_icon()
return
/obj/item/assembly/infra/dropped()
refreshBeam()
. = ..()
if(holder)
holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
else
refreshBeam()
/obj/item/assembly/infra/process()
if(!on || !secured)
@@ -74,7 +87,15 @@
/obj/item/assembly/infra/proc/refreshBeam()
QDEL_LIST(beams)
if(throwing || !on || !secured || !(isturf(loc) || holder && isturf(holder.loc)))
if(throwing || !on || !secured)
return
if(holder)
if(holder.master) //incase the sensor is part of an assembly that's contained in another item, such as a single tank bomb
if(!holder.master.IsSpecialAssembly() || !isturf(holder.master.loc))
return
else if(!isturf(holder.loc)) //else just check where the holder is
return
else if(!isturf(loc)) //or just where the fuck we are in general
return
var/turf/T = get_turf(src)
var/_dir = dir
@@ -82,6 +103,11 @@
if(_T)
for(var/i in 1 to maxlength)
var/obj/effect/beam/i_beam/I = new(T)
if(istype(holder, /obj/item/assembly_holder))
var/obj/item/assembly_holder/assembly_holder = holder
I.icon_state = "[initial(I.icon_state)]_[(assembly_holder.a_left == src) ? "l":"r"]" //Sync the offset of the beam with the position of the sensor.
else if(istype(holder, /obj/item/transfer_valve))
I.icon_state = "[initial(I.icon_state)]_ttv"
I.density = TRUE
if(!I.Move(_T))
qdel(I)
@@ -96,6 +122,12 @@
_T = get_step(_T, _dir)
CHECK_TICK
/obj/item/assembly/infra/on_detach()
. = ..()
if(!.)
return
refreshBeam()
/obj/item/assembly/infra/attack_hand()
. = ..()
refreshBeam()
@@ -116,19 +148,17 @@
setDir(olddir)
olddir = null
/obj/item/assembly/infra/holder_movement()
if(!holder)
return 0
refreshBeam()
return 1
/obj/item/assembly/infra/proc/trigger_beam(atom/movable/AM, turf/location)
refreshBeam()
switchListener(location)
if(!secured || !on || next_activate > world.time)
return FALSE
pulse(0)
audible_message("[icon2html(src, hearers(src))] *beep* *beep*", null, 3)
pulse(FALSE)
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
for(var/CHM in get_hearers_in_view(hearing_range, src))
if(ismob(CHM))
var/mob/LM = CHM
LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
next_activate = world.time + 30
/obj/item/assembly/infra/proc/switchListener(turf/newloc)
@@ -144,7 +174,9 @@
. = ..()
if(is_secured(user))
user.set_machine(src)
var/dat = "<TT><B>Infrared Laser</B>\n<B>Status</B>: [on ? "<A href='?src=[REF(src)];state=0'>On</A>" : "<A href='?src=[REF(src)];state=1'>Off</A>"]<BR>\n<B>Visibility</B>: [visible ? "<A href='?src=[REF(src)];visible=0'>Visible</A>" : "<A href='?src=[REF(src)];visible=1'>Invisible</A>"]<BR>\n</TT>"
var/dat = "<TT><B>Infrared Laser</B></TT>"
dat += "<BR><B>Status</B>: [on ? "<A href='?src=[REF(src)];state=0'>On</A>" : "<A href='?src=[REF(src)];state=1'>Off</A>"]"
dat += "<BR><B>Visibility</B>: [visible ? "<A href='?src=[REF(src)];visible=0'>Visible</A>" : "<A href='?src=[REF(src)];visible=1'>Invisible</A>"]"
dat += "<BR><BR><A href='?src=[REF(src)];refresh=1'>Refresh</A>"
dat += "<BR><BR><A href='?src=[REF(src)];close=1'>Close</A>"
user << browse(dat, "window=infra")
@@ -163,6 +195,7 @@
refreshBeam()
if(href_list["visible"])
visible = !(visible)
update_icon()
refreshBeam()
if(href_list["close"])
usr << browse(null, "window=infra")
+12 -18
View File
@@ -2,17 +2,15 @@
name = "mousetrap"
desc = "A handy little spring-loaded trap for catching pesty rodents."
icon_state = "mousetrap"
item_state = "mousetrap"
materials = list(MAT_METAL=100)
attachable = 1
var/armed = 0
attachable = TRUE
var/armed = FALSE
/obj/item/assembly/mousetrap/examine(mob/user)
..()
if(armed)
to_chat(user, "The mousetrap is armed!")
else
to_chat(user, "The mousetrap is not armed.")
to_chat(user, "<span class='notice'>The pressure plate is [armed?"primed":"safe"].</span>")
/obj/item/assembly/mousetrap/activate()
if(..())
@@ -22,13 +20,9 @@
var/mob/living/carbon/human/user = usr
if((user.has_trait(TRAIT_DUMB) || user.has_trait(TRAIT_CLUMSY)) && prob(50))
to_chat(user, "<span class='warning'>Your hand slips, setting off the trigger!</span>")
pulse(0)
pulse(FALSE)
update_icon()
if(usr)
playsound(usr.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3)
/obj/item/assembly/mousetrap/describe()
return "The pressure switch is [armed?"primed":"safe"]."
playsound(src, 'sound/weapons/handcuffs.ogg', 30, TRUE, -3)
/obj/item/assembly/mousetrap/update_icon()
if(armed)
@@ -45,10 +39,10 @@
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(H.has_trait(TRAIT_PIERCEIMMUNE))
playsound(src.loc, 'sound/effects/snap.ogg', 50, 1)
playsound(src, 'sound/effects/snap.ogg', 50, TRUE)
armed = FALSE
update_icon()
pulse(0)
pulse(FALSE)
return FALSE
switch(type)
if("feet")
@@ -66,10 +60,10 @@
var/mob/living/simple_animal/mouse/M = target
visible_message("<span class='boldannounce'>SPLAT!</span>")
M.splat()
playsound(src.loc, 'sound/effects/snap.ogg', 50, 1)
playsound(src, 'sound/effects/snap.ogg', 50, TRUE)
armed = FALSE
update_icon()
pulse(0)
pulse(FALSE)
/obj/item/assembly/mousetrap/attack_self(mob/living/carbon/human/user)
@@ -87,7 +81,7 @@
to_chat(user, "<span class='notice'>You disarm [src].</span>")
armed = !armed
update_icon()
playsound(user.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3)
playsound(src, 'sound/weapons/handcuffs.ogg', 30, TRUE, -3)
//ATTACK HAND IGNORING PARENT RETURN VALUE
@@ -145,4 +139,4 @@
/obj/item/assembly/mousetrap/armed
icon_state = "mousetraparmed"
armed = 1
armed = TRUE
+44 -31
View File
@@ -3,34 +3,36 @@
desc = "Used for scanning and alerting when someone enters a certain proximity."
icon_state = "prox"
materials = list(MAT_METAL=800, MAT_GLASS=200)
attachable = 1
attachable = TRUE
var/scanning = 0
var/timing = 0
var/scanning = FALSE
var/timing = FALSE
var/time = 10
var/sensitivity = 1
/obj/item/assembly/prox_sensor/proc/toggle_scan()
/obj/item/assembly/prox_sensor/proc/sense()
var/hearing_range = 3
/obj/item/assembly/prox_sensor/Initialize()
. = ..()
proximity_monitor = new(src, 0)
START_PROCESSING(SSobj, src)
/obj/item/assembly/prox_sensor/describe()
if(timing)
return "<span class='notice'>The proximity sensor is arming.</span>"
return "The proximity sensor is [scanning?"armed":"disarmed"]."
/obj/item/assembly/prox_sensor/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/item/assembly/prox_sensor/examine(mob/user)
..()
to_chat(user, "<span class='notice'>The proximity sensor is [timing ? "arming" : (scanning ? "armed" : "disarmed")].</span>")
/obj/item/assembly/prox_sensor/activate()
if(!..())
return 0//Cooldown check
timing = !timing
return FALSE//Cooldown check
if(!scanning)
timing = !timing
else
scanning = FALSE
update_icon()
return 1
return TRUE
/obj/item/assembly/prox_sensor/toggle_secure()
secured = !secured
@@ -38,8 +40,10 @@
if(scanning)
toggle_scan()
proximity_monitor.host = src
timing = 0
timing = FALSE
STOP_PROCESSING(SSobj, src)
else
START_PROCESSING(SSobj, src)
proximity_monitor.host = loc
update_icon()
return secured
@@ -51,25 +55,31 @@
sense()
/obj/item/assembly/prox_sensor/sense()
/obj/item/assembly/prox_sensor/proc/sense()
if(!scanning || !secured || next_activate > world.time)
return 0
pulse(0)
audible_message("[icon2html(src, hearers(src))] *beep* *beep*", null, 3)
return FALSE
pulse(FALSE)
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
for(var/CHM in get_hearers_in_view(hearing_range, src))
if(ismob(CHM))
var/mob/LM = CHM
LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
next_activate = world.time + 30
return TRUE
/obj/item/assembly/prox_sensor/process()
if(timing)
time--
if(time <= 0)
timing = 0
toggle_scan(1)
time = initial(time)
if(!timing)
return
time--
if(time <= 0)
timing = FALSE
toggle_scan(TRUE)
time = initial(time)
/obj/item/assembly/prox_sensor/toggle_scan(scan)
/obj/item/assembly/prox_sensor/proc/toggle_scan(scan)
if(!secured)
return 0
return FALSE
scanning = scan
proximity_monitor.SetRange(scanning ? sensitivity : 0)
update_icon()
@@ -98,8 +108,11 @@
if(is_secured(user))
var/second = time % 60
var/minute = (time - second) / 60
var/dat = "<TT><B>Proximity Sensor</B>\n[(timing ? "<A href='?src=[REF(src)];time=0'>Arming</A>" : "<A href='?src=[REF(src)];time=1'>Not Arming</A>")] [minute]:[second]\n<A href='?src=[REF(src)];tp=-30'>-</A> <A href='?src=[REF(src)];tp=-1'>-</A> <A href='?src=[REF(src)];tp=1'>+</A> <A href='?src=[REF(src)];tp=30'>+</A>\n</TT>"
dat += "<BR><A href='?src=[REF(src)];scanning=[scanning?"0'>Armed":"1'>Unarmed"]</A> (Movement sensor active when armed!)"
var/dat = "<TT><B>Proximity Sensor</B></TT>"
if(!scanning)
dat += "<BR>[(timing ? "<A href='?src=[REF(src)];time=0'>Arming</A>" : "<A href='?src=[REF(src)];time=1'>Not Arming</A>")] [minute]:[second]"
dat += "<BR><A href='?src=[REF(src)];tp=-30'>-</A> <A href='?src=[REF(src)];tp=-1'>-</A> <A href='?src=[REF(src)];tp=1'>+</A> <A href='?src=[REF(src)];tp=30'>+</A>"
dat += "<BR><A href='?src=[REF(src)];scanning=[scanning?"0'>Armed":"1'>Unarmed (Movement sensor active when armed!)"]</A>"
dat += "<BR>Detection range: <A href='?src=[REF(src)];sense=down'>-</A> [sensitivity] <A href='?src=[REF(src)];sense=up'>+</A>"
dat += "<BR><BR><A href='?src=[REF(src)];refresh=1'>Refresh</A>"
dat += "<BR><BR><A href='?src=[REF(src)];close=1'>Close</A>"
+8 -8
View File
@@ -13,18 +13,18 @@
qdel(part2)
return ..()
/obj/item/assembly/shock_kit/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/wrench))
/obj/item/assembly/shock_kit/wrench_act(mob/living/user, obj/item/I)
to_chat(user, "<span class='notice'>You disassemble [src].</span>")
if(part1)
part1.forceMove(drop_location())
part2.forceMove(drop_location())
part1.master = null
part2.master = null
part1 = null
if(part2)
part2.forceMove(drop_location())
part2.master = null
part2 = null
qdel(src)
return
add_fingerprint(user)
return
qdel(src)
return TRUE
/obj/item/assembly/shock_kit/attack_self(mob/user)
part1.attack_self(user)
+30 -20
View File
@@ -7,17 +7,18 @@
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
materials = list(MAT_METAL=400, MAT_GLASS=120)
wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE
attachable = 1
attachable = TRUE
var/code = DEFAULT_SIGNALER_CODE
var/frequency = FREQ_SIGNALER
var/delay = 0
var/datum/radio_frequency/radio_connection
var/suicider = null
var/hearing_range = 1
/obj/item/assembly/signaler/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user] eats \the [src]! If it is signaled, [user.p_they()] will die!</span>")
playsound(src, 'sound/items/eatfood.ogg', 50, 1)
playsound(src, 'sound/items/eatfood.ogg', 50, TRUE)
user.transferItemToLoc(src, user, TRUE)
suicider = user
return MANUAL_SUICIDE
@@ -27,15 +28,14 @@
user.adjustOxyLoss(200)//it sends an electrical pulse to their heart, killing them. or something.
user.death(0)
/obj/item/assembly/signaler/New()
..()
spawn(40)
set_frequency(frequency)
/obj/item/assembly/signaler/Initialize()
. = ..()
set_frequency(frequency)
/obj/item/assembly/signaler/Destroy()
SSradio.remove_object(src,frequency)
return ..()
. = ..()
/obj/item/assembly/signaler/activate()
if(!..())//cooldown processing
@@ -80,7 +80,7 @@ Code:
/obj/item/assembly/signaler/Topic(href, href_list)
..()
if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr))
if(!usr.canUseTopic(src, BE_CLOSE))
usr << browse(null, "window=radio")
onclose(usr, "radio")
return
@@ -111,7 +111,7 @@ Code:
var/obj/item/assembly/signaler/signaler2 = W
if(secured && signaler2.secured)
code = signaler2.code
frequency = signaler2.frequency
set_frequency(signaler2.frequency)
to_chat(user, "You transfer the frequency and code of \the [signaler2.name] to \the [name]")
..()
@@ -131,17 +131,22 @@ Code:
return
/obj/item/assembly/signaler/receive_signal(datum/signal/signal)
. = FALSE
if(!signal)
return 0
return
if(signal.data["code"] != code)
return 0
return
if(!(src.wires & WIRE_RADIO_RECEIVE))
return 0
return
if(suicider)
manual_suicide(suicider)
pulse(1)
audible_message("[icon2html(src, hearers(src))] *beep* *beep*", null, 1)
return
pulse(TRUE)
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
for(var/CHM in get_hearers_in_view(hearing_range, src))
if(ismob(CHM))
var/mob/LM = CHM
LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
return TRUE
/obj/item/assembly/signaler/proc/set_frequency(new_frequency)
@@ -161,10 +166,11 @@ Code:
/obj/item/assembly/signaler/reciever/activate()
toggle_safety()
return 1
return TRUE
/obj/item/assembly/signaler/reciever/describe()
return "The radio receiver is [on?"on":"off"]."
/obj/item/assembly/signaler/reciever/examine(mob/user)
..()
to_chat(user, "<span class='notice'>The radio receiver is [on?"on":"off"].</span>")
/obj/item/assembly/signaler/reciever/receive_signal(datum/signal/signal)
if(!on)
@@ -180,14 +186,16 @@ Code:
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
var/anomaly_type = /obj/effect/anomaly
/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal)
if(!signal)
return 0
return FALSE
if(signal.data["code"] != code)
return 0
return FALSE
for(var/obj/effect/anomaly/A in get_turf(src))
A.anomalyNeutralize()
return TRUE
/obj/item/assembly/signaler/anomaly/attack_self()
return
@@ -196,3 +204,5 @@ Code:
/obj/item/assembly/signaler/cyborg/attackby(obj/item/W, mob/user, params)
return
/obj/item/assembly/signaler/cyborg/screwdriver_act(mob/living/user, obj/item/I)
return
+33 -23
View File
@@ -3,12 +3,13 @@
desc = "Used to time things. Works well with contraptions which has to count down. Tick tock."
icon_state = "timer"
materials = list(MAT_METAL=500, MAT_GLASS=50)
attachable = 1
attachable = TRUE
var/timing = 0
var/timing = FALSE
var/time = 5
var/saved_time = 5
var/loop = 0
var/loop = FALSE
var/hearing_range = 3
/obj/item/assembly/timer/suicide_act(mob/living/user)
user.visible_message("<span class='suicide'>[user] looks at the timer and decides [user.p_their()] fate! It looks like [user.p_theyre()] going to commit suicide!</span>")
@@ -21,22 +22,24 @@
user.adjustOxyLoss(200)
user.death(0)
/obj/item/assembly/timer/New()
..()
/obj/item/assembly/timer/Initialize()
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/assembly/timer/describe()
if(timing)
return "The timer is counting down from [time]!"
return "The timer is set for [time] seconds."
/obj/item/assembly/timer/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/item/assembly/timer/examine(mob/user)
..()
to_chat(user, "<span class='notice'>The timer is [timing ? "counting down from [time]":"set for [time] seconds"].</span>")
/obj/item/assembly/timer/activate()
if(!..())
return 0//Cooldown check
return FALSE//Cooldown check
timing = !timing
update_icon()
return 1
return TRUE
/obj/item/assembly/timer/toggle_secure()
@@ -44,7 +47,7 @@
if(secured)
START_PROCESSING(SSobj, src)
else
timing = 0
timing = FALSE
STOP_PROCESSING(SSobj, src)
update_icon()
return secured
@@ -53,20 +56,25 @@
/obj/item/assembly/timer/proc/timer_end()
if(!secured || next_activate > world.time)
return FALSE
pulse(0)
audible_message("[icon2html(src, hearers(src))] *beep* *beep*", null, 3)
pulse(FALSE)
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
for(var/CHM in get_hearers_in_view(hearing_range, src))
if(ismob(CHM))
var/mob/LM = CHM
LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
if(loop)
timing = 1
timing = TRUE
update_icon()
/obj/item/assembly/timer/process()
if(timing)
time--
if(time <= 0)
timing = 0
timer_end()
time = saved_time
if(!timing)
return
time--
if(time <= 0)
timing = FALSE
timer_end()
time = saved_time
/obj/item/assembly/timer/update_icon()
@@ -84,7 +92,9 @@
if(is_secured(user))
var/second = time % 60
var/minute = (time - second) / 60
var/dat = "<TT><B>Timing Unit</B>\n[(timing ? "<A href='?src=[REF(src)];time=0'>Timing</A>" : "<A href='?src=[REF(src)];time=1'>Not Timing</A>")] [minute]:[second]\n<A href='?src=[REF(src)];tp=-30'>-</A> <A href='?src=[REF(src)];tp=-1'>-</A> <A href='?src=[REF(src)];tp=1'>+</A> <A href='?src=[REF(src)];tp=30'>+</A>\n</TT>"
var/dat = "<TT><B>Timing Unit</B></TT>"
dat += "<BR>[(timing ? "<A href='?src=[REF(src)];time=0'>Timing</A>" : "<A href='?src=[REF(src)];time=1'>Not Timing</A>")] [minute]:[second]"
dat += "<BR><A href='?src=[REF(src)];tp=-30'>-</A> <A href='?src=[REF(src)];tp=-1'>-</A> <A href='?src=[REF(src)];tp=1'>+</A> <A href='?src=[REF(src)];tp=30'>+</A>"
dat += "<BR><BR><A href='?src=[REF(src)];repeat=[(loop ? "0'>Stop repeating" : "1'>Set to repeat")]</A>"
dat += "<BR><BR><A href='?src=[REF(src)];refresh=1'>Refresh</A>"
dat += "<BR><BR><A href='?src=[REF(src)];close=1'>Close</A>"
@@ -95,7 +105,7 @@
/obj/item/assembly/timer/Topic(href, href_list)
..()
if(usr.incapacitated() || !in_range(loc, usr))
if(!usr.canUseTopic(src, BE_CLOSE))
usr << browse(null, "window=timer")
onclose(usr, "timer")
return
+43 -34
View File
@@ -1,14 +1,19 @@
#define INCLUSIVE_MODE 1
#define EXCLUSIVE_MODE 2
#define RECOGNIZER_MODE 3
#define VOICE_SENSOR_MODE 4
/obj/item/assembly/voice
name = "voice analyzer"
desc = "A small electronic device able to record a voice sample, and send a signal when that sample is repeated."
icon_state = "voice"
materials = list(MAT_METAL=500, MAT_GLASS=50)
flags_1 = HEAR_1
attachable = 1
attachable = TRUE
verb_say = "beeps"
verb_ask = "beeps"
verb_exclaim = "beeps"
var/listening = 0
var/listening = FALSE
var/recorded = "" //the activation message
var/mode = 1
var/static/list/modes = list("inclusive",
@@ -32,60 +37,64 @@
/obj/item/assembly/voice/proc/record_speech(atom/movable/speaker, raw_message, datum/language/message_language)
switch(mode)
if(1)
if(INCLUSIVE_MODE)
recorded = raw_message
listening = 0
listening = FALSE
say("Activation message is '[recorded]'.", message_language)
if(2)
if(EXCLUSIVE_MODE)
recorded = raw_message
listening = 0
listening = FALSE
say("Activation message is '[recorded]'.", message_language)
if(3)
if(RECOGNIZER_MODE)
recorded = speaker.GetVoice()
listening = 0
listening = FALSE
say("Your voice pattern is saved.", message_language)
if(4)
if(VOICE_SENSOR_MODE)
if(length(raw_message))
addtimer(CALLBACK(src, .proc/pulse, 0), 10)
/obj/item/assembly/voice/proc/check_activation(atom/movable/speaker, raw_message)
. = 0
. = FALSE
switch(mode)
if(1)
if(INCLUSIVE_MODE)
if(findtext(raw_message, recorded))
. = 1
if(2)
. = TRUE
if(EXCLUSIVE_MODE)
if(raw_message == recorded)
. = 1
if(3)
. = TRUE
if(RECOGNIZER_MODE)
if(speaker.GetVoice() == recorded)
. = 1
if(4)
. = TRUE
if(VOICE_SENSOR_MODE)
if(length(raw_message))
. = 1
. = TRUE
/obj/item/assembly/voice/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/multitool))
mode %= modes.len
mode++
to_chat(user, "You set [src] into a [modes[mode]] mode.")
listening = 0
recorded = ""
else
return ..()
/obj/item/assembly/voice/multitool_act(mob/living/user, obj/item/I)
mode %= modes.len
mode++
to_chat(user, "<span class='notice'>You set [src] into [modes[mode]] mode.</span>")
listening = FALSE
recorded = ""
return TRUE
/obj/item/assembly/voice/activate()
if(secured)
if(!holder)
listening = !listening
say("[listening ? "Now" : "No longer"] recording input.")
if(!secured || holder)
return FALSE
listening = !listening
say("[listening ? "Now" : "No longer"] recording input.")
return TRUE
/obj/item/assembly/voice/attack_self(mob/user)
if(!user)
return 0
return FALSE
activate()
return 1
return TRUE
/obj/item/assembly/voice/toggle_secure()
. = ..()
listening = 0
listening = FALSE
#undef INCLUSIVE_MODE
#undef EXCLUSIVE_MODE
#undef RECOGNIZER_MODE
#undef VOICE_SENSOR_MODE
@@ -4,11 +4,11 @@ What are the archived variables for?
This prevents race conditions that arise based on the order of tile processing.
*/
#define MINIMUM_HEAT_CAPACITY 0.0003
#define MINIMUM_MOLE_COUNT 0.01
#define QUANTIZE(variable) (round(variable,0.0000001))/*I feel the need to document what happens here. Basically this is used to catch most rounding errors, however it's previous value made it so that
once gases got hot enough, most procedures wouldnt occur due to the fact that the mole counts would get rounded away. Thus, we lowered it a few orders of magnititude */
GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm
GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/gas/nitrogen, /datum/gas/carbon_dioxide))) // These gasses cannot react amongst themselves
/proc/init_gaslist_cache()
. = list()
@@ -1,4 +1,5 @@
GLOBAL_LIST_INIT(hardcoded_gases, list(/datum/gas/oxygen, /datum/gas/nitrogen, /datum/gas/carbon_dioxide, /datum/gas/plasma)) //the main four gases, which were at one time hardcoded
GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/gas/nitrogen, /datum/gas/carbon_dioxide, /datum/gas/pluoxium, /datum/gas/stimulum, /datum/gas/nitryl))) //unable to react amongst themselves
/proc/meta_gas_list()
. = subtypesof(/datum/gas)
@@ -124,6 +125,7 @@ GLOBAL_LIST_INIT(hardcoded_gases, list(/datum/gas/oxygen, /datum/gas/nitrogen, /
specific_heat = 80
name = "Pluoxium"
fusion_power = 10
/obj/effect/overlay/gas
icon = 'icons/effects/tile_effects.dmi'
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
@@ -30,7 +30,6 @@
#define FUSION_PURITY_THRESHOLD 0.95
#define FUSION_HEAT_DROPOFF (20000+T0C)
#define NOBLIUM_FORMATION_ENERGY 2e9 //1 Mole of Noblium takes the planck energy to condense.
/datum/controller/subsystem/air/var/list/gas_reactions //this is our singleton of all reactions
/proc/init_gas_reactions()
var/list/reaction_types = list()
@@ -49,7 +48,7 @@
/datum/gas_reaction
//regarding the requirements lists: the minimum or maximum requirements must be non-zero.
//when in doubt, use MINIMUM_HEAT_CAPACITY.
//when in doubt, use MINIMUM_MOLE_COUNT.
var/list/min_requirements
var/list/max_requirements
var/exclude = FALSE //do it this way to allow for addition/removal of reactions midmatch in the future
@@ -61,6 +60,7 @@
init_reqs()
/datum/gas_reaction/proc/init_reqs()
/datum/gas_reaction/proc/react(datum/gas_mixture/air, atom/location)
return NO_REACTION
@@ -94,16 +94,20 @@
air.gases[/datum/gas/water_vapor][MOLES] -= MOLES_GAS_VISIBLE
. = REACTING
//fire: combustion of plasma and volatile fuel (treated as hydrocarbons). creates hotspots. exothermic
/datum/gas_reaction/fire
priority = -1 //fire should ALWAYS be last
name = "Hydrocarbon Combustion"
id = "fire"
//tritium combustion: combustion of oxygen and tritium (treated as hydrocarbons). creates hotspots. exothermic
/datum/gas_reaction/tritfire
priority = -1 //fire should ALWAYS be last, but tritium fires happen before plasma fires
name = "Tritium Combustion"
id = "tritfire"
/datum/gas_reaction/fire/init_reqs()
min_requirements = list("TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST) //doesn't include plasma reqs b/c of other, rarer, burning gases.
/datum/gas_reaction/tritfire/init_reqs()
min_requirements = list(
"TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST,
/datum/gas/tritium = MINIMUM_MOLE_COUNT,
/datum/gas/oxygen = MINIMUM_MOLE_COUNT
)
/datum/gas_reaction/fire/react(datum/gas_mixture/air, datum/holder)
/datum/gas_reaction/tritfire/react(datum/gas_mixture/air, datum/holder)
var/energy_released = 0
var/old_heat_capacity = air.heat_capacity()
var/list/cached_gases = air.gases //this speeds things up because accessing datum vars is slow
@@ -112,65 +116,99 @@
cached_results[id] = 0
var/turf/open/location = isturf(holder) ? holder : null
//General volatile gas burn
if(cached_gases[/datum/gas/tritium] && cached_gases[/datum/gas/tritium][MOLES])
var/burned_fuel
if(!cached_gases[/datum/gas/oxygen])
burned_fuel = 0
else if(cached_gases[/datum/gas/oxygen][MOLES] < cached_gases[/datum/gas/tritium][MOLES])
burned_fuel = cached_gases[/datum/gas/oxygen][MOLES]/TRITIUM_BURN_OXY_FACTOR
cached_gases[/datum/gas/tritium][MOLES] -= burned_fuel
else
burned_fuel = cached_gases[/datum/gas/tritium][MOLES]*TRITIUM_BURN_TRIT_FACTOR
cached_gases[/datum/gas/tritium][MOLES] -= cached_gases[/datum/gas/tritium][MOLES]/TRITIUM_BURN_TRIT_FACTOR
cached_gases[/datum/gas/oxygen][MOLES] -= cached_gases[/datum/gas/tritium][MOLES]
var/burned_fuel = 0
if(cached_gases[/datum/gas/oxygen][MOLES] < cached_gases[/datum/gas/tritium][MOLES])
burned_fuel = cached_gases[/datum/gas/oxygen][MOLES]/TRITIUM_BURN_OXY_FACTOR
cached_gases[/datum/gas/tritium][MOLES] -= burned_fuel
else
burned_fuel = cached_gases[/datum/gas/tritium][MOLES]*TRITIUM_BURN_TRIT_FACTOR
cached_gases[/datum/gas/tritium][MOLES] -= cached_gases[/datum/gas/tritium][MOLES]/TRITIUM_BURN_TRIT_FACTOR
cached_gases[/datum/gas/oxygen][MOLES] -= cached_gases[/datum/gas/tritium][MOLES]
if(burned_fuel)
energy_released += FIRE_HYDROGEN_ENERGY_RELEASED * burned_fuel
if(location && prob(10) && burned_fuel > TRITIUM_MINIMUM_RADIATION_ENERGY) //woah there let's not crash the server
radiation_pulse(location, energy_released/TRITIUM_BURN_RADIOACTIVITY_FACTOR)
if(burned_fuel)
energy_released += FIRE_HYDROGEN_ENERGY_RELEASED * burned_fuel
if(location && prob(10) && burned_fuel > TRITIUM_MINIMUM_RADIATION_ENERGY) //woah there let's not crash the server
radiation_pulse(location, energy_released/TRITIUM_BURN_RADIOACTIVITY_FACTOR)
ASSERT_GAS(/datum/gas/water_vapor, air) //oxygen+more-or-less hydrogen=H2O
cached_gases[/datum/gas/water_vapor][MOLES] += burned_fuel/TRITIUM_BURN_OXY_FACTOR
ASSERT_GAS(/datum/gas/water_vapor, air) //oxygen+more-or-less hydrogen=H2O
cached_gases[/datum/gas/water_vapor][MOLES] += burned_fuel/TRITIUM_BURN_OXY_FACTOR
cached_results[id] += burned_fuel
cached_results[id] += burned_fuel
if(energy_released > 0)
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = (temperature*old_heat_capacity + energy_released)/new_heat_capacity
//let the floor know a fire is happening
if(istype(location))
temperature = air.temperature
if(temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
location.hotspot_expose(temperature, CELL_VOLUME)
for(var/I in location)
var/atom/movable/item = I
item.temperature_expose(air, temperature, CELL_VOLUME)
location.temperature_expose(air, temperature, CELL_VOLUME)
return cached_results[id] ? REACTING : NO_REACTION
//plasma combustion: combustion of oxygen and plasma (treated as hydrocarbons). creates hotspots. exothermic
/datum/gas_reaction/plasmafire
priority = -2 //fire should ALWAYS be last, but plasma fires happen after tritium fires
name = "Plasma Combustion"
id = "plasmafire"
/datum/gas_reaction/plasmafire/init_reqs()
min_requirements = list(
"TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST,
/datum/gas/plasma = MINIMUM_MOLE_COUNT,
/datum/gas/oxygen = MINIMUM_MOLE_COUNT
)
/datum/gas_reaction/plasmafire/react(datum/gas_mixture/air, datum/holder)
var/energy_released = 0
var/old_heat_capacity = air.heat_capacity()
var/list/cached_gases = air.gases //this speeds things up because accessing datum vars is slow
var/temperature = air.temperature
var/list/cached_results = air.reaction_results
cached_results[id] = 0
var/turf/open/location = isturf(holder) ? holder : null
//Handle plasma burning
if(cached_gases[/datum/gas/plasma] && cached_gases[/datum/gas/plasma][MOLES] > MINIMUM_HEAT_CAPACITY)
var/plasma_burn_rate = 0
var/oxygen_burn_rate = 0
//more plasma released at higher temperatures
var/temperature_scale
var/super_saturation
if(temperature > PLASMA_UPPER_TEMPERATURE)
temperature_scale = 1
var/plasma_burn_rate = 0
var/oxygen_burn_rate = 0
//more plasma released at higher temperatures
var/temperature_scale = 0
//to make tritium
var/super_saturation = FALSE
if(temperature > PLASMA_UPPER_TEMPERATURE)
temperature_scale = 1
else
temperature_scale = (temperature-PLASMA_MINIMUM_BURN_TEMPERATURE)/(PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE)
if(temperature_scale > 0)
oxygen_burn_rate = OXYGEN_BURN_RATE_BASE - temperature_scale
if(cached_gases[/datum/gas/oxygen][MOLES] / cached_gases[/datum/gas/plasma][MOLES] > SUPER_SATURATION_THRESHOLD) //supersaturation. Form Tritium.
super_saturation = TRUE
if(cached_gases[/datum/gas/oxygen][MOLES] > cached_gases[/datum/gas/plasma][MOLES]*PLASMA_OXYGEN_FULLBURN)
plasma_burn_rate = (cached_gases[/datum/gas/plasma][MOLES]*temperature_scale)/PLASMA_BURN_RATE_DELTA
else
temperature_scale = (temperature-PLASMA_MINIMUM_BURN_TEMPERATURE)/(PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE)
if(temperature_scale > 0)
var/o2 = cached_gases[/datum/gas/oxygen] ? cached_gases[/datum/gas/oxygen][MOLES] : 0
oxygen_burn_rate = OXYGEN_BURN_RATE_BASE - temperature_scale
if(o2 / cached_gases[/datum/gas/plasma][MOLES] > SUPER_SATURATION_THRESHOLD) //supersaturation. Form Tritium.
super_saturation = TRUE
if(o2 > cached_gases[/datum/gas/plasma][MOLES]*PLASMA_OXYGEN_FULLBURN)
plasma_burn_rate = (cached_gases[/datum/gas/plasma][MOLES]*temperature_scale)/PLASMA_BURN_RATE_DELTA
plasma_burn_rate = (temperature_scale*(cached_gases[/datum/gas/oxygen][MOLES]/PLASMA_OXYGEN_FULLBURN))/PLASMA_BURN_RATE_DELTA
if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY)
plasma_burn_rate = min(plasma_burn_rate,cached_gases[/datum/gas/plasma][MOLES],cached_gases[/datum/gas/oxygen][MOLES]/oxygen_burn_rate) //Ensures matter is conserved properly
cached_gases[/datum/gas/plasma][MOLES] = QUANTIZE(cached_gases[/datum/gas/plasma][MOLES] - plasma_burn_rate)
cached_gases[/datum/gas/oxygen][MOLES] = QUANTIZE(cached_gases[/datum/gas/oxygen][MOLES] - (plasma_burn_rate * oxygen_burn_rate))
if (super_saturation)
ASSERT_GAS(/datum/gas/tritium,air)
cached_gases[/datum/gas/tritium][MOLES] += plasma_burn_rate
else
plasma_burn_rate = (temperature_scale*(o2/PLASMA_OXYGEN_FULLBURN))/PLASMA_BURN_RATE_DELTA
ASSERT_GAS(/datum/gas/carbon_dioxide,air)
cached_gases[/datum/gas/carbon_dioxide][MOLES] += plasma_burn_rate
if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY)
ASSERT_GAS(/datum/gas/carbon_dioxide, air) //don't need to assert o2, since if it isn't present we'll never reach this point anyway
plasma_burn_rate = min(plasma_burn_rate,cached_gases[/datum/gas/plasma][MOLES],cached_gases[/datum/gas/oxygen][MOLES]/oxygen_burn_rate) //Ensures matter is conserved properly
cached_gases[/datum/gas/plasma][MOLES] = QUANTIZE(cached_gases[/datum/gas/plasma][MOLES] - plasma_burn_rate)
cached_gases[/datum/gas/oxygen][MOLES] = QUANTIZE(cached_gases[/datum/gas/oxygen][MOLES] - (plasma_burn_rate * oxygen_burn_rate))
if (super_saturation)
ASSERT_GAS(/datum/gas/tritium,air)
cached_gases[/datum/gas/tritium][MOLES] += plasma_burn_rate
else
ASSERT_GAS(/datum/gas/carbon_dioxide,air)
cached_gases[/datum/gas/carbon_dioxide][MOLES] += plasma_burn_rate
energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate)
energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate)
cached_results[id] += (plasma_burn_rate)*(1+oxygen_burn_rate)
cached_results[id] += (plasma_burn_rate)*(1+oxygen_burn_rate)
if(energy_released > 0)
var/new_heat_capacity = air.heat_capacity()
@@ -191,12 +229,11 @@
//fusion: a terrible idea that was fun but broken. Now reworked to be less broken and more interesting. Again.
/datum/gas_reaction/fusion
exclude = FALSE
exclude = TRUE
priority = 2
name = "Plasmic Fusion"
id = "fusion"
/datum/gas_reaction/fusion/init_reqs()
min_requirements = list(
"ENER" = PLASMA_BINDING_ENERGY * 1000,
@@ -361,7 +398,7 @@
air.temperature = max(((air.temperature*old_heat_capacity + stim_energy_change)/new_heat_capacity),TCMB)
return REACTING
/datum/gas_reaction/nobliumformation //Hyper-Nobelium formation is extrememly endothermic, but requires high temperatures to start. Due to its high mass, hyper-nobelium uses large amounts of nitrogen and tritium. BZ can be used as a catalyst to make it less endothermic.
/datum/gas_reaction/nobliumformation //Hyper-Noblium formation is extrememly endothermic, but requires high temperatures to start. Due to its high mass, hyper-nobelium uses large amounts of nitrogen and tritium. BZ can be used as a catalyst to make it less endothermic.
priority = 6
name = "Hyper-Noblium condensation"
id = "nobformation"
@@ -380,8 +417,8 @@
var/energy_taken = nob_formed*(NOBLIUM_FORMATION_ENERGY/(max(cached_gases[/datum/gas/bz][MOLES],1)))
if ((cached_gases[/datum/gas/tritium][MOLES] - 10*nob_formed < 0) || (cached_gases[/datum/gas/nitrogen][MOLES] - 20*nob_formed < 0))
return NO_REACTION
cached_gases[/datum/gas/tritium][MOLES] = max(cached_gases[/datum/gas/tritium][MOLES]- 10*nob_formed,0)
cached_gases[/datum/gas/nitrogen][MOLES] = max(cached_gases[/datum/gas/nitrogen][MOLES]- 20*nob_formed,0)
cached_gases[/datum/gas/tritium][MOLES] -= 10*nob_formed
cached_gases[/datum/gas/nitrogen][MOLES] -= 20*nob_formed
cached_gases[/datum/gas/hypernoblium][MOLES]+= nob_formed
@@ -153,6 +153,9 @@
req_access = null
req_one_access = null
/obj/machinery/airalarm/syndicate //general syndicate access
req_access = list(ACCESS_SYNDICATE)
//all air alarms in area are connected via magic
/area
var/list/air_vent_names = list()
@@ -34,8 +34,10 @@ Iconnery
var/turf/T = loc
if(level == 2 || !T.intact)
showpipe = TRUE
plane = GAME_PLANE
else
showpipe = FALSE
plane = FLOOR_PLANE
if(!showpipe)
return //no need to update the pipes if they aren't showing
+1 -1
View File
@@ -101,7 +101,7 @@
export_types = list(/obj/machinery/iv_drip)
/datum/export/large/barrier
cost = 325
cost = 100
unit_name = "security barrier"
export_types = list(/obj/item/grenade/barrier, /obj/structure/barricade/security)
+3 -2
View File
@@ -1649,8 +1649,9 @@
. = ..()
if(prob(50))
var/mob/living/simple_animal/pet/dog/corgi/D = locate() in .
qdel(D)
new /mob/living/simple_animal/pet/dog/corgi/Lisa(.)
if(D.gender == FEMALE)
qdel(D)
new /mob/living/simple_animal/pet/dog/corgi/Lisa(.)
/datum/supply_pack/critter/cow
name = "Cow Crate"
+243 -62
View File
@@ -157,12 +157,14 @@ GLOBAL_LIST_EMPTY(asset_datums)
//get an assetdatum or make a new one
/proc/get_asset_datum(var/type)
if (!(type in GLOB.asset_datums))
return new type()
return GLOB.asset_datums[type]
return GLOB.asset_datums[type] || new type()
/datum/asset
var/_abstract = /datum/asset
/datum/asset/New()
GLOB.asset_datums[type] = src
register()
/datum/asset/proc/register()
return
@@ -170,20 +172,173 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/proc/send(client)
return
//If you don't need anything complicated.
/datum/asset/simple
_abstract = /datum/asset/simple
var/assets = list()
var/verify = FALSE
/datum/asset/simple/register()
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
/datum/asset/simple/send(client)
send_asset_list(client,assets,verify)
// For registering or sending multiple others at once
/datum/asset/group
_abstract = /datum/asset/group
var/list/children
/datum/asset/group/register()
for(var/type in children)
get_asset_datum(type)
/datum/asset/group/send(client/C)
for(var/type in children)
var/datum/asset/A = get_asset_datum(type)
A.send(C)
// spritesheet implementation - coalesces various icons into a single .png file
// and uses CSS to select icons out of that file - saves on transferring some
// 1400-odd individual PNG files
#define SPR_SIZE 1
#define SPR_IDX 2
#define SPRSZ_COUNT 1
#define SPRSZ_ICON 2
#define SPRSZ_STRIPPED 3
/datum/asset/spritesheet
_abstract = /datum/asset/spritesheet
var/name
var/list/sizes = list() // "32x32" -> list(10, icon/normal, icon/stripped)
var/list/sprites = list() // "foo_bar" -> list("32x32", 5)
var/verify = FALSE
/datum/asset/spritesheet/register()
if (!name)
CRASH("spritesheet [type] cannot register without a name")
ensure_stripped()
var/res_name = "spritesheet_[name].css"
var/fname = "data/spritesheets/[res_name]"
call("rust_g", "file_write")(generate_css(), fname)
register_asset(res_name, file(fname))
for(var/size_id in sizes)
var/size = sizes[size_id]
register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
/datum/asset/spritesheet/send(client/C)
if (!name)
return
var/all = list("spritesheet_[name].css")
for(var/size_id in sizes)
all += "[name]_[size_id].png"
send_asset_list(C, all, verify)
/datum/asset/spritesheet/proc/ensure_stripped(sizes_to_strip = sizes)
for(var/size_id in sizes_to_strip)
var/size = sizes[size_id]
if (size[SPRSZ_STRIPPED])
continue
// save flattened version
var/fname = "data/spritesheets/[name]_[size_id].png"
fcopy(size[SPRSZ_ICON], fname)
var/error = call("rust_g", "dmi_strip_metadata")(fname)
if(length(error))
stack_trace("Failed to strip [name]_[size_id].png: [error]")
size[SPRSZ_STRIPPED] = icon(fname)
/datum/asset/spritesheet/proc/generate_css()
var/list/out = list()
for (var/size_id in sizes)
var/size = sizes[size_id]
var/icon/tiny = size[SPRSZ_ICON]
out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[name]_[size_id].png') no-repeat;}"
for (var/sprite_id in sprites)
var/sprite = sprites[sprite_id]
var/size_id = sprite[SPR_SIZE]
var/idx = sprite[SPR_IDX]
var/size = sizes[size_id]
var/icon/tiny = size[SPRSZ_ICON]
var/icon/big = size[SPRSZ_STRIPPED]
var/per_line = big.Width() / tiny.Width()
var/x = (idx % per_line) * tiny.Width()
var/y = round(idx / per_line) * tiny.Height()
out += ".[name][size_id].[sprite_id]{background-position:-[x]px -[y]px;}"
return out.Join("\n")
/datum/asset/spritesheet/proc/Insert(sprite_name, icon/I, icon_state="", dir=SOUTH, frame=1, moving=FALSE)
I = icon(I, icon_state=icon_state, dir=dir, frame=frame, moving=moving)
if (!I || !length(icon_states(I))) // that direction or state doesn't exist
return
var/size_id = "[I.Width()]x[I.Height()]"
var/size = sizes[size_id]
if (sprites[sprite_name])
CRASH("duplicate sprite \"[sprite_name]\" in sheet [name] ([type])")
if (size)
var/position = size[SPRSZ_COUNT]++
var/icon/sheet = size[SPRSZ_ICON]
size[SPRSZ_STRIPPED] = null
sheet.Insert(I, icon_state=sprite_name)
sprites[sprite_name] = list(size_id, position)
else
sizes[size_id] = size = list(1, I, null)
sprites[sprite_name] = list(size_id, 0)
/datum/asset/spritesheet/proc/InsertAll(prefix, icon/I, list/directions)
if (length(prefix))
prefix = "[prefix]-"
if (!directions)
directions = list(SOUTH)
for (var/icon_state_name in icon_states(I))
for (var/direction in directions)
var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]-" : ""
Insert("[prefix][prefix2][icon_state_name]", I, icon_state=icon_state_name, dir=direction)
/datum/asset/spritesheet/proc/css_tag()
return {"<link rel="stylesheet" href="spritesheet_[name].css" />"}
/datum/asset/spritesheet/proc/icon_tag(sprite_name)
var/sprite = sprites[sprite_name]
if (!sprite)
return null
var/size_id = sprite[SPR_SIZE]
return {"<span class="[name][size_id] [sprite_name]"></span>"}
#undef SPR_SIZE
#undef SPR_IDX
#undef SPRSZ_COUNT
#undef SPRSZ_ICON
#undef SPRSZ_STRIPPED
/datum/asset/spritesheet/simple
_abstract = /datum/asset/spritesheet/simple
var/list/assets
/datum/asset/spritesheet/simple/register()
for (var/key in assets)
Insert(key, assets[key])
..()
//Generates assets based on iconstates of a single icon
/datum/asset/simple/icon_states
_abstract = /datum/asset/simple/icon_states
var/icon
var/list/directions = list(SOUTH)
var/frame = 1
@@ -209,6 +364,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
register_asset(asset_name, asset)
/datum/asset/simple/icon_states/multiple_icons
_abstract = /datum/asset/simple/icon_states/multiple_icons
var/list/icons
/datum/asset/simple/icon_states/multiple_icons/register()
@@ -260,50 +416,52 @@ GLOBAL_LIST_EMPTY(asset_datums)
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif'
)
/datum/asset/simple/pda
/datum/asset/spritesheet/simple/pda
name = "pda"
assets = list(
"pda_atmos.png" = 'icons/pda_icons/pda_atmos.png',
"pda_back.png" = 'icons/pda_icons/pda_back.png',
"pda_bell.png" = 'icons/pda_icons/pda_bell.png',
"pda_blank.png" = 'icons/pda_icons/pda_blank.png',
"pda_boom.png" = 'icons/pda_icons/pda_boom.png',
"pda_bucket.png" = 'icons/pda_icons/pda_bucket.png',
"pda_medbot.png" = 'icons/pda_icons/pda_medbot.png',
"pda_floorbot.png" = 'icons/pda_icons/pda_floorbot.png',
"pda_cleanbot.png" = 'icons/pda_icons/pda_cleanbot.png',
"pda_crate.png" = 'icons/pda_icons/pda_crate.png',
"pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png',
"pda_eject.png" = 'icons/pda_icons/pda_eject.png',
"pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png',
"pda_honk.png" = 'icons/pda_icons/pda_honk.png',
"pda_mail.png" = 'icons/pda_icons/pda_mail.png',
"pda_medical.png" = 'icons/pda_icons/pda_medical.png',
"pda_menu.png" = 'icons/pda_icons/pda_menu.png',
"pda_mule.png" = 'icons/pda_icons/pda_mule.png',
"pda_notes.png" = 'icons/pda_icons/pda_notes.png',
"pda_power.png" = 'icons/pda_icons/pda_power.png',
"pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png',
"pda_reagent.png" = 'icons/pda_icons/pda_reagent.png',
"pda_refresh.png" = 'icons/pda_icons/pda_refresh.png',
"pda_scanner.png" = 'icons/pda_icons/pda_scanner.png',
"pda_signaler.png" = 'icons/pda_icons/pda_signaler.png',
"pda_status.png" = 'icons/pda_icons/pda_status.png',
"pda_dronephone.png" = 'icons/pda_icons/pda_dronephone.png'
"atmos" = 'icons/pda_icons/pda_atmos.png',
"back" = 'icons/pda_icons/pda_back.png',
"bell" = 'icons/pda_icons/pda_bell.png',
"blank" = 'icons/pda_icons/pda_blank.png',
"boom" = 'icons/pda_icons/pda_boom.png',
"bucket" = 'icons/pda_icons/pda_bucket.png',
"medbot" = 'icons/pda_icons/pda_medbot.png',
"floorbot" = 'icons/pda_icons/pda_floorbot.png',
"cleanbot" = 'icons/pda_icons/pda_cleanbot.png',
"crate" = 'icons/pda_icons/pda_crate.png',
"cuffs" = 'icons/pda_icons/pda_cuffs.png',
"eject" = 'icons/pda_icons/pda_eject.png',
"flashlight" = 'icons/pda_icons/pda_flashlight.png',
"honk" = 'icons/pda_icons/pda_honk.png',
"mail" = 'icons/pda_icons/pda_mail.png',
"medical" = 'icons/pda_icons/pda_medical.png',
"menu" = 'icons/pda_icons/pda_menu.png',
"mule" = 'icons/pda_icons/pda_mule.png',
"notes" = 'icons/pda_icons/pda_notes.png',
"power" = 'icons/pda_icons/pda_power.png',
"rdoor" = 'icons/pda_icons/pda_rdoor.png',
"reagent" = 'icons/pda_icons/pda_reagent.png',
"refresh" = 'icons/pda_icons/pda_refresh.png',
"scanner" = 'icons/pda_icons/pda_scanner.png',
"signaler" = 'icons/pda_icons/pda_signaler.png',
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png'
)
/datum/asset/simple/paper
/datum/asset/spritesheet/simple/paper
name = "paper"
assets = list(
"large_stamp-clown.png" = 'icons/stamp_icons/large_stamp-clown.png',
"large_stamp-deny.png" = 'icons/stamp_icons/large_stamp-deny.png',
"large_stamp-ok.png" = 'icons/stamp_icons/large_stamp-ok.png',
"large_stamp-hop.png" = 'icons/stamp_icons/large_stamp-hop.png',
"large_stamp-cmo.png" = 'icons/stamp_icons/large_stamp-cmo.png',
"large_stamp-ce.png" = 'icons/stamp_icons/large_stamp-ce.png',
"large_stamp-hos.png" = 'icons/stamp_icons/large_stamp-hos.png',
"large_stamp-rd.png" = 'icons/stamp_icons/large_stamp-rd.png',
"large_stamp-cap.png" = 'icons/stamp_icons/large_stamp-cap.png',
"large_stamp-qm.png" = 'icons/stamp_icons/large_stamp-qm.png',
"large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png'
"stamp-clown" = 'icons/stamp_icons/large_stamp-clown.png',
"stamp-deny" = 'icons/stamp_icons/large_stamp-deny.png',
"stamp-ok" = 'icons/stamp_icons/large_stamp-ok.png',
"stamp-hop" = 'icons/stamp_icons/large_stamp-hop.png',
"stamp-cmo" = 'icons/stamp_icons/large_stamp-cmo.png',
"stamp-ce" = 'icons/stamp_icons/large_stamp-ce.png',
"stamp-hos" = 'icons/stamp_icons/large_stamp-hos.png',
"stamp-rd" = 'icons/stamp_icons/large_stamp-rd.png',
"stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png',
"stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png',
"stamp-law" = 'icons/stamp_icons/large_stamp-law.png'
)
/datum/asset/simple/IRV
@@ -335,10 +493,22 @@ GLOBAL_LIST_EMPTY(asset_datums)
"changelog.css" = 'html/changelog.css'
)
/datum/asset/simple/goonchat
/datum/asset/group/goonchat
children = list(
/datum/asset/simple/jquery,
/datum/asset/simple/goonchat,
/datum/asset/spritesheet/goonchat
)
/datum/asset/simple/jquery
verify = FALSE
assets = list(
"jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js',
)
/datum/asset/simple/goonchat
verify = FALSE
assets = list(
"json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
"errorHandler.js" = 'code/modules/goonchat/browserassets/js/errorHandler.js',
"browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js',
@@ -350,6 +520,24 @@ GLOBAL_LIST_EMPTY(asset_datums)
"browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
)
/datum/asset/spritesheet/goonchat
name = "chat"
/datum/asset/spritesheet/goonchat/register()
InsertAll("emoji", 'icons/emoji.dmi')
// pre-loading all lanugage icons also helps to avoid meta
InsertAll("language", 'icons/misc/language.dmi')
// catch languages which are pulling icons from another file
for(var/path in typesof(/datum/language))
var/datum/language/L = path
var/icon = initial(L.icon)
if (icon != 'icons/misc/language.dmi')
var/icon_state = initial(L.icon_state)
Insert("language-[icon_state]", icon, icon_state=icon_state)
..()
/datum/asset/simple/permissions
assets = list(
"padlock.png" = 'html/padlock.png'
@@ -362,26 +550,19 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/datum/language/L = new path ()
L.get_icon()
/datum/asset/simple/icon_states/emojis
icon = 'icons/emoji.dmi'
generic_icon_names = TRUE
/datum/asset/spritesheet/pipes
name = "pipes"
/datum/asset/simple/icon_states/multiple_icons/pipes
icons = list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi')
prefix = "pipe"
/datum/asset/simple/icon_states/multiple_icons/pipes/New()
directions = GLOB.alldirs
/datum/asset/spritesheet/pipes/register()
for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi'))
InsertAll("", each, GLOB.alldirs)
..()
/datum/asset/simple/icon_states/multiple_icons/pipes/register()
..()
var/meter = icon('icons/obj/atmospherics/pipes/simple.dmi', "meterX", SOUTH, frame, movement_states)
if(meter)
register_asset(sanitize_filename("[prefix].south.meterX.png"), fcopy_rsc(meter))
// Representative icons for each research design
/datum/asset/simple/research_designs/register()
/datum/asset/spritesheet/research_designs
name = "design"
/datum/asset/spritesheet/research_designs/register()
for (var/path in subtypesof(/datum/design))
var/datum/design/D = path
@@ -401,7 +582,6 @@ GLOBAL_LIST_EMPTY(asset_datums)
if (machine)
item = machine
var/icon_file = initial(item.icon)
var/all_states = icon_states(icon_file)
var/icon/I = icon(icon_file, initial(item.icon_state), SOUTH)
// computers (and snowflakes) get their screen and keyboard sprites
@@ -409,10 +589,11 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/obj/machinery/computer/C = item
var/screen = initial(C.icon_screen)
var/keyboard = initial(C.icon_keyboard)
var/all_states = icon_states(icon_file)
if (screen && (screen in all_states))
I.Blend(icon(icon_file, screen, SOUTH), ICON_OVERLAY)
if (keyboard && (keyboard in all_states))
I.Blend(icon(icon_file, keyboard, SOUTH), ICON_OVERLAY)
assets["design_[initial(D.id)].png"] = I
Insert(initial(D.id), I)
return ..()
+27 -15
View File
@@ -92,7 +92,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (citadel_client_procs(href_list))
return
// CITADEL End
switch(href_list["_src_"])
if("holder")
hsrc = holder
@@ -115,16 +115,16 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
switch(href_list["action"])
if("openLink")
src << link(href_list["link"])
var/datum/real_src = hsrc
if(QDELETED(real_src))
return
if (hsrc)
var/datum/real_src = hsrc
if(QDELETED(real_src))
return
..() //redirect to hsrc.Topic()
/client/proc/is_content_unlocked()
if(!prefs.unlock_content)
to_chat(src, "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! <a href='http://www.byond.com/membership'>Click Here to find out more</a>.")
to_chat(src, "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! <a href=\"https://secure.byond.com/membership\">Click Here to find out more</a>.")
return 0
return 1
@@ -165,6 +165,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
return var_name != NAMEOF(src, holder) && ..()
/client/New(TopicData)
world.SetConfig("APP/admin", ckey, "role=admin") //CITADEL EDIT - Allows admins to reboot in OOM situations
var/tdata = TopicData //save this for later use
chatOutput = new /datum/chatOutput(src)
TopicData = null //Prevent calls to client.Topic from connect
@@ -179,10 +180,15 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
var/connecting_admin = FALSE //because de-admined admins connecting should be treated like admins.
//Admin Authorisation
holder = GLOB.admin_datums[ckey]
var/debug_tools_allowed = FALSE //CITADEL EDIT
if(holder)
GLOB.admins |= src
holder.owner = src
connecting_admin = TRUE
//CITADEL EDIT
if(check_rights_for(src, R_DEBUG))
debug_tools_allowed = TRUE
//END CITADEL EDIT
else if(GLOB.deadmins[ckey])
verbs += /client/proc/readmin
connecting_admin = TRUE
@@ -197,6 +203,12 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
to_chat(world, "Autoadmin rank not found")
else
new /datum/admins(autorank, ckey)
//CITADEL EDIT
if(check_rights_for(src, R_DEBUG)) //check if autoadmin gave us it
debug_tools_allowed = TRUE
if(!debug_tools_allowed)
world.SetConfig("APP/admin", ckey, null)
//END CITADEL EDIT
if(CONFIG_GET(flag/enable_localhost_rank) && !connecting_admin)
var/localhost_addresses = list("127.0.0.1", "::1")
if(isnull(address) || (address in localhost_addresses))
@@ -255,7 +267,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
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>")
to_chat(src, "<span class='danger'>Please download a new version of byond. If [byond_build] is the latest, you can go to <a href=\"https://secure.byond.com/download/build\">BYOND's website</a> 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
@@ -278,11 +290,11 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
var/cev = CONFIG_GET(number/client_error_version)
var/cwv = CONFIG_GET(number/client_warn_version)
if (byond_version < cev) //Out of date client.
to_chat(src, "<span class='danger'><b>Your version of byond is too old:</b></span>")
to_chat(src, "<span class='danger'><b>Your version of BYOND is too old:</b></span>")
to_chat(src, CONFIG_GET(string/client_error_message))
to_chat(src, "Your version: [byond_version]")
to_chat(src, "Required version: [cev] or later")
to_chat(src, "Visit http://www.byond.com/download/ to get the latest version of byond.")
to_chat(src, "Visit <a href=\"https://secure.byond.com/download\">BYOND's website</a> to get the latest version of BYOND.")
if (connecting_admin)
to_chat(src, "Because you are an admin, you are being allowed to walk past this limitation, But it is still STRONGLY suggested you upgrade")
else
@@ -294,14 +306,14 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
msg += CONFIG_GET(string/client_warn_message) + "<br><br>"
msg += "Your version: [byond_version]<br>"
msg += "Required version to remove this message: [cwv] or later<br>"
msg += "Visit http://www.byond.com/download/ to get the latest version of byond.<br>"
msg += "Visit <a href=\"https://secure.byond.com/download\">BYOND's website</a> to get the latest version of BYOND.<br>"
src << browse(msg, "window=warning_popup")
else
to_chat(src, "<span class='danger'><b>Your version of byond may be getting out of date:</b></span>")
to_chat(src, CONFIG_GET(string/client_warn_message))
to_chat(src, "Your version: [byond_version]")
to_chat(src, "Required version to remove this message: [cwv] or later")
to_chat(src, "Visit http://www.byond.com/download/ to get the latest version of byond.")
to_chat(src, "Visit <a href=\"https://secure.byond.com/download\">BYOND's website</a> to get the latest version of BYOND.")
if (connection == "web" && !connecting_admin)
if (!CONFIG_GET(flag/allow_webclient))
@@ -381,7 +393,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
winset(src, "[topmenu.type]", "parent=menu;name=[url_encode(topmenuname)]")
var/list/entries = topmenu.Generate_list(src)
for (var/child in entries)
winset(src, "[url_encode(child)]", "[entries[child]]")
winset(src, "[child]", "[entries[child]]")
if (!ispath(child, /datum/verbs/menu))
var/atom/verb/verbpath = child
if (copytext(verbpath.name,1,2) != "@")
@@ -391,9 +403,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
var/datum/verbs/menu/menuitem = GLOB.menulist[thing]
if (menuitem)
menuitem.Load_checked(src)
hook_vr("client_new",list(src)) // CIT CHANGE - hook for client/New() changes
Master.UpdateTickRate()
//////////////
@@ -472,7 +484,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if (CONFIG_GET(flag/panic_bunker) && !holder && !GLOB.deadmins[ckey])
log_access("Failed Login: [key] - New account attempting to connect during panic bunker")
message_admins("<span class='adminnotice'>Failed Login: [key] - New account attempting to connect during panic bunker</span>")
to_chat(src, "Sorry but the server is currently not accepting connections from never before seen players.")
to_chat(src, "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") //CIT CHANGE - makes the panic bunker disconnect message point to the discord
var/list/connectiontopic_a = params2list(connectiontopic)
var/list/panic_addr = CONFIG_GET(string/panic_server_address)
if(panic_addr && !connectiontopic_a["redirect"])
+91 -85
View File
@@ -83,12 +83,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
//Mob preview
var/icon/preview_icon = null
//Trait list
var/list/positive_traits = list()
var/list/negative_traits = list()
var/list/neutral_traits = list()
var/list/all_traits = list()
var/list/character_traits = list()
//Quirk list
var/list/positive_quirks = list()
var/list/negative_quirks = list()
var/list/neutral_quirks = list()
var/list/all_quirks = list()
var/list/character_quirks = list()
//Jobs, uses bitflags
var/job_civilian_high = 0
@@ -120,6 +120,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/parallax
var/ambientocclusion = TRUE
var/uplink_spawn_loc = UPLINK_PDA
var/list/exp = list()
@@ -193,9 +195,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<center><h2>Occupation Choices</h2>"
dat += "<a href='?_src_=prefs;preference=job;task=menu'>Set Occupation Preferences</a><br></center>"
if(CONFIG_GET(flag/roundstart_traits))
dat += "<center><h2>Trait Setup</h2>"
dat += "<a href='?_src_=prefs;preference=trait;task=menu'>Configure Traits</a><br></center>"
dat += "<center><b>Current traits:</b> [all_traits.len ? all_traits.Join(", ") : "None"]</center>"
dat += "<center><h2>Quirk Setup</h2>"
dat += "<a href='?_src_=prefs;preference=trait;task=menu'>Configure Quirks</a><br></center>"
dat += "<center><b>Current Quirks:</b> [all_quirks.len ? all_quirks.Join(", ") : "None"]</center>"
dat += "<h2>Identity</h2>"
dat += "<table width='100%'><tr><td width='75%' valign='top'>"
if(jobban_isbanned(user, "appearance"))
@@ -490,6 +492,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "High"
dat += "</a><br>"
dat += "<b>Ambient Occlusion:</b> <a href='?_src_=prefs;preference=ambientocclusion'>[ambientocclusion ? "Enabled" : "Disabled"]</a><br>"
dat += "</td><td width='300px' height='300px' valign='top'>"
dat += "<h2>Special Role Settings</h2>"
@@ -855,72 +859,72 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return job_engsec_low
return 0
/datum/preferences/proc/SetTraits(mob/user)
if(!SStraits)
to_chat(user, "<span class='danger'>The trait subsystem is still initializing! Try again in a minute.</span>")
/datum/preferences/proc/SetQuirks(mob/user)
if(!SSquirks)
to_chat(user, "<span class='danger'>The quirk subsystem is still initializing! Try again in a minute.</span>")
return
var/list/dat = list()
if(!SStraits.traits.len)
dat += "The trait subsystem hasn't finished initializing, please hold..."
if(!SSquirks.quirks.len)
dat += "The quirk subsystem hasn't finished initializing, please hold..."
dat += "<center><a href='?_src_=prefs;preference=trait;task=close'>Done</a></center><br>"
else
dat += "<center><b>Choose trait setup</b></center><br>"
dat += "<div align='center'>Left-click to add or remove traits. You need one negative trait for every positive trait.<br>\
Traits are applied at roundstart and cannot normally be removed.</div>"
dat += "<center><b>Choose quirk setup</b></center><br>"
dat += "<div align='center'>Left-click to add or remove quirks. You need negative quirks to have positive ones.<br>\
Quirks are applied at roundstart and cannot normally be removed.</div>"
dat += "<center><a href='?_src_=prefs;preference=trait;task=close'>Done</a></center>"
dat += "<hr>"
dat += "<center><b>Current traits:</b> [all_traits.len ? all_traits.Join(", ") : "None"]</center>"
dat += "<center>[all_traits.len] / [MAX_TRAITS] max traits<br>\
<b>Trait balance remaining:</b> [GetTraitBalance()]</center><br>"
for(var/V in SStraits.traits)
var/datum/trait/T = SStraits.traits[V]
var/trait_name = initial(T.name)
var/has_trait
var/trait_cost = initial(T.value) * -1
dat += "<center><b>Current quirks:</b> [all_quirks.len ? all_quirks.Join(", ") : "None"]</center>"
dat += "<center>[positive_quirks.len] / [MAX_QUIRKS] max positive quirks<br>\
<b>Quirk balance remaining:</b> [GetQuirkBalance()]</center><br>"
for(var/V in SSquirks.quirks)
var/datum/quirk/T = SSquirks.quirks[V]
var/quirk_name = initial(T.name)
var/has_quirk
var/quirk_cost = initial(T.value) * -1
var/lock_reason = "This trait is unavailable."
var/trait_conflict = FALSE
for(var/_V in all_traits)
if(_V == trait_name)
has_trait = TRUE
if(initial(T.mood_trait) && CONFIG_GET(flag/disable_human_mood))
var/quirk_conflict = FALSE
for(var/_V in all_quirks)
if(_V == quirk_name)
has_quirk = TRUE
if(initial(T.mood_quirk) && CONFIG_GET(flag/disable_human_mood))
lock_reason = "Mood is disabled."
trait_conflict = TRUE
if(has_trait)
if(trait_conflict)
all_traits -= trait_name
has_trait = FALSE
quirk_conflict = TRUE
if(has_quirk)
if(quirk_conflict)
all_quirks -= quirk_name
has_quirk = FALSE
else
trait_cost *= -1 //invert it back, since we'd be regaining this amount
if(trait_cost > 0)
trait_cost = "+[trait_cost]"
quirk_cost *= -1 //invert it back, since we'd be regaining this amount
if(quirk_cost > 0)
quirk_cost = "+[quirk_cost]"
var/font_color = "#AAAAFF"
if(initial(T.value) != 0)
font_color = initial(T.value) > 0 ? "#AAFFAA" : "#FFAAAA"
if(trait_conflict)
dat += "<font color='[font_color]'>[trait_name]</font> - [initial(T.desc)] \
if(quirk_conflict)
dat += "<font color='[font_color]'>[quirk_name]</font> - [initial(T.desc)] \
<font color='red'><b>LOCKED: [lock_reason]</b></font><br>"
else
if(has_trait)
dat += "<b><font color='[font_color]'>[trait_name]</font></b> - [initial(T.desc)] \
<a href='?_src_=prefs;preference=trait;task=update;trait=[trait_name]'>[has_trait ? "Lose" : "Take"] ([trait_cost] pts.)</a><br>"
if(has_quirk)
dat += "<b><font color='[font_color]'>[quirk_name]</font></b> - [initial(T.desc)] \
<a href='?_src_=prefs;preference=trait;task=update;trait=[quirk_name]'>[has_quirk ? "Lose" : "Take"] ([quirk_cost] pts.)</a><br>"
else
dat += "<font color='[font_color]'>[trait_name]</font> - [initial(T.desc)] \
<a href='?_src_=prefs;preference=trait;task=update;trait=[trait_name]'>[has_trait ? "Lose" : "Take"] ([trait_cost] pts.)</a><br>"
dat += "<font color='[font_color]'>[quirk_name]</font> - [initial(T.desc)] \
<a href='?_src_=prefs;preference=trait;task=update;trait=[quirk_name]'>[has_quirk ? "Lose" : "Take"] ([quirk_cost] pts.)</a><br>"
dat += "<br><center><a href='?_src_=prefs;preference=trait;task=reset'>Reset Traits</a></center>"
user << browse(null, "window=preferences")
var/datum/browser/popup = new(user, "mob_occupation", "<div align='center'>Trait Preferences</div>", 900, 600) //no reason not to reuse the occupation window, as it's cleaner that way
var/datum/browser/popup = new(user, "mob_occupation", "<div align='center'>Quirk Preferences</div>", 900, 600) //no reason not to reuse the occupation window, as it's cleaner that way
popup.set_window_options("can_close=0")
popup.set_content(dat.Join())
popup.open(0)
return
/datum/preferences/proc/GetTraitBalance()
/datum/preferences/proc/GetQuirkBalance()
var/bal = 0
for(var/V in all_traits)
var/datum/trait/T = SStraits.traits[V]
for(var/V in all_quirks)
var/datum/quirk/T = SSquirks.quirks[V]
bal -= initial(T.value)
return bal
@@ -977,55 +981,49 @@ GLOBAL_LIST_EMPTY(preferences_datums)
user << browse(null, "window=mob_occupation")
ShowChoices(user)
if("update")
var/trait = href_list["trait"]
if(!SStraits.traits[trait])
var/quirk = href_list["trait"]
if(!SSquirks.quirks[quirk])
return
var/value = SStraits.trait_points[trait]
var/value = SSquirks.quirk_points[quirk]
if(value == 0)
if(trait in neutral_traits)
neutral_traits -= trait
all_traits -= trait
if(quirk in neutral_quirks)
neutral_quirks -= quirk
all_quirks -= quirk
else
if(all_traits.len >= MAX_TRAITS)
to_chat(user, "<span class='warning'>You can't have more than [MAX_TRAITS] traits!</span>")
return
neutral_traits += trait
all_traits += trait
neutral_quirks += quirk
all_quirks += quirk
else
var/balance = GetTraitBalance()
if(trait in positive_traits)
positive_traits -= trait
all_traits -= trait
else if(trait in negative_traits)
var/balance = GetQuirkBalance()
if(quirk in positive_quirks)
positive_quirks -= quirk
all_quirks -= quirk
else if(quirk in negative_quirks)
if(balance + value < 0)
to_chat(user, "<span class='warning'>Refunding this would cause you to go below your balance!</span>")
return
negative_traits -= trait
all_traits -= trait
negative_quirks -= quirk
all_quirks -= quirk
else if(value > 0)
if(all_traits.len >= MAX_TRAITS)
to_chat(user, "<span class='warning'>You can't have more than [MAX_TRAITS] traits!</span>")
if(positive_quirks.len >= MAX_QUIRKS)
to_chat(user, "<span class='warning'>You can't have more than [MAX_QUIRKS] positive quirks!</span>")
return
if(balance - value < 0)
to_chat(user, "<span class='warning'>You don't have enough balance to gain this trait!</span>")
to_chat(user, "<span class='warning'>You don't have enough balance to gain this quirk!</span>")
return
positive_traits += trait
all_traits += trait
positive_quirks += quirk
all_quirks += quirk
else
if(all_traits.len >= MAX_TRAITS)
to_chat(user, "<span class='warning'>You can't have more than [MAX_TRAITS] traits!</span>")
return
negative_traits += trait
all_traits += trait
SetTraits(user)
negative_quirks += quirk
all_quirks += quirk
SetQuirks(user)
if("reset")
all_traits = list()
positive_traits = list()
negative_traits = list()
neutral_traits = list()
SetTraits(user)
all_quirks = list()
positive_quirks = list()
negative_quirks = list()
neutral_quirks = list()
SetQuirks(user)
else
SetTraits(user)
SetQuirks(user)
return TRUE
switch(href_list["task"])
@@ -1535,6 +1533,14 @@ GLOBAL_LIST_EMPTY(preferences_datums)
cit_toggles ^= DIGESTION_NOISES
//END CITADEL EDIT
if("ambientocclusion")
ambientocclusion = !ambientocclusion
if(parent && parent.screen && parent.screen.len)
var/obj/screen/plane_master/game_world/PM = locate(/obj/screen/plane_master/game_world) in parent.screen
PM.filters -= AMBIENT_OCCLUSION
if(ambientocclusion)
PM.filters += AMBIENT_OCCLUSION
if("save")
save_preferences()
save_character()
@@ -1642,4 +1648,4 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(icon_updates)
character.update_body()
character.update_hair()
character.update_body_parts()
character.update_body_parts()
+17 -14
View File
@@ -130,6 +130,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["uses_glasses_colour"]>> uses_glasses_colour
S["clientfps"] >> clientfps
S["parallax"] >> parallax
S["ambientocclusion"] >> ambientocclusion
S["menuoptions"] >> menuoptions
S["enable_tips"] >> enable_tips
S["tip_delay"] >> tip_delay
@@ -160,6 +161,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
toggles = sanitize_integer(toggles, 0, 65535, initial(toggles))
clientfps = sanitize_integer(clientfps, 0, 1000, 0)
parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null)
ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion))
ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form))
ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit))
ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION)
@@ -210,6 +212,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["uses_glasses_colour"], uses_glasses_colour)
WRITE_FILE(S["clientfps"], clientfps)
WRITE_FILE(S["parallax"], parallax)
WRITE_FILE(S["ambientocclusion"], ambientocclusion)
WRITE_FILE(S["menuoptions"], menuoptions)
WRITE_FILE(S["enable_tips"], enable_tips)
WRITE_FILE(S["tip_delay"], tip_delay)
@@ -310,11 +313,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["job_engsec_med"] >> job_engsec_med
S["job_engsec_low"] >> job_engsec_low
//Traits
S["all_traits"] >> all_traits
S["positive_traits"] >> positive_traits
S["negative_traits"] >> negative_traits
S["neutral_traits"] >> neutral_traits
//Quirks
S["all_quirks"] >> all_quirks
S["positive_quirks"] >> positive_quirks
S["negative_quirks"] >> negative_quirks
S["neutral_quirks"] >> neutral_quirks
//Citadel code
S["feature_genitals_use_skintone"] >> features["genitals_use_skintone"]
@@ -421,10 +424,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med))
job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low))
all_traits = SANITIZE_LIST(all_traits)
positive_traits = SANITIZE_LIST(positive_traits)
negative_traits = SANITIZE_LIST(negative_traits)
neutral_traits = SANITIZE_LIST(neutral_traits)
all_quirks = SANITIZE_LIST(all_quirks)
positive_quirks = SANITIZE_LIST(positive_quirks)
negative_quirks = SANITIZE_LIST(negative_quirks)
neutral_quirks = SANITIZE_LIST(neutral_quirks)
cit_character_pref_load(S)
@@ -491,11 +494,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["job_engsec_med"] , job_engsec_med)
WRITE_FILE(S["job_engsec_low"] , job_engsec_low)
//Traits
WRITE_FILE(S["all_traits"] , all_traits)
WRITE_FILE(S["positive_traits"] , positive_traits)
WRITE_FILE(S["negative_traits"] , negative_traits)
WRITE_FILE(S["neutral_traits"] , neutral_traits)
//Quirks
WRITE_FILE(S["all_quirks"] , all_quirks)
WRITE_FILE(S["positive_quirks"] , positive_quirks)
WRITE_FILE(S["negative_quirks"] , negative_quirks)
WRITE_FILE(S["neutral_quirks"] , neutral_quirks)
cit_character_pref_save(S)
+2 -1
View File
@@ -83,7 +83,8 @@
/obj/item/clothing/equipped(mob/user, slot)
..()
if (!istype(user))
return
if(slot_flags & slotdefine2slotbit(slot)) //Was equipped to a valid slot for this item?
if (LAZYLEN(user_vars_to_edit))
for(var/variable in user_vars_to_edit)
@@ -365,6 +365,7 @@
scan_reagents = 1
flags_1 = NODROP_1
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
resistance_flags = LAVA_PROOF | FIRE_PROOF
/obj/item/clothing/glasses/godeye/attackby(obj/item/W as obj, mob/user as mob, params)
if(istype(W, src) && W != src && W.loc == user)
@@ -93,7 +93,7 @@
MA.alpha = 180
MA.maptext = "[strength]k"
MA.color = "#64C864"
MA.layer = AREA_LAYER
MA.layer = FLY_LAYER
pic.appearance = MA
flick_overlay(pic, list(user.client), 8)
+8
View File
@@ -158,12 +158,20 @@
strip_delay = 100
dog_fashion = null
/obj/item/clothing/head/helmet/roman/fake
desc = "An ancient helmet made of plastic and leather."
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
/obj/item/clothing/head/helmet/roman/legionaire
name = "roman legionaire helmet"
desc = "An ancient helmet made of bronze and leather. Has a red crest on top of it."
icon_state = "roman_c"
item_state = "roman_c"
/obj/item/clothing/head/helmet/roman/legionaire/fake
desc = "An ancient helmet made of plastic and leather. Has a red crest on top of it."
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
/obj/item/clothing/head/helmet/gladiator
name = "gladiator helmet"
desc = "Ave, Imperator, morituri te salutant."
+24 -1
View File
@@ -321,4 +321,27 @@
name = "headress of Nemes"
desc = "Lavish space tomb not included."
icon_state = "nemes_headdress"
icon_state = "nemes_headdress"
icon_state = "nemes_headdress"
/obj/item/clothing/head/frenchberet
name = "french beret"
desc = "A quality beret, infused with the aroma of chain-smoking, wine-swilling Parisians. You feel less inclined to engage military conflict, for some reason."
icon_state = "beretblack"
/obj/item/clothing/head/frenchberet/speechModification(M)
if(copytext(M, 1, 2) != "*")
M = " [M]"
var/list/french_words = strings("french_replacement.json", "french")
for(var/key in french_words)
var/value = french_words[key]
if(islist(value))
value = pick(value)
M = replacetextEx(M, " [uppertext(key)]", " [uppertext(value)]")
M = replacetextEx(M, " [capitalize(key)]", " [capitalize(value)]")
M = replacetextEx(M, " [key]", " [value]")
if(prob(3))
M += pick(" Honh honh honh!"," Honh!"," Zut Alors!")
return trim(M)
+1 -1
View File
@@ -47,7 +47,7 @@
/obj/item/clothing/mask/fakemoustache/italian/speechModification(M)
if(copytext(M, 1, 2) != "*")
M = " [M]"
var/list/italian_words = strings("word_replacement.json", "italian")
var/list/italian_words = strings("italian_replacement.json", "italian")
for(var/key in italian_words)
var/value = italian_words[key]
+10 -1
View File
@@ -126,7 +126,7 @@
if(user.transferItemToLoc(I, src))
jetpack = I
to_chat(user, "<span class='notice'>You successfully install the jetpack into [src].</span>")
return
else if(istype(I, /obj/item/screwdriver))
if(!jetpack)
to_chat(user, "<span class='warning'>[src] has no jetpack installed.</span>")
@@ -139,6 +139,8 @@
jetpack.forceMove(drop_location())
jetpack = null
to_chat(user, "<span class='notice'>You successfully remove the jetpack from [src].</span>")
return
return ..()
/obj/item/clothing/suit/space/hardsuit/equipped(mob/user, slot)
@@ -237,6 +239,9 @@
brightness_on = 7
allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator)
/obj/item/clothing/head/helmet/space/hardsuit/mining/Initialize()
. = ..()
AddComponent(/datum/component/armor_plate)
/obj/item/clothing/suit/space/hardsuit/mining
icon_state = "hardsuit-mining"
@@ -250,6 +255,10 @@
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/mining
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
/obj/item/clothing/suit/space/hardsuit/mining/Initialize()
. = ..()
AddComponent(/datum/component/armor_plate)
//Syndicate hardsuit
/obj/item/clothing/head/helmet/space/hardsuit/syndi
name = "blood-red hardsuit helmet"
-180
View File
@@ -165,186 +165,6 @@
. = ..()
allowed = GLOB.detective_vest_allowed
//Reactive armor
/obj/item/clothing/suit/armor/reactive
name = "reactive armor"
desc = "Doesn't seem to do much for some reason."
var/active = 0
var/reactivearmor_cooldown_duration = 0 //cooldown specific to reactive armor
var/reactivearmor_cooldown = 0
icon_state = "reactiveoff"
item_state = "reactiveoff"
blood_overlay_type = "armor"
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
actions_types = list(/datum/action/item_action/toggle)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
hit_reaction_chance = 50
/obj/item/clothing/suit/armor/reactive/attack_self(mob/user)
src.active = !( src.active )
if (src.active)
to_chat(user, "<span class='notice'>[src] is now active.</span>")
src.icon_state = "reactive"
src.item_state = "reactive"
else
to_chat(user, "<span class='notice'>[src] is now inactive.</span>")
src.icon_state = "reactiveoff"
src.item_state = "reactiveoff"
src.add_fingerprint(user)
return
/obj/item/clothing/suit/armor/reactive/emp_act(severity)
active = 0
src.icon_state = "reactiveoff"
src.item_state = "reactiveoff"
reactivearmor_cooldown = world.time + 200
..()
//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!)
/obj/item/clothing/suit/armor/reactive/teleport
name = "reactive teleport armor"
desc = "Someone separated our Research Director from his own head!"
var/tele_range = 6
var/rad_amount= 15
reactivearmor_cooldown_duration = 100
/obj/item/clothing/suit/armor/reactive/teleport/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
var/mob/living/carbon/human/H = owner
if(world.time < reactivearmor_cooldown)
owner.visible_message("<span class='danger'>The reactive teleport system is still recharging! It fails to teleport [H]!</span>")
return
owner.visible_message("<span class='danger'>The reactive teleport system flings [H] clear of [attack_text], shutting itself off in the process!</span>")
var/list/turfs = new/list()
for(var/turf/T in orange(tele_range, H))
if(T.density)
continue
if(T.x>world.maxx-tele_range || T.x<tele_range)
continue
if(T.y>world.maxy-tele_range || T.y<tele_range)
continue
turfs += T
if(!turfs.len)
turfs += pick(/turf in orange(tele_range, H))
var/turf/picked = pick(turfs)
if(!isturf(picked))
return
H.forceMove(picked)
H.rad_act(rad_amount)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
return 0
/obj/item/clothing/suit/armor/reactive/fire
name = "reactive incendiary armor"
desc = "An experimental suit of armor with a reactive sensor array rigged to a flame emitter. For the stylish pyromaniac."
/obj/item/clothing/suit/armor/reactive/fire/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
if(world.time < reactivearmor_cooldown)
owner.visible_message("<span class='danger'>The reactive incendiary armor on [owner] activates, but fails to send out flames as it is still recharging its flame jets!</span>")
return
owner.visible_message("<span class='danger'>[src] blocks [attack_text], sending out jets of flame!</span>")
for(var/mob/living/carbon/C in range(6, owner))
if(C != owner)
C.fire_stacks += 8
C.IgniteMob()
owner.fire_stacks = -20
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
return 0
/obj/item/clothing/suit/armor/reactive/stealth
name = "reactive stealth armor"
desc = "An experimental suit of armor that renders the wearer invisible on detection of imminent harm, and creates a decoy that runs away from the owner. You can't fight what you can't see."
/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
if(world.time < reactivearmor_cooldown)
owner.visible_message("<span class='danger'>The reactive stealth system on [owner] activates, but is still recharging its holographic emitters!</span>")
return
var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc)
E.Copy_Parent(owner, 50)
E.GiveTarget(owner) //so it starts running right away
E.Goto(owner, E.move_to_delay, E.minimum_distance)
owner.alpha = 0
owner.visible_message("<span class='danger'>[owner] is hit by [attack_text] in the chest!</span>") //We pretend to be hit, since blocking it would stop the message otherwise
spawn(40)
owner.alpha = initial(owner.alpha)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
/obj/item/clothing/suit/armor/reactive/tesla
name = "reactive tesla armor"
desc = "An experimental suit of armor with sensitive detectors hooked up to a huge capacitor grid, with emitters strutting out of it. Zap."
siemens_coefficient = -1
var/tesla_power = 25000
var/tesla_range = 20
var/tesla_boom = FALSE
var/tesla_stun = FALSE
/obj/item/clothing/suit/armor/reactive/tesla/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
if(world.time < reactivearmor_cooldown)
var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread
sparks.set_up(1, 1, src)
sparks.start()
owner.visible_message("<span class='danger'>The tesla capacitors on [owner]'s reactive tesla armor are still recharging! The armor merely emits some sparks.</span>")
return
owner.visible_message("<span class='danger'>[src] blocks [attack_text], sending out arcs of lightning!</span>")
tesla_zap(owner,tesla_range,tesla_power,tesla_boom, tesla_stun)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
/obj/item/clothing/suit/armor/reactive/table
name = "reactive table armor"
desc = "If you can't beat the memes, embrace them."
var/tele_range = 10
/obj/item/clothing/suit/armor/reactive/table/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
var/mob/living/carbon/human/H = owner
if(world.time < reactivearmor_cooldown)
owner.visible_message("<span class='danger'>The reactive table armor's fabricators are still on cooldown!</span>")
return
owner.visible_message("<span class='danger'>The reactive teleport system flings [H] clear of [attack_text] and slams them into a fabricated table!</span>")
owner.visible_message("<font color='red' size='3'>[H] GOES ON THE TABLE!!!</font>")
owner.Knockdown(40)
var/list/turfs = new/list()
for(var/turf/T in orange(tele_range, H))
if(T.density)
continue
if(T.x>world.maxx-tele_range || T.x<tele_range)
continue
if(T.y>world.maxy-tele_range || T.y<tele_range)
continue
turfs += T
if(!turfs.len)
turfs += pick(/turf in orange(tele_range, H))
var/turf/picked = pick(turfs)
if(!isturf(picked))
return
H.forceMove(picked)
new /obj/structure/table(get_turf(owner))
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
return 0
/obj/item/clothing/suit/armor/reactive/table/emp_act()
return
//All of the armor below is mostly unused
/obj/item/clothing/suit/armor/centcom
@@ -0,0 +1,235 @@
/obj/item/reactive_armour_shell
name = "reactive armour shell"
desc = "An experimental suit of armour, awaiting installation of an anomaly core."
icon_state = "reactiveoff"
icon = 'icons/obj/clothing/suits.dmi'
w_class = WEIGHT_CLASS_BULKY
/obj/item/reactive_armour_shell/attackby(obj/item/I, mob/user, params)
..()
var/static/list/anomaly_armour_types = list(
/obj/effect/anomaly/grav = /obj/item/clothing/suit/armor/reactive/repulse,
/obj/effect/anomaly/flux = /obj/item/clothing/suit/armor/reactive/tesla,
/obj/effect/anomaly/bluespace = /obj/item/clothing/suit/armor/reactive/teleport,
/obj/effect/anomaly/pyro = /obj/item/clothing/suit/armor/reactive/fire)
if(istype(I, /obj/item/assembly/signaler/anomaly))
var/obj/item/assembly/signaler/anomaly/A = I
var/armour_path = anomaly_armour_types[A.anomaly_type]
if(!armour_path)
armour_path = /obj/item/clothing/suit/armor/reactive/stealth //Lets not cheat the player if an anomaly type doesnt have its own armour coded
to_chat(user, "You insert [A] into the chest plate, and the armour gently hums to life.")
new armour_path(get_turf(src))
qdel(src)
qdel(A)
//Reactive armor
/obj/item/clothing/suit/armor/reactive
name = "reactive armor"
desc = "Doesn't seem to do much for some reason."
var/active = 0
var/reactivearmor_cooldown_duration = 0 //cooldown specific to reactive armor
var/reactivearmor_cooldown = 0
icon_state = "reactiveoff"
item_state = "reactiveoff"
blood_overlay_type = "armor"
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
actions_types = list(/datum/action/item_action/toggle)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
hit_reaction_chance = 50
/obj/item/clothing/suit/armor/reactive/attack_self(mob/user)
active = !(active)
if(active)
to_chat(user, "<span class='notice'>[src] is now active.</span>")
icon_state = "reactive"
item_state = "reactive"
else
to_chat(user, "<span class='notice'>[src] is now inactive.</span>")
icon_state = "reactiveoff"
item_state = "reactiveoff"
add_fingerprint(user)
return
/obj/item/clothing/suit/armor/reactive/emp_act(severity)
active = 0
icon_state = "reactiveoff"
item_state = "reactiveoff"
reactivearmor_cooldown = world.time + 200
..()
//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!)
/obj/item/clothing/suit/armor/reactive/teleport
name = "reactive teleport armor"
desc = "Someone separated our Research Director from his own head!"
var/tele_range = 6
var/rad_amount= 15
reactivearmor_cooldown_duration = 100
/obj/item/clothing/suit/armor/reactive/teleport/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
var/mob/living/carbon/human/H = owner
if(world.time < reactivearmor_cooldown)
owner.visible_message("<span class='danger'>The reactive teleport system is still recharging! It fails to teleport [H]!</span>")
return
owner.visible_message("<span class='danger'>The reactive teleport system flings [H] clear of [attack_text], shutting itself off in the process!</span>")
var/list/turfs = new/list()
for(var/turf/T in orange(tele_range, H))
if(T.density)
continue
if(T.x>world.maxx-tele_range || T.x<tele_range)
continue
if(T.y>world.maxy-tele_range || T.y<tele_range)
continue
turfs += T
if(!turfs.len)
turfs += pick(/turf in orange(tele_range, H))
var/turf/picked = pick(turfs)
if(!isturf(picked))
return
H.forceMove(picked)
H.rad_act(rad_amount)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
return 0
//Fire
/obj/item/clothing/suit/armor/reactive/fire
name = "reactive incendiary armor"
desc = "An experimental suit of armor with a reactive sensor array rigged to a flame emitter. For the stylish pyromaniac."
/obj/item/clothing/suit/armor/reactive/fire/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
if(world.time < reactivearmor_cooldown)
owner.visible_message("<span class='danger'>The reactive incendiary armor on [owner] activates, but fails to send out flames as it is still recharging its flame jets!</span>")
return
owner.visible_message("<span class='danger'>[src] blocks [attack_text], sending out jets of flame!</span>")
for(var/mob/living/carbon/C in range(6, owner))
if(C != owner)
C.fire_stacks += 8
C.IgniteMob()
owner.fire_stacks = -20
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
return 0
//Stealth
/obj/item/clothing/suit/armor/reactive/stealth
name = "reactive stealth armor"
desc = "An experimental suit of armor that renders the wearer invisible on detection of imminent harm, and creates a decoy that runs away from the owner. You can't fight what you can't see."
/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
if(world.time < reactivearmor_cooldown)
owner.visible_message("<span class='danger'>The reactive stealth system on [owner] activates, but is still recharging its holographic emitters!</span>")
return
var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc)
E.Copy_Parent(owner, 50)
E.GiveTarget(owner) //so it starts running right away
E.Goto(owner, E.move_to_delay, E.minimum_distance)
owner.alpha = 0
owner.visible_message("<span class='danger'>[owner] is hit by [attack_text] in the chest!</span>") //We pretend to be hit, since blocking it would stop the message otherwise
spawn(40)
owner.alpha = initial(owner.alpha)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
//Tesla
/obj/item/clothing/suit/armor/reactive/tesla
name = "reactive tesla armor"
desc = "An experimental suit of armor with sensitive detectors hooked up to a huge capacitor grid, with emitters strutting out of it. Zap."
siemens_coefficient = -1
var/tesla_power = 25000
var/tesla_range = 20
var/tesla_boom = FALSE
var/tesla_stun = FALSE
/obj/item/clothing/suit/armor/reactive/tesla/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
if(world.time < reactivearmor_cooldown)
var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread
sparks.set_up(1, 1, src)
sparks.start()
owner.visible_message("<span class='danger'>The tesla capacitors on [owner]'s reactive tesla armor are still recharging! The armor merely emits some sparks.</span>")
return
owner.visible_message("<span class='danger'>[src] blocks [attack_text], sending out arcs of lightning!</span>")
tesla_zap(owner,tesla_range,tesla_power,tesla_boom, tesla_stun)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
//Repulse
/obj/item/clothing/suit/armor/reactive/repulse
name = "reactive repulse armor"
desc = "An experimental suit of armor that violently throws back attackers."
/obj/item/clothing/suit/armor/reactive/repulse/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
if(world.time < reactivearmor_cooldown)
owner.visible_message("<span class='danger'>The repulse generator is still recharging!</span>")
return 0
owner.visible_message("<span class='danger'>[src] blocks [attack_text], converting the attack into a wave of force!</span>")
var/turf/T = get_turf(owner)
var/list/thrown_items = list()
for(var/atom/movable/A in range(T, 7))
if(A == owner || A.anchored || thrown_items[A])
continue
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(A, src)))
A.throw_at(throwtarget,10,1)
thrown_items[A] = A
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
/obj/item/clothing/suit/armor/reactive/table
name = "reactive table armor"
desc = "If you can't beat the memes, embrace them."
var/tele_range = 10
/obj/item/clothing/suit/armor/reactive/table/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(!active)
return 0
if(prob(hit_reaction_chance))
var/mob/living/carbon/human/H = owner
if(world.time < reactivearmor_cooldown)
owner.visible_message("<span class='danger'>The reactive table armor's fabricators are still on cooldown!</span>")
return
owner.visible_message("<span class='danger'>The reactive teleport system flings [H] clear of [attack_text] and slams them into a fabricated table!</span>")
owner.visible_message("<font color='red' size='3'>[H] GOES ON THE TABLE!!!</font>")
owner.Knockdown(40)
var/list/turfs = new/list()
for(var/turf/T in orange(tele_range, H))
if(T.density)
continue
if(T.x>world.maxx-tele_range || T.x<tele_range)
continue
if(T.y>world.maxy-tele_range || T.y<tele_range)
continue
turfs += T
if(!turfs.len)
turfs += pick(/turf in orange(tele_range, H))
var/turf/picked = pick(turfs)
if(!isturf(picked))
return
H.forceMove(picked)
new /obj/structure/table(get_turf(owner))
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
return 0
/obj/item/clothing/suit/armor/reactive/table/emp_act()
return
@@ -28,6 +28,14 @@
item_state = "gy_suit"
item_color = "security"
/obj/item/clothing/under/rank/security/skirt
name = "security jumpskirt"
desc = "A \"tactical\" security jumpsuit with the legs replaced by a skirt."
icon_state = "secskirt"
item_state = "r_suit"
item_color = "secskirt"
can_adjust = FALSE //you know now that i think of it if you adjust the skirt and the sprite disappears isn't that just like flashing everyone
/obj/item/clothing/under/rank/warden
name = "security suit"
+4 -2
View File
@@ -15,8 +15,10 @@
search = findtext(text, ":", pos+1)
if(search)
emoji = lowertext(copytext(text, pos+1, search))
if(emoji in emojis)
parsed += icon2html('icons/emoji.dmi', world, emoji)
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat)
var/tag = sheet.icon_tag("emoji-[emoji]")
if(tag)
parsed += tag
pos = search + 1
else
parsed += copytext(text, pos, search)
+11 -6
View File
@@ -11,22 +11,27 @@
/datum/round_event/mass_hallucination/start()
switch(rand(1,4))
if(1) //same sound for everyone
var/sound = pick("explosion","far_explosion","phone","alarm","hallelujah","creepy","ratvar","shuttle_dock",
"wall_decon","door_hack","blob_alert","tesla","malf_ai","meteors")
var/sound = pick("airlock","airlock_pry","console","explosion","far_explosion","mech","glass","alarm","beepsky","mech","wall_decon","door_hack","tesla")
for(var/mob/living/carbon/C in GLOB.alive_mob_list)
new /datum/hallucination/sounds(C, TRUE, sound)
if(2 to 4)
if(2)
var/weirdsound = pick("phone","hallelujah","highlander","hyperspace","game_over","creepy","tesla")
for(var/mob/living/carbon/C in GLOB.alive_mob_list)
new /datum/hallucination/weird_sounds(C, TRUE, weirdsound)
if(3)
var/stationmessage = pick("ratvar","shuttle_dock","blob_alert","malf_ai","meteors","supermatter")
for(var/mob/living/carbon/C in GLOB.alive_mob_list)
new /datum/hallucination/stationmessage(C, TRUE, stationmessage)
if(4 to 6)
var/picked_hallucination = pick( /datum/hallucination/bolts,
/datum/hallucination/whispers,
/datum/hallucination/chat,
/datum/hallucination/message,
/datum/hallucination/bolts,
/datum/hallucination/fake_flood,
/datum/hallucination/battle,
/datum/hallucination/fire,
/datum/hallucination/self_delusion,
/datum/hallucination/fakeattacker,
/datum/hallucination/death,
/datum/hallucination/xeno_attack,
/datum/hallucination/delusion,
/datum/hallucination/oh_yeah)
for(var/mob/living/carbon/C in GLOB.alive_mob_list)
+3 -2
View File
@@ -14,8 +14,9 @@
var/randomProbability = 1
var/reagentsAmount = 100
var/list/saferChems = list("water","carbon","flour","cleaner","nutriment","condensedcapsaicin","mushroomhallucinogen","lube","pink_glitter",
"plantbgone","blood","charcoal","space_drugs","morphine","holywater","ethanol","hot_coco","sacid","mindbreaker","rotatium","skewium",
"pax","laughter","concentrated_barbers_aid","colorful_reagent","dizzysolution","tiresolution","salt","beer","hair_dye","sugar","white_glitter","growthserum")
"plantbgone","blood","charcoal","space_drugs","morphine","holywater","ethanol","hot_coco","sacid","mindbreaker","rotatium",
"pax","laughter","concentrated_barbers_aid","colorful_reagent","dizzysolution","tiresolution","sodiumchloride","beer","hair_dye","sugar","white_glitter","growthserum")
//needs to be chemid unit checked at some point
/datum/round_event/vent_clog/announce()
priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "Atmospherics alert")
File diff suppressed because it is too large Load Diff
@@ -298,6 +298,29 @@
icon_state = "grappabottle"
list_reagents = list("grappa" = 100)
/obj/item/reagent_containers/food/drinks/bottle/sake
name = "Ryo's traditional sake"
desc = "Sweet as can be, and burns like fire going down."
icon_state = "sakebottle"
list_reagents = list("sake" = 100)
/obj/item/reagent_containers/food/drinks/bottle/sake/Initialize()
. = ..()
if(prob(10))
name = "Fluffy Tail Sake"
desc += " On the bottle is a picture of a kitsune with nine touchable tails."
icon_state = "sakebottle_k"
else if(prob(10))
name = "Inubashiri's Home Brew"
desc += " Awoo."
icon_state = "sakebottle_i"
/obj/item/reagent_containers/food/drinks/bottle/fernet
name = "Fernet Bronca"
desc = "A bottle of pure Fernet Bronca, produced in Cordoba Space Station"
icon_state = "fernetbottle"
list_reagents = list("fernet" = 100)
//////////////////////////JUICES AND STUFF ///////////////////////
/obj/item/reagent_containers/food/drinks/bottle/orangejuice
@@ -344,6 +367,15 @@
list_reagents = list("limejuice" = 100)
foodtype = FRUIT
/obj/item/reagent_containers/food/drinks/bottle/menthol
name = "menthol"
desc = "Tastes naturally minty, and imparts a very mild numbing sensation."
icon_state = "mentholbox"
item_state = "carton"
lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi'
isGlass = FALSE
list_reagents = list("menthol" = 100)
////////////////////////// MOLOTOV ///////////////////////
/obj/item/reagent_containers/food/drinks/bottle/molotov
@@ -158,7 +158,7 @@
desc = "A timeless classic."
ingredients_placement = INGREDIENTS_STACKPLUSTOP
icon = 'icons/obj/food/burgerbread.dmi'
icon_state = "bun"
icon_state = "custburg"
foodtype = GRAIN
@@ -299,8 +299,7 @@
qdel(S)
return TRUE
for(var/obj/item/stack/sheet/wetleather/WL in src)
var/obj/item/stack/sheet/leather/L = new(drop_location())
L.amount = WL.amount
new /obj/item/stack/sheet/leather(drop_location(), WL.amount)
qdel(WL)
return TRUE
return FALSE
@@ -623,4 +623,61 @@
id = "crevice_spike"
results = list("crevice_spike" = 6)
required_reagents = list("limejuice" = 2, "capsaicin" = 4) //2 powdered delta (sour), 4 flanergide (spicy)
mix_message = "The mixture stings your eyes as it settles."
mix_message = "The mixture stings your eyes as it settles."
/datum/chemical_reaction/sake
name = "sake"
id = "sake"
results = list("sake" = 10)
required_reagents = list("rice" = 10)
required_catalysts = list("enzyme" = 5)
mix_message = "The rice grains ferment into a clear, sweet-smelling liquid."
/datum/chemical_reaction/alexander
name = "Alexander"
id = "alexander"
results = list("alexander" = 3)
required_reagents = list("cognac" = 1, "creme_de_cacao" = 1, "cream" = 1)
/datum/chemical_reaction/sidecar
name = "Sidecar"
id = "sidecar"
results = list("sidecar" = 4)
required_reagents = list("cognac" = 2, "triple_sec" = 1, "lemonjuice" = 1)
/datum/chemical_reaction/between_the_sheets
name = "Between the Sheets"
id = "between_the_sheets"
results = list("between_the_sheets" = 5)
required_reagents = list("rum" = 1, "sidecar" = 4)
/datum/chemical_reaction/kamikaze
name = "Kamikaze"
id = "kamikaze"
results = list("kamikaze" = 3)
required_reagents = list("vodka" = 1, "triple_sec" = 1, "limejuice" = 1)
/datum/chemical_reaction/mojito
name = "Mojito"
id = "mojito"
results = list("mojito" = 5)
required_reagents = list("rum" = 1, "sugar" = 1, "limejuice" = 1, "sodawater" = 1, "menthol" = 1)
/datum/chemical_reaction/fernet_cola
name = "Fernet Cola"
id = "fernet_cola"
results = list("fernet_cola" = 2)
required_reagents = list("fernet" = 1, "cola" = 1)
/datum/chemical_reaction/fanciulli
name = "Fanciulli"
id = "fanciulli"
results = list("fanciulli" = 2)
required_reagents = list("manhattan" = 1, "fernet" = 1)
/datum/chemical_reaction/branca_menta
name = "Branca Menta"
id = "branca_menta"
results = list("branca_menta" = 3)
required_reagents = list("fernet" = 1, "creme_de_menthe" = 1, "ice" = 1)
+1 -2
View File
@@ -46,8 +46,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
if(!owner)
return
var/datum/asset/stuff = get_asset_datum(/datum/asset/simple/goonchat)
stuff.register()
var/datum/asset/stuff = get_asset_datum(/datum/asset/group/goonchat)
stuff.send(owner)
owner << browse(file('code/modules/goonchat/browserassets/html/browserOutput.html'), "window=browseroutput")
@@ -6,6 +6,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="font-awesome.css" />
<link rel="stylesheet" type="text/css" href="browserOutput.css" />
<link rel="stylesheet" type="text/css" href="spritesheet_chat.css" />
<script type="text/javascript" src="errorHandler.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="json2.min.js"></script>
+9 -1
View File
@@ -70,6 +70,7 @@
begin_month = DECEMBER
end_day = 2
end_month = JANUARY
drone_hat = /obj/item/clothing/head/festive
/datum/holiday/new_year/getStationPrefix()
return pick("Party","New","Hangover","Resolution")
@@ -78,6 +79,7 @@
name = "Groundhog Day"
begin_day = 2
begin_month = FEBRUARY
drone_hat = /obj/item/clothing/head/helmet/space/chronos
/datum/holiday/valentines
name = VALENTINES
@@ -92,6 +94,7 @@
name = "Birthday of Space Station 13"
begin_day = 16
begin_month = FEBRUARY
drone_hat = /obj/item/clothing/head/festive
/datum/holiday/birthday/greet()
var/game_age = text2num(time2text(world.timeofday, "YY")) - 3
@@ -202,6 +205,7 @@
name = "UFO Day"
begin_day = 2
begin_month = JULY
drone_hat = /obj/item/clothing/mask/facehugger/dead
/datum/holiday/UFO/getStationPrefix() //Is such a thing even possible?
return pick("Ayy","Truth","Tsoukalos","Mulder") //Yes it is!
@@ -272,11 +276,13 @@
name = "Smiling Day"
begin_day = 7
begin_month = OCTOBER
drone_hat = /obj/item/clothing/head/papersack/smiley
/datum/holiday/boss
name = "Boss' Day"
begin_day = 16
begin_month = OCTOBER
drone_hat = /obj/item/clothing/head/that
/datum/holiday/halloween
name = HALLOWEEN
@@ -305,6 +311,7 @@
name = "Flowers Day"
begin_day = 19
begin_month = NOVEMBER
drone_hat = /obj/item/reagent_containers/food/snacks/grown/moonflower
/datum/holiday/hello
name = "Saying-'Hello' Day"
@@ -312,7 +319,7 @@
begin_month = NOVEMBER
/datum/holiday/hello/greet()
return "[pick(list("Aloha", "Bonjour", "Hello", "Hi", "Greetings", "Salutations", "Bienvenidos", "Hola", "Howdy"))]! " + ..()
return "[pick(list("Aloha", "Bonjour", "Hello", "Hi", "Greetings", "Salutations", "Bienvenidos", "Hola", "Howdy", "Ni hao", "Guten Tag", "Konnichiwa", "G'day cunt"))]! " + ..()
/datum/holiday/human_rights
name = "Human-Rights Day"
@@ -323,6 +330,7 @@
name = "Monkey Day"
begin_day = 14
begin_month = DECEMBER
drone_hat = /obj/item/clothing/mask/gas/monkeymask
/datum/holiday/thanksgiving
name = "Thanksgiving in the United States"
+1 -2
View File
@@ -262,8 +262,7 @@
if(!check_cost(D.materials, amount))
return FALSE
var/obj/item/stack/product = new D.build_path(loc)
product.amount = amount
new D.build_path(drop_location(), amount)
for(var/R in D.make_reagents)
beaker.reagents.add_reagent(R, D.make_reagents[R]*amount)
else
+26 -20
View File
@@ -85,14 +85,15 @@
interact(user)
return
else if(istype(I, /obj/item/disk/plantgene))
if(disk)
to_chat(user, "<span class='warning'>A data disk is already loaded into the machine!</span>")
else
if(!user.transferItemToLoc(I, src))
return
disk = I
to_chat(user, "<span class='notice'>You add [I] to the machine.</span>")
interact(user)
if (operation)
to_chat(user, "<span class='notice'>Please complete current operation.</span>")
return
eject_disk()
if(!user.transferItemToLoc(I, src))
return
disk = I
to_chat(user, "<span class='notice'>You add [I] to the machine.</span>")
interact(user)
else
..()
@@ -266,18 +267,13 @@
to_chat(usr, "<span class='notice'>You add [I] to the machine.</span>")
update_icon()
else if(href_list["eject_disk"] && !operation)
if (disk)
disk.forceMove(drop_location())
disk.verb_pickup()
disk = null
update_genes()
else
var/obj/item/I = usr.get_active_held_item()
if(istype(I, /obj/item/disk/plantgene))
if(!usr.transferItemToLoc(I, src))
return
disk = I
to_chat(usr, "<span class='notice'>You add [I] to the machine.</span>")
var/obj/item/I = usr.get_active_held_item()
eject_disk()
if(istype(I, /obj/item/disk/plantgene))
if(!usr.transferItemToLoc(I, src))
return
disk = I
to_chat(usr, "<span class='notice'>You add [I] to the machine.</span>")
else if(href_list["op"] == "insert" && disk && disk.gene && seed)
if(!operation) // Wait for confirmation
operation = "insert"
@@ -365,6 +361,16 @@
update_genes()
update_icon()
/obj/machinery/plantgenes/proc/eject_disk()
if (disk && !operation)
if(Adjacent(usr) && !issilicon(usr))
if (!usr.put_in_hands(disk))
disk.forceMove(drop_location())
else
disk.forceMove(drop_location())
disk = null
update_genes()
/obj/machinery/plantgenes/proc/update_genes()
core_genes = list()
reagent_genes = list()
+1 -12
View File
@@ -36,19 +36,8 @@
continue
grassAmt += 1 + round(G.seed.potency * tile_coefficient)
qdel(G)
var/obj/item/stack/tile/GT = new stacktype(user.loc)
while(grassAmt > GT.max_amount)
GT.amount = GT.max_amount
grassAmt -= GT.max_amount
GT = new stacktype(user.loc)
GT.amount = grassAmt
for(var/obj/item/stack/tile/T in user.loc)
if((T.type == stacktype) && (T.amount < T.max_amount))
GT.merge(T)
if(GT.amount <= 0)
break
new stacktype(user.drop_location(), grassAmt)
qdel(src)
return
// Carpet
/obj/item/seeds/grass/carpet
+3 -3
View File
@@ -22,7 +22,7 @@
var/blood_type = null
var/list/features = null
var/factions = null
var/list/traits = null
var/list/quirks = null
var/contains_sample = 0
/obj/item/seeds/replicapod/Initialize()
@@ -42,7 +42,7 @@
blood_type = B.data["blood_type"]
features = B.data["features"]
factions = B.data["factions"]
factions = B.data["traits"]
factions = B.data["quirks"]
contains_sample = TRUE
visible_message("<span class='notice'>The [src] is injected with a fresh blood sample.</span>")
else
@@ -114,7 +114,7 @@
podman.faction |= factions
if(!features["mcolor"])
features["mcolor"] = "#59CE00"
for(var/V in traits)
for(var/V in quirks)
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()
@@ -3,7 +3,7 @@
/obj/item/electronic_assembly
name = "electronic assembly"
obj_flags = CAN_BE_HIT
obj_flags = CAN_BE_HIT | UNIQUE_RENAME
desc = "It's a case, for building small electronics with."
w_class = WEIGHT_CLASS_SMALL
icon = 'icons/obj/assemblies/electronic_setups.dmi'
@@ -17,6 +17,7 @@
var/obj/item/stock_parts/cell/battery // Internal cell which most circuits need to work.
var/cell_type = /obj/item/stock_parts/cell
var/can_charge = TRUE //Can it be charged in a recharger?
var/can_fire_equipped = FALSE //Can it fire/throw weapons when the assembly is being held?
var/charge_sections = 4
var/charge_tick = FALSE
var/charge_delay = 4
@@ -603,7 +604,11 @@
/obj/item/electronic_assembly/medium/gun
name = "type-e electronic mechanism"
icon_state = "setup_medium_gun"
desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, if you're feeling optimistic."
item_state = "circuitgun"
desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, if you're feeling optimistic. It can fire guns and throw items while the user is holding it."
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
can_fire_equipped = TRUE
/obj/item/electronic_assembly/medium/radio
name = "type-f electronic mechanism"
@@ -124,6 +124,10 @@
// Save modified name
if(initial(name) != name)
assembly_params["name"] = name
// Save modified description
if(initial(desc) != desc)
assembly_params["desc"] = desc
// Save panel status
if(opened)
@@ -142,6 +146,8 @@
// Validate name and color
if(assembly_params["name"] && !reject_bad_name(assembly_params["name"], TRUE))
return "Bad assembly name."
if(assembly_params["desc"] && !reject_bad_text(assembly_params["desc"], TRUE))
return "Bad assembly description."
if(assembly_params["detail_color"] && !(assembly_params["detail_color"] in color_whitelist))
return "Bad assembly color."
@@ -151,6 +157,10 @@
// Load modified name, if any.
if(assembly_params["name"])
name = assembly_params["name"]
// Load modified description, if any.
if(assembly_params["desc"])
desc = assembly_params["desc"]
// Load panel status
if(assembly_params["opened"])
@@ -73,7 +73,7 @@
// For really fat machines.
/obj/item/integrated_circuit/passive/power/relay/large
name = "large tesla power relay"
desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them, now in industiral size!"
desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them, now in industrial size!"
w_class = WEIGHT_CLASS_BULKY
extended_desc = "The siphon drains 2 kW of power from an APC in the same room as it as long as it has charge remaining. It will always drain \
from the 'equipment' power channel."
@@ -89,7 +89,7 @@
desc = "Produces electricity from chemicals."
icon_state = "chemical_cell"
extended_desc = "This is effectively an internal beaker. It will consume and produce power from plasma, slime jelly, welding fuel, carbon,\
ethanol, nutriments, and blood in order of decreasing efficiency. It will consume fuel only if the battery can take more energy."
ethanol, nutriment, and blood in order of decreasing efficiency. It will consume fuel only if the battery can take more energy."
container_type = OPENCONTAINER
complexity = 4
inputs = list()
@@ -1,6 +1,6 @@
/obj/item/integrated_circuit/input/card_reader
name = "card reader"
desc = "A circuit that can read registred name, assignment and a PassKey string from an ID card."
desc = "A circuit that can read the registred name, assignment, and PassKey string from an ID card."
icon_state = "card_reader"
complexity = 4
@@ -94,7 +94,7 @@
/obj/item/integrated_circuit/arithmetic/division
name = "division circuit"
desc = "This circuit can divide numbers, just don't think about trying to divide by zero!"
desc = "This circuit can divide numbers. Don't even think about trying to divide by zero!"
extended_desc = "The order that the calculation goes is;<br>\
result = ((((A / B) / C) / D) ... ) and so on, until all pins have been divided. \
Null pins, and pins containing 0, are ignored. Pin A <b>must</b> be a number or the circuit will not function."
@@ -142,8 +142,8 @@
/obj/item/integrated_circuit/arithmetic/sign
name = "sign circuit"
desc = "This will say if a number is positive, negative, or zero."
extended_desc = "Will output 1, -1, or 0, depending on if A is a postive number, a negative number, or zero, respectively."
desc = "This circuit can tell if a number is positive, negative, or zero."
extended_desc = "Will output 1, -1, or 0, depending on if A is a positive number, a negative number, or zero, respectively."
icon_state = "sign"
inputs = list("A" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -212,8 +212,8 @@
/obj/item/integrated_circuit/arithmetic/average
name = "average circuit"
desc = "This circuit is of average quality, however it will compute the average for numbers you give it."
extended_desc = "Note that null pins are ignored, where as a pin containing 0 is included in the averaging calculation."
desc = "This circuit is of average quality. It will compute the average of the numbers you give it."
extended_desc = "Note that null pins are ignored, whereas a pin containing 0 is included in the averaging calculation."
icon_state = "average"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -240,7 +240,7 @@
icon_state = "pi"
inputs = list()
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/arithmetic/pi/Initialize()
. = ..()
desc = "Not recommended for cooking. Outputs '[PI]' when it receives a pulse."
@@ -276,7 +276,7 @@
/obj/item/integrated_circuit/arithmetic/square_root
name = "square root circuit"
desc = "This outputs the square root of a number you input."
desc = "This outputs the square root of the number you input."
icon_state = "square_root"
inputs = list("A" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -69,7 +69,7 @@
/obj/item/integrated_circuit/converter/refcode
name = "reference encoder"
desc = "This circuit can encode a reference into a string, which can then be read by an EPV2 circuit."
desc = "This circuit can encode a reference into a string, which can then be read by a reference decoder circuit."
icon_state = "ref-string"
inputs = list("input" = IC_PINTYPE_REF)
outputs = list("output" = IC_PINTYPE_STRING)
@@ -88,7 +88,7 @@
/obj/item/integrated_circuit/converter/refdecode
name = "reference decoder"
desc = "This circuit can convert an encoded reference to actual reference."
desc = "This circuit can convert an encoded reference to an actual reference."
icon_state = "ref-string"
inputs = list("input" = IC_PINTYPE_STRING)
outputs = list("output" = IC_PINTYPE_REF)
@@ -142,21 +142,18 @@
/obj/item/integrated_circuit/converter/concatenator
name = "concatenator"
desc = "This joins many strings together to get one big string."
desc = "This can join up to 8 strings together to get one big string."
complexity = 4
inputs = list(
"A" = IC_PINTYPE_STRING,
"B" = IC_PINTYPE_STRING,
"C" = IC_PINTYPE_STRING,
"D" = IC_PINTYPE_STRING,
"E" = IC_PINTYPE_STRING,
"F" = IC_PINTYPE_STRING,
"G" = IC_PINTYPE_STRING,
"H" = IC_PINTYPE_STRING
)
inputs = list()
outputs = list("result" = IC_PINTYPE_STRING)
activators = list("concatenate" = IC_PINTYPE_PULSE_IN, "on concatenated" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
var/number_of_pins = 8
/obj/item/integrated_circuit/converter/concatenator/Initialize()
for(var/i = 1 to number_of_pins)
inputs["input [i]"] = IC_PINTYPE_STRING
. = ..()
/obj/item/integrated_circuit/converter/concatenator/do_work()
var/result = null
@@ -169,10 +166,22 @@
push_data()
activate_pin(2)
/obj/item/integrated_circuit/converter/concatenator/small
name = "small concatenator"
desc = "This can join up to 4 strings together to get one big string."
complexity = 2
number_of_pins = 4
/obj/item/integrated_circuit/converter/concatenator/large
name = "large concatenator"
desc = "This can join up to 16 strings together to get one very big string."
complexity = 6
number_of_pins = 16
/obj/item/integrated_circuit/converter/separator
name = "separator"
desc = "This splits as single string into two at the relative split point."
extended_desc = "This circuits splits a given string into two, based on the string, and the index value. \
desc = "This splits a single string into two at the relative split point."
extended_desc = "This circuit splits a given string into two, based on the string and the index value. \
The index splits the string <b>after</b> the given index, including spaces. So 'a person' with an index of '3' \
will split into 'a p' and 'erson'."
icon_state = "split"
@@ -203,11 +212,38 @@
activate_pin(2)
/obj/item/integrated_circuit/converter/indexer
name = "indexer"
desc = "This circuit takes a string and an index value, then returns the character found at in the string at the given index."
extended_desc = "Make sure the index is not longer or shorter than the string length. If you don't, the circuit will return empty."
icon_state = "split"
complexity = 4
inputs = list(
"string to index" = IC_PINTYPE_STRING,
"index" = IC_PINTYPE_NUMBER,
)
outputs = list(
"found character" = IC_PINTYPE_STRING
)
activators = list("index" = IC_PINTYPE_PULSE_IN, "on indexed" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/indexer/do_work()
var/strin = get_pin_data(IC_INPUT, 1)
var/ind = get_pin_data(IC_INPUT, 2)
if(ind > 0 && ind <= length(strin))
set_pin_data(IC_OUTPUT, 1, strin[ind])
else
set_pin_data(IC_OUTPUT, 1, "")
push_data()
activate_pin(2)
/obj/item/integrated_circuit/converter/findstring
name = "find text"
desc = "This gives position of sample in the string. Or returns 0."
desc = "This outputs the position of the sample in the string, or returns 0."
extended_desc = "The first pin is the string to be examined. The second pin is the sample to be found. \
For example, 'eat this burger' will give you position 4. This circuit isn't case sensitive."
For example, inputting 'my wife has caught on fire' with 'has' as the sample will give you position 9. \
This circuit isn't case sensitive, and it does not ignore spaces."
complexity = 4
inputs = list(
"string" = IC_PINTYPE_STRING,
@@ -216,14 +252,38 @@
outputs = list(
"position" = IC_PINTYPE_NUMBER
)
activators = list("search" = IC_PINTYPE_PULSE_IN, "after search" = IC_PINTYPE_PULSE_OUT)
activators = list("search" = IC_PINTYPE_PULSE_IN, "after search" = IC_PINTYPE_PULSE_OUT, "found" = IC_PINTYPE_PULSE_OUT, "not found" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/findstring/do_work()
var/position = findtext(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2))
set_pin_data(IC_OUTPUT, 1, findtext(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2)) )
set_pin_data(IC_OUTPUT, 1, position)
push_data()
activate_pin(2)
if(position)
activate_pin(3)
else
activate_pin(4)
/obj/item/integrated_circuit/converter/stringlength
name = "get length"
desc = "This circuit will return the number of characters in a string."
complexity = 1
inputs = list(
"string" = IC_PINTYPE_STRING
)
outputs = list(
"length" = IC_PINTYPE_NUMBER
)
activators = list("get length" = IC_PINTYPE_PULSE_IN, "on acquisition" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/stringlength/do_work()
set_pin_data(IC_OUTPUT, 1, length(get_pin_data(IC_INPUT, 1)))
push_data()
activate_pin(2)
@@ -232,7 +292,8 @@
name = "string exploder"
desc = "This splits a single string into a list of strings."
extended_desc = "This circuit splits a given string into a list of strings based on the string and given delimiter. \
For example, 'eat this burger' will be converted to list('eat','this','burger')."
For example, 'eat this burger' will be converted to list('eat','this','burger'). Leave the delimiter null to get a list \
of every individual character."
icon_state = "split"
complexity = 4
inputs = list(
@@ -248,7 +309,10 @@
/obj/item/integrated_circuit/converter/exploders/do_work()
var/strin = get_pin_data(IC_INPUT, 1)
var/delimiter = get_pin_data(IC_INPUT, 2)
set_pin_data(IC_OUTPUT, 1, splittext(strin, delimiter))
if(delimiter == null)
set_pin_data(IC_OUTPUT, 1, string2charlist(strin))
else
set_pin_data(IC_OUTPUT, 1, splittext(strin, delimiter))
push_data()
activate_pin(2)
@@ -350,7 +414,7 @@
/obj/item/integrated_circuit/converter/rgb2hex
name = "rgb to hexadecimal"
desc = "This circuit can convert a RGB (Red, Green, Blue) color to a Hexadecimal RGB color."
extended_desc = "The first pin controls red amount, the second pin controls green amount, and the third controls blue amount. All go from 0-255."
extended_desc = "The first pin controls red amount, the second pin controls green amount, and the third controls blue amount. They all go from 0-255."
icon_state = "rgb-hex"
inputs = list(
"red" = IC_PINTYPE_NUMBER,
@@ -4,7 +4,7 @@
/obj/item/integrated_circuit/transfer/multiplexer
name = "two multiplexer"
desc = "This is what those in the business tend to refer to as a 'mux' or data selector. It moves data from one of the selected inputs to the output."
desc = "This is what those in the business tend to refer to as a 'mux', or data selector. It moves data from one of the selected inputs to the output."
extended_desc = "The first input pin is used to select which of the other input pins which has its data moved to the output. \
If the input selection is outside the valid range then no output is given."
complexity = 2
@@ -147,7 +147,7 @@
/obj/item/integrated_circuit/transfer/wire_node
name = "wire node"
desc = "Just wire node to make wiring more easy.Transfer pulse from in to out."
desc = "Just a wire node to make wiring easier. Transfers the pulse from in to out."
icon_state = "wire_node"
activators = list("pulse in" = IC_PINTYPE_PULSE_IN, "pulse out" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -150,7 +150,7 @@
/obj/item/integrated_circuit/input/slime_scanner
name = "slime_scanner"
desc = "A very small version of the xenobio analyser. This allows the machine to know every needed properties of slime. Output mutation list is non associative."
desc = "A very small version of the xenobio analyser. This allows the machine to know every needed properties of slime. Output mutation list is non-associative."
icon_state = "medscan_adv"
complexity = 12
inputs = list("target" = IC_PINTYPE_REF)
@@ -193,8 +193,8 @@
/obj/item/integrated_circuit/input/plant_scanner
name = "integrated plant analyzer"
desc = "A very small version of the plant analyser. This allows the machine to know all valuable params of plants in trays. \
It cannot scan seeds nor fruits, only plants."
desc = "A very small version of the plant analyser. This allows the machine to know all valuable parameters of plants in trays. \
It can only scan plants, not seeds or fruits."
icon_state = "medscan_adv"
complexity = 12
inputs = list("target" = IC_PINTYPE_REF)
@@ -259,9 +259,9 @@
/obj/item/integrated_circuit/input/gene_scanner
name = "gene scanner"
desc = "This circuit will scan plant for traits and reagent genes. Output is non-associative."
desc = "This circuit will scan the target plant for traits and reagent genes. Output is non-associative."
extended_desc = "This allows the machine to scan plants in trays for reagent and trait genes. \
It cannot scan seeds nor fruits, only plants."
It can only scan plants, not seeds or fruits."
inputs = list(
"target" = IC_PINTYPE_REF
)
@@ -358,9 +358,9 @@
/obj/item/integrated_circuit/input/turfpoint
name = "Tile pointer"
desc = "This circuit will get tile ref with given absolute coordinates."
desc = "This circuit will get a tile ref with the provided absolute coordinates."
extended_desc = "If the machine cannot see the target, it will not be able to calculate the correct direction.\
This circuit works only inside an assembly."
This circuit only works while inside an assembly."
icon_state = "numberpad"
complexity = 5
inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER)
@@ -461,7 +461,7 @@
/obj/item/integrated_circuit/input/adjacent_locator
name = "adjacent locator"
desc = "This is needed for certain devices that demand a reference for a target to act upon. This type only locates something \
that is standing a meter away from the machine."
that is standing up to a meter away from the machine."
extended_desc = "The first pin requires a ref to the kind of object that you want the locator to acquire. This means that it will \
give refs to nearby objects that are similar. If more than one valid object is found nearby, it will choose one of them at \
random."
@@ -500,9 +500,9 @@
/obj/item/integrated_circuit/input/advanced_locator_list
complexity = 6
name = "list advanced locator"
desc = "This is needed for certain devices that demand list of names for a targets to act upon. This type locates something \
that is standing in given radius up to 8 meters. Output is non-associative. Input will only consider keys if associative."
extended_desc = "The first pin requires a list of kinds of objects that you want the locator to acquire. It will locate nearby objects by name and description, \
desc = "This is needed for certain devices that demand list of names for a target to act upon. This type locates something \
that is standing in given radius of up to 8 meters. Output is non-associative. Input will only consider keys if associative."
extended_desc = "The first pin requires a list of the kinds of objects that you want the locator to acquire. It will locate nearby objects by name and description, \
and will then provide a list of all found objects which are similar. \
The second pin is a radius."
inputs = list("desired type ref" = IC_PINTYPE_LIST, "radius" = IC_PINTYPE_NUMBER)
@@ -564,10 +564,10 @@
complexity = 6
name = "advanced locator"
desc = "This is needed for certain devices that demand a reference for a target to act upon. This type locates something \
that is standing in given radius up to 8 meters"
extended_desc = "The first pin requires a ref to a kind of object that you want the locator to acquire. This means that it will \
give refs to nearby objects which are similar. If this pin is string, this locator will search for an \
item by matching desired text in it's name and description. If more than one valid object is found nearby, it will choose one of them at \
that is standing in given radius of up to 8 meters"
extended_desc = "The first pin requires a ref to the kind of object that you want the locator to acquire. This means that it will \
give refs to nearby objects which are similar. If this pin is a string, the locator will search for an \
item matching the desired text in its name and description. If more than one valid object is found nearby, it will choose one of them at \
random. The second pin is a radius."
inputs = list("desired type" = IC_PINTYPE_ANY, "radius" = IC_PINTYPE_NUMBER)
outputs = list("located ref" = IC_PINTYPE_REF)
@@ -617,7 +617,7 @@
/obj/item/integrated_circuit/input/signaler
name = "integrated signaler"
desc = "Signals from a signaler can be received with this, allowing for remote control. Additionally, it can send signals as well."
desc = "Signals from a signaler can be received with this, allowing for remote control. It can also send signals."
extended_desc = "When a signal is received from another signaler, the 'on signal received' activator pin will be pulsed. \
The two input pins are to configure the integrated signaler's settings. Note that the frequency should not have a decimal in it, \
meaning the default frequency is expressed as 1457, not 145.7. To send a signal, pulse the 'send signal' activator pin."
@@ -637,6 +637,7 @@
var/frequency = FREQ_SIGNALER
var/code = DEFAULT_SIGNALER_CODE
var/datum/radio_frequency/radio_connection
var/hearing_range = 1
/obj/item/integrated_circuit/input/signaler/Initialize()
. = ..()
@@ -690,15 +691,19 @@
return 0
activate_pin(3)
audible_message("[icon2html(src, hearers(src))] *beep* *beep*", null, 1)
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
for(var/CHM in get_hearers_in_view(hearing_range, src))
if(ismob(CHM))
var/mob/LM = CHM
LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
/obj/item/integrated_circuit/input/ntnet_packet
name = "NTNet networking circuit"
desc = "Enables the sending and receiving of messages on NTNet via packet data protocol."
extended_desc = "Data can be send or received using the second pin on each side, \
desc = "Enables the sending and receiving of messages over NTNet via packet data protocol."
extended_desc = "Data can be sent or received using the second pin on each side, \
with additonal data reserved for the third pin. When a message is received, the second activation pin \
will pulse whatever's connected to it. Pulsing the first activation pin will send a message. Message \
can be send to multiple recepients. Addresses must be separated with ; symbol."
will pulse whatever is connected to it. Pulsing the first activation pin will send a message. Messages \
can be sent to multiple recepients. Addresses must be separated with a semicolon, like this: Address1;Address2;Etc."
icon_state = "signal"
complexity = 2
cooldown_per_use = 1
@@ -751,11 +756,11 @@
/obj/item/integrated_circuit/input/ntnet_advanced
name = "Low level NTNet transreciever"
desc = "Enables the sending and receiving of messages on NTNet via packet data protocol. Allows advanced control of message contents and signalling. Must use associative lists. Outputs associative list. Has a slower transmission rate than normal NTNet circuits, due to increased data processing complexity.."
extended_desc = "Data can be send or received using the second pin on each side, \
desc = "Enables the sending and receiving of messages over NTNet via packet data protocol. Allows advanced control of message contents and signalling. Must use associative lists. Outputs associative list. Has a slower transmission rate than normal NTNet circuits, due to increased data processing complexity."
extended_desc = "Data can be sent or received using the second pin on each side, \
with additonal data reserved for the third pin. When a message is received, the second activation pin \
will pulse whatever's connected to it. Pulsing the first activation pin will send a message. Message \
can be send to multiple recepients. Addresses must be separated with ; symbol."
will pulse whatever is connected to it. Pulsing the first activation pin will send a message. Messages \
can be sent to multiple recepients. Addresses must be separated with a semicolon, like this: Address1;Address2;Etc."
icon_state = "signal"
complexity = 4
cooldown_per_use = 10
@@ -797,7 +802,7 @@
/obj/item/integrated_circuit/input/gps
name = "global positioning system"
desc = "This allows you to easily know the position of a machine containing this device."
extended_desc = "The GPS's coordinates it gives is absolute, not relative."
extended_desc = "The coordinates that the GPS outputs are absolute, not relative."
icon_state = "gps"
complexity = 4
inputs = list()
@@ -824,7 +829,7 @@
/obj/item/integrated_circuit/input/microphone
name = "microphone"
desc = "Useful for spying on people or for voice activated machines."
desc = "Useful for spying on people, or for voice-activated machines."
extended_desc = "This will automatically translate most languages it hears to Galactic Common. \
The first activation pin is always pulsed when the circuit hears someone talk, while the second one \
is only triggered if it hears someone speaking a language other than Galactic Common."
@@ -916,7 +921,7 @@
/obj/item/integrated_circuit/input/obj_scanner
name = "scanner"
desc = "Scans and obtains a reference for any objects you use on the assembly."
extended_desc = "If the 'put down' pin is set to true, the assembly will take the scanned object from your hands to it's location. \
extended_desc = "If the 'put down' pin is set to true, the assembly will take the scanned object from your hands to its location. \
Useful for interaction with the grabber. The scanner only works using the help intent."
icon_state = "recorder"
complexity = 4
@@ -943,7 +948,7 @@
name = "internal battery monitor"
desc = "This monitors the charge level of an internal battery."
icon_state = "internalbm"
extended_desc = "This circuit will give you values of charge, max charge, and percentage of the internal battery on demand."
extended_desc = "This circuit will give you the values of charge, max charge, and the current percentage of the internal battery on demand."
w_class = WEIGHT_CLASS_TINY
complexity = 1
inputs = list()
@@ -976,9 +981,9 @@
/obj/item/integrated_circuit/input/externalbm
name = "external battery monitor"
desc = "This can help to watch battery state of any device in view"
desc = "This can read the battery state of any device in view."
icon_state = "externalbm"
extended_desc = "This circuit will give you values of charge, max charge, and percentage of any device or battery in view"
extended_desc = "This circuit will give you the charge, max charge, and the current percentage values of any device or battery in view."
w_class = WEIGHT_CLASS_TINY
complexity = 2
inputs = list("target" = IC_PINTYPE_REF)
@@ -1011,7 +1016,7 @@
/obj/item/integrated_circuit/input/ntnetsc
name = "NTNet scanner"
desc = "This can return NTNet IDs of a component inside the given object, if there are any."
desc = "This can return the NTNet IDs of a component inside the given object, if there are any."
icon_state = "signalsc"
w_class = WEIGHT_CLASS_TINY
complexity = 2
@@ -17,8 +17,8 @@
/obj/item/integrated_circuit/lists/pick
name = "pick circuit"
desc = "This circuit will pick a random element from the input list, and output said element."
extended_desc = "Input list is unmodified."
desc = "This circuit will pick a random element from the input list, and output that element."
extended_desc = "The input list is not modified."
icon_state = "addition"
outputs = list(
"result" = IC_PINTYPE_ANY
@@ -70,7 +70,7 @@
/obj/item/integrated_circuit/lists/search
name = "search circuit"
desc = "This circuit will get the index location of the desired element in a list."
extended_desc = "Search will start at 1 position and will return first matching position."
extended_desc = "Search will start at position 1 and will return the first matching position."
inputs = list(
"list" = IC_PINTYPE_LIST,
"item" = IC_PINTYPE_ANY
@@ -104,8 +104,8 @@
/obj/item/integrated_circuit/lists/filter
name = "filter circuit"
desc = "This circuit will search through a list for anything matching the desired element(s) and outputs two lists: \
one containing just matching elements, and one with matching elements filtered out."
extended_desc = "Sample accepts lists. If no match is found, original list is sent to output 1."
one containing only the matching elements, and one with the matching elements filtered out."
extended_desc = "Sample accepts lists. If no match is found, the original list is sent to output 1."
inputs = list(
"input list" = IC_PINTYPE_LIST,
"sample" = IC_PINTYPE_ANY
@@ -168,7 +168,7 @@
/obj/item/integrated_circuit/lists/listset
name = "list set circuit"
desc = "This circuit will remove any duplicate entries from a list."
extended_desc = "If there are no duplicate entries, result list will be unchanged."
extended_desc = "If there are no duplicate entries, the output list will be unchanged."
inputs = list(
"list" = IC_PINTYPE_LIST
)
@@ -189,7 +189,7 @@
/obj/item/integrated_circuit/lists/at
name = "at circuit"
desc = "This circuit will pick an element from a list by the input index."
extended_desc = "If there is no element with such index, result will be null."
extended_desc = "If there is no element at the given index, the result will be null."
inputs = list(
"list" = IC_PINTYPE_LIST,
"index" = IC_PINTYPE_INDEX
@@ -225,7 +225,7 @@
/obj/item/integrated_circuit/lists/delete
name = "delete circuit"
desc = "This circuit will remove an element from a list by the index."
extended_desc = "If there is no element with such index, result list will be unchanged."
extended_desc = "If there is no element at the given index, the result list will be unchanged."
inputs = list(
"list" = IC_PINTYPE_LIST,
"index" = IC_PINTYPE_INDEX
@@ -254,7 +254,7 @@
/obj/item/integrated_circuit/lists/write
name = "write circuit"
desc = "This circuit will write an element to a list at the given index location."
extended_desc = "If there is no element with such index, it will give the same list as before."
extended_desc = "If there is no element at the given index, it will output the same list as before."
inputs = list(
"list" = IC_PINTYPE_LIST,
"index" = IC_PINTYPE_INDEX,
@@ -313,7 +313,7 @@
/obj/item/integrated_circuit/lists/jointext
name = "join text circuit"
desc = "This circuit will combine two lists into one and output it as a string."
desc = "This circuit will combine two lists into one, and output it as a string."
extended_desc = "Default settings will encode the entire list into a string."
icon_state = "join"
inputs = list(
@@ -351,7 +351,7 @@
/obj/item/integrated_circuit/lists/constructor
name = "large list constructor"
desc = "This circuit will build a list out of sixteen input values."
desc = "This circuit will build a list out of up to sixteen input values."
icon_state = "constr8"
inputs = list()
outputs = list(
@@ -383,20 +383,20 @@
/obj/item/integrated_circuit/lists/constructor/small
name = "list constructor"
desc = "This circuit will build a list out of four input values."
desc = "This circuit will build a list out of up to four input values."
icon_state = "constr"
number_of_pins = 4
/obj/item/integrated_circuit/lists/constructor/medium
name = "medium list constructor"
desc = "This circuit will build a list out of eight input values."
desc = "This circuit will build a list out of up to eight input values."
icon_state = "constr8"
number_of_pins = 8
/obj/item/integrated_circuit/lists/deconstructor
name = "large list deconstructor"
desc = "This circuit will write first sixteen entries of input list, starting with index, into the output values."
desc = "This circuit will write the first sixteen entries of its input list, starting with the index, into the output values."
icon_state = "deconstr8"
inputs = list(
"input" = IC_PINTYPE_LIST,
@@ -428,11 +428,11 @@
/obj/item/integrated_circuit/lists/deconstructor/small
name = "list deconstructor"
desc = "This circuit will write first four entries of input list, starting with index, into the output values."
desc = "This circuit will write the first four entries of its input list, starting with the index, into the output values."
icon_state = "deconstr"
number_of_pins = 4
/obj/item/integrated_circuit/lists/deconstructor/medium
name = "medium list deconstructor"
desc = "This circuit will write first eight entries of input list, starting with index, into the output values."
desc = "This circuit will write the first eight entries of its input list, starting with the index, into the output values."
number_of_pins = 8
@@ -89,7 +89,7 @@
/obj/item/integrated_circuit/logic/binary/rslatch
name = "RS latch"
desc = "This gate is a synchronized RS latch. If both R and S are true, state will not change."
desc = "This gate is a synchronized RS latch. If both R and S are true, its state will not change."
icon_state = "sr_nor"
inputs = list("S" = IC_PINTYPE_ANY,"R" = IC_PINTYPE_ANY)
outputs = list("Q" = IC_PINTYPE_BOOLEAN,"!Q" = IC_PINTYPE_BOOLEAN)
@@ -6,8 +6,9 @@
desc = "This somewhat complicated system allows one to slot in a gun, direct it towards a position, and remotely fire it."
extended_desc = "The firing mechanism can slot in any energy weapon. \
The first and second inputs need to be numbers which correspond to coordinates for the gun to fire at relative to the machine itself. \
The 'fire' activator will cause the mechanism to attempt to fire the weapon at the coordinates, if possible. Mode is switch between \
lethal (TRUE) or stun (FALSE) modes. It uses the internal battery of the weapon."
The 'fire' activator will cause the mechanism to attempt to fire the weapon at the coordinates, if possible. Mode will switch between \
lethal (TRUE) or stun (FALSE) modes. It uses the internal battery of the weapon itself, not the assembly. If you wish to fire the gun while the circuit is in \
hand, you will need to use an assembly that is a gun."
complexity = 20
w_class = WEIGHT_CLASS_SMALL
size = 3
@@ -80,7 +81,7 @@
/obj/item/integrated_circuit/manipulation/weapon_firing/do_work()
if(!installed_gun || !installed_gun.handle_pins())
return
if(!isturf(assembly.loc))
if(!isturf(assembly.loc) && !(assembly.can_fire_equipped && ishuman(assembly.loc)))
return
set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun))
push_data()
@@ -99,6 +100,7 @@
var/target_x = CLAMP(T.x + xo.data, 0, world.maxx)
var/target_y = CLAMP(T.y + yo.data, 0, world.maxy)
assembly.visible_message("<span class='danger'>[assembly] fires [installed_gun]!</span>")
shootAt(locate(target_x, target_y, T.z))
/obj/item/integrated_circuit/manipulation/weapon_firing/proc/shootAt(turf/target)
@@ -134,8 +136,8 @@
desc = "This allows a machine to move in a given direction."
icon_state = "locomotion"
extended_desc = "The circuit accepts a 'dir' number as a direction to move towards.<br>\
Pulsing the 'step towards dir' activator pin will cause the machine to move a meter in that direction, assuming it is not \
being held, or anchored in some way. It should be noted that the ability to move is dependant on the type of assembly that this circuit inhabits."
Pulsing the 'step towards dir' activator pin will cause the machine to move one step in that direction, assuming it is not \
being held, or anchored in some way. It should be noted that the ability to move is dependant on the type of assembly that this circuit inhabits; only drone assemblies can move."
w_class = WEIGHT_CLASS_SMALL
complexity = 10
cooldown_per_use = 1
@@ -169,9 +171,9 @@
/obj/item/integrated_circuit/manipulation/grenade
name = "grenade primer"
desc = "This circuit comes with the ability to attach most types of grenades and prime them at will."
extended_desc = "Time between priming and detonation is limited to between 1 to 12 seconds but is optional. \
If unset, not a number, or a number less than 1 then the grenade's built-in timing will be used. \
Beware: Once primed there is no aborting the process!"
extended_desc = "The time between priming and detonation is limited to between 1 to 12 seconds, but is optional. \
If the input is not set, not a number, or a number less than 1, the grenade's built-in timing will be used. \
Beware: Once primed, there is no aborting the process!"
icon_state = "grenade"
complexity = 30
cooldown_per_use = 10
@@ -243,9 +245,9 @@
name = "plant manipulation module"
desc = "Used to uproot weeds and harvest/plant trays."
icon_state = "plant_m"
extended_desc = "The circuit accepts a reference to a hydroponic tray or an item in an adjacent tile. \
Mode input(0-harvest, 1-uproot weeds, 2-uproot plant, 3-plant seed) determines action. \
Harvesting returns a list of the harvested plants."
extended_desc = "The circuit accepts a reference to a hydroponic tray or an item on an adjacent tile. \
Mode input (0-harvest, 1-uproot weeds, 2-uproot plant, 3-plant seed) determines action. \
Harvesting outputs a list of the harvested plants."
w_class = WEIGHT_CLASS_TINY
complexity = 10
inputs = list("tray" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER,"item" = IC_PINTYPE_REF)
@@ -345,9 +347,9 @@
/obj/item/integrated_circuit/manipulation/grabber
name = "grabber"
desc = "A circuit with it's own inventory for items, used to grab and store things."
desc = "A circuit with its own inventory for items. Used to grab and store things."
icon_state = "grabber"
extended_desc = "The circuit accepts a reference to an object to be grabbed and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, and -1 to eject all objects."
extended_desc = "This circuit accepts a reference to an object to be grabbed, and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, and -1 to eject all objects. If you throw something from a grabber's inventory with a thrower, the grabber will update its outputs accordingly."
w_class = WEIGHT_CLASS_SMALL
size = 3
cooldown_per_use = 5
@@ -384,6 +386,10 @@
var/obj/item/U
for(U in contents)
U.forceMove(T)
update_outputs()
activate_pin(2)
/obj/item/integrated_circuit/manipulation/grabber/proc/update_outputs()
if(contents.len)
set_pin_data(IC_OUTPUT, 1, WEAKREF(contents[1]))
set_pin_data(IC_OUTPUT, 2, WEAKREF(contents[contents.len]))
@@ -393,7 +399,6 @@
set_pin_data(IC_OUTPUT, 3, contents.len)
set_pin_data(IC_OUTPUT, 4, contents)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/manipulation/grabber/attack_self(var/mob/user)
if(contents.len)
@@ -401,16 +406,14 @@
var/obj/item/U
for(U in contents)
U.forceMove(T)
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, null)
set_pin_data(IC_OUTPUT, 3, contents.len)
update_outputs()
push_data()
/obj/item/integrated_circuit/manipulation/claw
name = "pulling claw"
desc = "Circuit which can pull things.."
icon_state = "pull_claw"
extended_desc = "The circuit accepts a reference to thing to be pulled. Modes: 0 for release. 1 for pull."
extended_desc = "This circuit accepts a reference to a thing to be pulled. Modes: 0 for release. 1 for pull."
w_class = WEIGHT_CLASS_SMALL
size = 3
cooldown_per_use = 5
@@ -464,9 +467,10 @@
/obj/item/integrated_circuit/manipulation/thrower
name = "thrower"
desc = "A compact launcher to throw things from inside or nearby tiles."
extended_desc = "The first and second inputs need to be numbers which correspond to coordinates to throw objects at relative to the machine itself. \
extended_desc = "The first and second inputs need to be numbers which correspond to the coordinates to throw objects at relative to the machine itself. \
The 'fire' activator will cause the mechanism to attempt to throw objects at the coordinates, if possible. Note that the \
projectile need to be inside the machine, or to be on an adjacent tile, and must be medium sized or smaller."
projectile needs to be inside the machine, or on an adjacent tile, and must be medium sized or smaller. The assembly \
must also be a gun if you wish to throw something while the assembly is in hand."
complexity = 25
w_class = WEIGHT_CLASS_SMALL
size = 2
@@ -497,6 +501,9 @@
if(max_w_class && (A.w_class > max_w_class))
return
if(!assembly.can_fire_equipped && ishuman(assembly.loc))
return
// Is the target inside the assembly or close to it?
if(!check_target(A, exclude_components = TRUE))
return
@@ -511,13 +518,21 @@
if(!M.temporarilyRemoveItemFromInventory(A))
return
// If the item is in a grabber circuit we'll update the grabber's outputs after we've thrown it.
var/obj/item/integrated_circuit/manipulation/grabber/G = A.loc
var/x_abs = CLAMP(T.x + target_x_rel, 0, world.maxx)
var/y_abs = CLAMP(T.y + target_y_rel, 0, world.maxy)
var/range = round(CLAMP(sqrt(target_x_rel*target_x_rel+target_y_rel*target_y_rel),0,8),1)
assembly.visible_message("<span class='danger'>[assembly] has thrown [A]!</span>")
A.forceMove(drop_location())
A.throw_at(locate(x_abs, y_abs, T.z), range, 3)
// If the item came from a grabber now we can update the outputs since we've thrown it.
if(G)
G.update_outputs()
/obj/item/integrated_circuit/manipulation/matman
name = "material manager"
desc = "This circuit is designed for automatic storage and distribution of materials."
@@ -56,14 +56,14 @@
/obj/item/integrated_circuit/memory/large
name = "large memory circuit"
desc = "This big circuit can hold eight pieces of data."
desc = "This big circuit can store eight pieces of data."
icon_state = "memory8"
power_draw_per_use = 4
number_of_pins = 8
/obj/item/integrated_circuit/memory/huge
name = "large memory stick"
desc = "This stick of memory can hold up up to sixteen pieces of data."
desc = "This stick of memory can store up up to sixteen pieces of data."
icon_state = "memory16"
w_class = WEIGHT_CLASS_SMALL
spawn_flags = IC_SPAWN_RESEARCH
@@ -92,7 +92,7 @@
/obj/item/integrated_circuit/output/light/advanced
name = "advanced light"
desc = "A light that takes a hexadecimal color value and a brightness value, and can be toggled on/off with a pulse."
desc = "A light that takes a hexadecimal color value and a brightness value, and can be toggled on/off by pulsing it."
icon_state = "light_adv"
complexity = 8
inputs = list(
@@ -28,8 +28,8 @@
extended_desc = "This circuit transmits 20 kJ of electricity every time the activator pin is pulsed. The input pin must be \
a reference to a machine to send electricity to. This can be a battery, or anything containing a battery. The machine can exist \
inside the assembly, or adjacent to it. The power is sourced from the assembly's power cell. If the target is outside of the assembly, \
some power is lost due to ineffiency.Warning!Don't stack more than 1 power transmittors.it becomes less efficient for every other \
transmission circuit in its own assembly and other nearby ones. "
some power is lost due to ineffiency. Warning! Don't stack more than 1 power transmitter, as it becomes less efficient for every other \
transmission circuit in its own assembly and other nearby ones."
w_class = WEIGHT_CLASS_BULKY
complexity = 32
power_draw_per_use = 2000
@@ -21,7 +21,7 @@
desc = "Unlike most electronics, creating smoke is completely intentional."
icon_state = "smoke"
extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \
into the smoke clouds when activated. The reagents are consumed when smoke is made."
into the smoke clouds when activated. The reagents are consumed when the smoke is made."
ext_cooldown = 1
container_type = OPENCONTAINER
volume = 100
@@ -99,10 +99,10 @@
/obj/item/integrated_circuit/reagent/injector
name = "integrated hypo-injector"
desc = "This scary looking thing is able to pump liquids into whatever it's pointed at."
desc = "This scary looking thing is able to pump liquids into, or suck liquids out of, whatever it's pointed at."
icon_state = "injector"
extended_desc = "This autoinjector can push reagents into another container or someone else outside of the machine. The target \
must be adjacent to the machine, and if it is a person, they cannot be wearing thick clothing. Negative given amount makes injector suck out reagents."
extended_desc = "This autoinjector can push up to 30 units of reagents into another container or someone else outside of the machine. The target \
must be adjacent to the machine, and if it is a person, they cannot be wearing thick clothing. Negative given amounts makes the injector suck out reagents instead."
container_type = OPENCONTAINER
volume = 30
@@ -252,7 +252,7 @@
icon_state = "reagent_pump"
extended_desc = "This is a pump which will move liquids from the source ref to the target ref. The third pin determines \
how much liquid is moved per pulse, between 0 and 50. The pump can move reagents to any open container inside the machine, or \
outside the machine if it is next to the machine."
outside the machine if it is adjacent to the machine."
complexity = 8
inputs = list("source" = IC_PINTYPE_REF, "target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER)
@@ -346,7 +346,7 @@
/obj/item/integrated_circuit/reagent/storage/cryo
name = "cryo reagent storage"
desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. This will also suppress reactions."
desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. This will also prevent reactions."
icon_state = "reagent_storage_cryo"
extended_desc = "This is effectively an internal cryo beaker."
@@ -359,7 +359,7 @@
/obj/item/integrated_circuit/reagent/storage/grinder
name = "reagent grinder"
desc = "This is reagent grinder. It accepts a ref to something and refines it into reagents. It can store up to 100u."
desc = "This is a reagent grinder. It accepts a ref to something, and refines it into reagents. It can store up to 100u."
icon_state = "blender"
extended_desc = ""
inputs = list(
@@ -406,7 +406,7 @@
obj/item/integrated_circuit/reagent/storage/juicer
name = "reagent juicer"
desc = "This is reagent juicer. It accepts a ref to something and refines it into reagents. It can store up to 100u."
desc = "This is a reagent juicer. It accepts a ref to something and refines it into reagents. It can store up to 100u."
icon_state = "blender"
extended_desc = ""
inputs = list(
@@ -454,7 +454,7 @@ obj/item/integrated_circuit/reagent/storage/juicer
name = "reagent scanner"
desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. On pulse this beaker will send list of contained reagents."
icon_state = "reagent_scan"
extended_desc = "Mostly useful for reagent filter."
extended_desc = "Mostly useful for filtering reagents."
complexity = 8
outputs = list(
@@ -482,12 +482,12 @@ obj/item/integrated_circuit/reagent/storage/juicer
/obj/item/integrated_circuit/reagent/filter
name = "reagent filter"
desc = "Filtering liquids by list of desired or unwanted reagents."
desc = "Filters liquids by list of desired or unwanted reagents."
icon_state = "reagent_filter"
extended_desc = "This is a filter which will move liquids from the source to the target. \
It will move all reagents, except those in the unwanted list, given the fourth pin if amount value is positive, \
or it will move only desired reagents if amount is negative. The third pin determines \
how much reagent is moved per pulse, between 0 and 50. Amount is given for each separate reagent."
extended_desc = "This is a filter which will move liquids from the source to its target. \
If the amount in the fourth pin is positive, it will move all reagents except those in the unwanted list. \
If the amount in the fourth pin is negative, it will only move the reagents in the wanted list. \
The third pin determines how many reagents are moved per pulse, between 0 and 50. Amount is given for each separate reagent."
complexity = 8
inputs = list(
@@ -37,10 +37,11 @@
activate_pin(2)
/obj/item/integrated_circuit/smart/coord_basic_pathfinder
name = "coordinte pathfinder"
name = "coordinate pathfinder"
desc = "This complex circuit is able to determine what direction a given target is."
extended_desc = "This circuit uses absolute coordintes to determine where the target is. If the machine \
cannot see the target, it will not be able to calculate the correct direction.This circuit is working only in assembly."
extended_desc = "This circuit uses absolute coordinates to determine where the target is. If the machine \
cannot see the target, it will not be able to calculate the correct direction. \
This circuit will only work while inside an assembly."
icon_state = "numberpad"
complexity = 5
inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER,"ignore obstacles" = IC_PINTYPE_BOOLEAN)
@@ -74,8 +75,8 @@
/obj/item/integrated_circuit/smart/advanced_pathfinder
name = "advanced pathfinder"
desc = "This circuit uses a complex processor for long-range pathfinding."
extended_desc = "This circuit uses absolute coordinates for target. A path will be generated taking obstacle input into account, \
pathing around any instances of said input. The passkey provided from a card reader is used to create a valid path through doorways."
extended_desc = "This circuit uses absolute coordinates to find its target. A path will be generated to the target, taking obstacles into account, \
and pathing around any instances of said input. The passkey provided from a card reader is used to calculate a valid path through airlocks."
icon_state = "numberpad"
complexity = 40
cooldown_per_use = 50
@@ -117,4 +118,4 @@
set_pin_data(IC_OUTPUT, 1, Xn)
set_pin_data(IC_OUTPUT, 2, Yn)
push_data()
activate_pin(2)
activate_pin(2)
@@ -53,8 +53,10 @@
/obj/item/integrated_circuit/time/delay/custom
name = "custom delay circuit"
desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
This circuit's delay can be customized, between 1/10th of a second to one hour. The delay is updated upon receiving a pulse."
desc = "This sends a pulse signal out after a delay defined in tenths of a second, critical for ensuring proper control \
flow in a complex machine. This circuit's delay can be customized, between 1/10th of a second to one hour. \
The delay is updated upon receiving a pulse."
extended_desc = "The delay is defined in tenths of a second. For instance, 4 will be a delay of 0.4 seconds, or 15 for 1.5 seconds."
icon_state = "delay"
inputs = list("delay time" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_RESEARCH
@@ -104,7 +106,9 @@
/obj/item/integrated_circuit/time/ticker/custom
name = "custom ticker"
desc = "This advanced circuit sends an automatic pulse every given interval."
desc = "This advanced circuit sends an automatic pulse every given interval, defined in tenths of a second."
extended_desc ="This advanced circuit sends an automatic pulse every given interval, defined in tenths of a second. \
For example, setting the time pin to 4 will send a pulse every 0.4 seconds, or 15 for every 1.5 seconds."
icon_state = "tick-f"
complexity = 8
delay = 2 SECONDS
@@ -115,7 +119,7 @@
/obj/item/integrated_circuit/time/ticker/custom/on_data_written()
var/delay_input = get_pin_data(IC_INPUT, 2)
if(delay_input && isnum(delay_input) )
var/new_delay = CLAMP(delay_input ,1 ,1 HOURS)
var/new_delay = CLAMP(delay_input ,1 ,1 HOURS)
delay = new_delay
..()

Some files were not shown because too many files have changed in this diff Show More