initial commit - cross reference with 5th port - obviously has compile errors

This commit is contained in:
LetterJay
2016-07-03 02:17:19 -05:00
commit 35a1723e98
4355 changed files with 2221257 additions and 0 deletions
+490
View File
@@ -0,0 +1,490 @@
#define MAX_ADMIN_BANS_PER_ADMIN 1
//Either pass the mob you wish to ban in the 'banned_mob' attribute, or the banckey, banip and bancid variables. If both are passed, the mob takes priority! If a mob is not passed, banckey is the minimum that needs to be passed! banip and bancid are optional.
/datum/admins/proc/DB_ban_record(bantype, mob/banned_mob, duration = -1, reason, job = "", rounds = 0, banckey = null, banip = null, bancid = null)
if(!check_rights(R_BAN))
return
establish_db_connection()
if(!dbcon.IsConnected())
return
var/serverip = "[world.internet_address]:[world.port]"
var/bantype_pass = 0
var/bantype_str
var/maxadminbancheck //Used to limit the number of active bans of a certein type that each admin can give. Used to protect against abuse or mutiny.
var/announceinirc //When set, it announces the ban in irc. Intended to be a way to raise an alarm, so to speak.
var/blockselfban //Used to prevent the banning of yourself.
var/kickbannedckey //Defines whether this proc should kick the banned person, if they are connected (if banned_mob is defined).
//some ban types kick players after this proc passes (tempban, permaban), but some are specific to db_ban, so
//they should kick within this proc.
switch(bantype)
if(BANTYPE_PERMA)
bantype_str = "PERMABAN"
duration = -1
bantype_pass = 1
blockselfban = 1
if(BANTYPE_TEMP)
bantype_str = "TEMPBAN"
bantype_pass = 1
blockselfban = 1
if(BANTYPE_JOB_PERMA)
bantype_str = "JOB_PERMABAN"
duration = -1
bantype_pass = 1
if(BANTYPE_JOB_TEMP)
bantype_str = "JOB_TEMPBAN"
bantype_pass = 1
if(BANTYPE_APPEARANCE)
bantype_str = "APPEARANCE_PERMABAN"
duration = -1
bantype_pass = 1
if(BANTYPE_ADMIN_PERMA)
bantype_str = "ADMIN_PERMABAN"
duration = -1
bantype_pass = 1
maxadminbancheck = 1
announceinirc = 1
blockselfban = 1
kickbannedckey = 1
if(BANTYPE_ADMIN_TEMP)
bantype_str = "ADMIN_TEMPBAN"
bantype_pass = 1
maxadminbancheck = 1
announceinirc = 1
blockselfban = 1
kickbannedckey = 1
if( !bantype_pass ) return
if( !istext(reason) ) return
if( !isnum(duration) ) return
var/ckey
var/computerid
var/ip
if(ismob(banned_mob))
ckey = banned_mob.ckey
if(banned_mob.client)
computerid = banned_mob.client.computer_id
ip = banned_mob.client.address
else
computerid = banned_mob.computer_id
ip = banned_mob.lastKnownIP
else if(banckey)
ckey = ckey(banckey)
computerid = bancid
ip = banip
var/DBQuery/query = dbcon.NewQuery("SELECT id FROM [format_table_name("player")] WHERE ckey = '[ckey]'")
query.Execute()
var/validckey = 0
if(query.NextRow())
validckey = 1
if(!validckey)
if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key)))
message_admins("<font color='red'>[key_name_admin(usr)] attempted to ban [ckey], but [ckey] has not been seen yet. Please only ban actual players.</font>",1)
return
var/a_ckey
var/a_computerid
var/a_ip
if(src.owner && istype(src.owner, /client))
a_ckey = src.owner:ckey
a_computerid = src.owner:computer_id
a_ip = src.owner:address
if(blockselfban)
if(a_ckey == ckey)
usr << "<span class='danger'>You cannot apply this ban type on yourself.</span>"
return
var/who
for(var/client/C in clients)
if(!who)
who = "[C]"
else
who += ", [C]"
var/adminwho
for(var/client/C in admins)
if(!adminwho)
adminwho = "[C]"
else
adminwho += ", [C]"
reason = sanitizeSQL(reason)
if(maxadminbancheck)
var/DBQuery/adm_query = dbcon.NewQuery("SELECT count(id) AS num FROM [format_table_name("ban")] WHERE (a_ckey = '[a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
adm_query.Execute()
if(adm_query.NextRow())
var/adm_bans = text2num(adm_query.item[1])
if(adm_bans >= MAX_ADMIN_BANS_PER_ADMIN)
usr << "<span class='danger'>You already logged [MAX_ADMIN_BANS_PER_ADMIN] admin ban(s) or more. Do not abuse this function!</span>"
return
var/sql = "INSERT INTO [format_table_name("ban")] (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)"
var/DBQuery/query_insert = dbcon.NewQuery(sql)
query_insert.Execute()
usr << "<span class='adminnotice'>Ban saved to database.</span>"
message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1)
if(announceinirc)
send2irc("BAN ALERT","[a_ckey] applied a [bantype_str] on [ckey]")
if(kickbannedckey)
if(banned_mob && banned_mob.client && banned_mob.client.ckey == banckey)
del(banned_mob.client)
/datum/admins/proc/DB_ban_unban(ckey, bantype, job = "")
if(!check_rights(R_BAN))
return
var/bantype_str
if(bantype)
var/bantype_pass = 0
switch(bantype)
if(BANTYPE_PERMA)
bantype_str = "PERMABAN"
bantype_pass = 1
if(BANTYPE_TEMP)
bantype_str = "TEMPBAN"
bantype_pass = 1
if(BANTYPE_JOB_PERMA)
bantype_str = "JOB_PERMABAN"
bantype_pass = 1
if(BANTYPE_JOB_TEMP)
bantype_str = "JOB_TEMPBAN"
bantype_pass = 1
if(BANTYPE_APPEARANCE)
bantype_str = "APPEARANCE_PERMABAN"
bantype_pass = 1
if(BANTYPE_ADMIN_PERMA)
bantype_str = "ADMIN_PERMABAN"
bantype_pass = 1
if(BANTYPE_ADMIN_TEMP)
bantype_str = "ADMIN_TEMPBAN"
bantype_pass = 1
if(BANTYPE_ANY_FULLBAN)
bantype_str = "ANY"
bantype_pass = 1
if(BANTYPE_ANY_JOB)
bantype_str = "ANYJOB"
bantype_pass = 1
if( !bantype_pass ) return
var/bantype_sql
if(bantype_str == "ANY")
bantype_sql = "(bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now() ) )"
else if(bantype_str == "ANYJOB")
bantype_sql = "(bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now() ) )"
else
bantype_sql = "bantype = '[bantype_str]'"
var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)"
if(job)
sql += " AND job = '[job]'"
establish_db_connection()
if(!dbcon.IsConnected())
return
var/ban_id
var/ban_number = 0 //failsafe
var/DBQuery/query = dbcon.NewQuery(sql)
query.Execute()
while(query.NextRow())
ban_id = query.item[1]
ban_number++;
if(ban_number == 0)
usr << "<span class='danger'>Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.</span>"
return
if(ban_number > 1)
usr << "<span class='danger'>Database update failed due to multiple bans fitting the search criteria. Note down the ckey, job and current time and contact the database admin.</span>"
return
if(istext(ban_id))
ban_id = text2num(ban_id)
if(!isnum(ban_id))
usr << "<span class='danger'>Database update failed due to a ban ID mismatch. Contact the database admin.</span>"
return
DB_ban_unban_by_id(ban_id)
/datum/admins/proc/DB_ban_edit(banid = null, param = null)
if(!check_rights(R_BAN))
return
if(!isnum(banid) || !istext(param))
usr << "Cancelled"
return
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, duration, reason FROM [format_table_name("ban")] WHERE id = [banid]")
query.Execute()
var/eckey = usr.ckey //Editing admin ckey
var/pckey //(banned) Player ckey
var/duration //Old duration
var/reason //Old reason
if(query.NextRow())
pckey = query.item[1]
duration = query.item[2]
reason = query.item[3]
else
usr << "Invalid ban id. Contact the database admin"
return
reason = sanitizeSQL(reason)
var/value
switch(param)
if("reason")
if(!value)
value = input("Insert the new reason for [pckey]'s ban", "New Reason", "[reason]", null) as null|text
value = sanitizeSQL(value)
if(!value)
usr << "Cancelled"
return
var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("ban")] SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from <cite><b>\\\"[reason]\\\"</b></cite> to <cite><b>\\\"[value]\\\"</b></cite><BR>') WHERE id = [banid]")
update_query.Execute()
message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s reason from [reason] to [value]",1)
if("duration")
if(!value)
value = input("Insert the new duration (in minutes) for [pckey]'s ban", "New Duration", "[duration]", null) as null|num
if(!isnum(value) || !value)
usr << "Cancelled"
return
var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("ban")] SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]<br>'), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]")
message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s duration from [duration] to [value]",1)
update_query.Execute()
if("unban")
if(alert("Unban [pckey]?", "Unban?", "Yes", "No") == "Yes")
DB_ban_unban_by_id(banid)
return
else
usr << "Cancelled"
return
else
usr << "Cancelled"
return
/datum/admins/proc/DB_ban_unban_by_id(id)
if(!check_rights(R_BAN))
return
var/sql = "SELECT ckey FROM [format_table_name("ban")] WHERE id = [id]"
establish_db_connection()
if(!dbcon.IsConnected())
return
var/ban_number = 0 //failsafe
var/pckey
var/DBQuery/query = dbcon.NewQuery(sql)
query.Execute()
while(query.NextRow())
pckey = query.item[1]
ban_number++;
if(ban_number == 0)
usr << "<span class='danger'>Database update failed due to a ban id not being present in the database.</span>"
return
if(ban_number > 1)
usr << "<span class='danger'>Database update failed due to multiple bans having the same ID. Contact the database admin.</span>"
return
if(!src.owner || !istype(src.owner, /client))
return
var/unban_ckey = src.owner:ckey
var/unban_computerid = src.owner:computer_id
var/unban_ip = src.owner:address
var/sql_update = "UPDATE [format_table_name("ban")] SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = '[unban_ip]' WHERE id = [id]"
message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.",1)
var/DBQuery/query_update = dbcon.NewQuery(sql_update)
query_update.Execute()
/client/proc/DB_ban_panel()
set category = "Admin"
set name = "Banning Panel"
set desc = "Edit admin permissions"
if(!holder)
return
holder.DB_ban_panel()
/datum/admins/proc/DB_ban_panel(playerckey = null, adminckey = null)
if(!usr.client)
return
if(!check_rights(R_BAN))
return
establish_db_connection()
if(!dbcon.IsConnected())
usr << "<span class='danger'>Failed to establish database connection.</span>"
return
var/output = "<div align='center'><table width='90%'><tr>"
output += "<td width='35%' align='center'>"
output += "<h1>Banning panel</h1>"
output += "</td>"
output += "<td width='65%' align='center' bgcolor='#f9f9f9'>"
output += "<form method='GET' action='?src=\ref[src]'><b>Add custom ban:</b> (ONLY use this if you can't ban through any other method)"
output += "<input type='hidden' name='src' value='\ref[src]'>"
output += "<table width='100%'><tr>"
output += "<td><b>Ban type:</b><select name='dbbanaddtype'>"
output += "<option value=''>--</option>"
output += "<option value='[BANTYPE_PERMA]'>PERMABAN</option>"
output += "<option value='[BANTYPE_TEMP]'>TEMPBAN</option>"
output += "<option value='[BANTYPE_JOB_PERMA]'>JOB PERMABAN</option>"
output += "<option value='[BANTYPE_JOB_TEMP]'>JOB TEMPBAN</option>"
output += "<option value='[BANTYPE_APPEARANCE]'>IDENTITY BAN</option>"
output += "<option value='[BANTYPE_ADMIN_PERMA]'>ADMIN PERMABAN</option>"
output += "<option value='[BANTYPE_ADMIN_TEMP]'>ADMIN TEMPBAN</option>"
output += "</select></td>"
output += "<td><b>Ckey:</b> <input type='text' name='dbbanaddckey'></td></tr>"
output += "<tr><td><b>IP:</b> <input type='text' name='dbbanaddip'></td>"
output += "<td><b>Computer id:</b> <input type='text' name='dbbanaddcid'></td></tr>"
output += "<tr><td><b>Duration:</b> <input type='text' name='dbbaddduration'></td>"
output += "<td><b>Job:</b><select name='dbbanaddjob'>"
output += "<option value=''>--</option>"
for(var/j in get_all_jobs())
output += "<option value='[j]'>[j]</option>"
for(var/j in nonhuman_positions)
output += "<option value='[j]'>[j]</option>"
for(var/j in list("traitor","changeling","operative","revolutionary", "gangster","cultist","wizard"))
output += "<option value='[j]'>[j]</option>"
output += "</select></td></tr></table>"
output += "<b>Reason:<br></b><textarea name='dbbanreason' cols='50'></textarea><br>"
output += "<input type='submit' value='Add ban'>"
output += "</form>"
output += "</td>"
output += "</tr>"
output += "</table>"
output += "<form method='GET' action='?src=\ref[src]'><b>Search:</b> "
output += "<input type='hidden' name='src' value='\ref[src]'>"
output += "<b>Ckey:</b> <input type='text' name='dbsearchckey' value='[playerckey]'>"
output += "<b>Admin ckey:</b> <input type='text' name='dbsearchadmin' value='[adminckey]'>"
output += "<input type='submit' value='search'>"
output += "</form>"
output += "Please note that all jobban bans or unbans are in-effect the following round."
if(adminckey || playerckey)
var/blcolor = "#ffeeee" //banned light
var/bdcolor = "#ffdddd" //banned dark
var/ulcolor = "#eeffee" //unbanned light
var/udcolor = "#ddffdd" //unbanned dark
output += "<table width='90%' bgcolor='#e3e3e3' cellpadding='5' cellspacing='0' align='center'>"
output += "<tr>"
output += "<th width='25%'><b>TYPE</b></th>"
output += "<th width='20%'><b>CKEY</b></th>"
output += "<th width='20%'><b>TIME APPLIED</b></th>"
output += "<th width='20%'><b>ADMIN</b></th>"
output += "<th width='15%'><b>OPTIONS</b></th>"
output += "</tr>"
adminckey = ckey(adminckey)
playerckey = ckey(playerckey)
var/adminsearch = ""
var/playersearch = ""
if(adminckey)
adminsearch = "AND a_ckey = '[adminckey]' "
if(playerckey)
playersearch = "AND ckey = '[playerckey]' "
var/DBQuery/select_query = dbcon.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits FROM [format_table_name("ban")] WHERE 1 [playersearch] [adminsearch] ORDER BY bantime DESC")
select_query.Execute()
while(select_query.NextRow())
var/banid = select_query.item[1]
var/bantime = select_query.item[2]
var/bantype = select_query.item[3]
var/reason = select_query.item[4]
var/job = select_query.item[5]
var/duration = select_query.item[6]
var/expiration = select_query.item[7]
var/ckey = select_query.item[8]
var/ackey = select_query.item[9]
var/unbanned = select_query.item[10]
var/unbanckey = select_query.item[11]
var/unbantime = select_query.item[12]
var/edits = select_query.item[13]
var/lcolor = blcolor
var/dcolor = bdcolor
if(unbanned)
lcolor = ulcolor
dcolor = udcolor
var/typedesc =""
switch(bantype)
if("PERMABAN")
typedesc = "<font color='red'><b>PERMABAN</b></font>"
if("TEMPBAN")
typedesc = "<b>TEMPBAN</b><br><font size='2'>([duration] minutes [(unbanned) ? "" : "(<a href=\"byond://?src=\ref[src];dbbanedit=duration;dbbanid=[banid]\">Edit</a>))"]<br>Expires [expiration]</font>"
if("JOB_PERMABAN")
typedesc = "<b>JOBBAN</b><br><font size='2'>([job])"
if("JOB_TEMPBAN")
typedesc = "<b>TEMP JOBBAN</b><br><font size='2'>([job])<br>([duration] minutes [(unbanned) ? "" : "(<a href=\"byond://?src=\ref[src];dbbanedit=duration;dbbanid=[banid]\">Edit</a>))"]<br>Expires [expiration]"
if("APPEARANCE_PERMABAN")
typedesc = "<b>IDENTITY PERMABAN</b>"
if("ADMIN_PERMABAN")
typedesc = "<b>ADMIN PERMABAN</b>"
if("ADMIN_TEMPBAN")
typedesc = "<b>ADMIN TEMPBAN</b><br><font size='2'>([duration] minutes [(unbanned) ? "" : "(<a href=\"byond://?src=\ref[src];dbbanedit=duration;dbbanid=[banid]\">Edit</a>))"]<br>Expires [expiration]</font>"
output += "<tr bgcolor='[dcolor]'>"
output += "<td align='center'>[typedesc]</td>"
output += "<td align='center'><b>[ckey]</b></td>"
output += "<td align='center'>[bantime]</td>"
output += "<td align='center'><b>[ackey]</b></td>"
output += "<td align='center'>[(unbanned) ? "" : "<b><a href=\"byond://?src=\ref[src];dbbanedit=unban;dbbanid=[banid]\">Unban</a></b>"]</td>"
output += "</tr>"
output += "<tr bgcolor='[lcolor]'>"
output += "<td align='center' colspan='5'><b>Reason: [(unbanned) ? "" : "(<a href=\"byond://?src=\ref[src];dbbanedit=reason;dbbanid=[banid]\">Edit</a>)"]</b> <cite>\"[reason]\"</cite></td>"
output += "</tr>"
if(edits)
output += "<tr bgcolor='[dcolor]'>"
output += "<td align='center' colspan='5'><b>EDITS</b></td>"
output += "</tr>"
output += "<tr bgcolor='[lcolor]'>"
output += "<td align='center' colspan='5'><font size='2'>[edits]</font></td>"
output += "</tr>"
if(unbanned)
output += "<tr bgcolor='[dcolor]'>"
output += "<td align='center' colspan='5' bgcolor=''><b>UNBANNED by admin [unbanckey] on [unbantime]</b></td>"
output += "</tr>"
output += "<tr>"
output += "<td colspan='5' bgcolor='white'>&nbsp</td>"
output += "</tr>"
output += "</table></div>"
usr << browse(output,"window=lookupbans;size=900x500")
+115
View File
@@ -0,0 +1,115 @@
//Blocks an attempt to connect before even creating our client datum thing.
/world/IsBanned(key,address,computer_id)
if (!key || !address || !computer_id)
log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]'. (If you continue to get this error, please restart byond or contact byond support.)")
if (text2num(computer_id) == 2147483647) //this cid causes stickybans to go haywire
log_access("Failed Login (invalid cid): [key] [address]-[computer_id]")
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided an invalid Computer ID.)")
var/admin = 0
var/ckey = ckey(key)
if((ckey in admin_datums) || (ckey in deadmins))
admin = 1
//Guest Checking
if(IsGuestKey(key))
if (!guests_allowed)
log_access("Failed Login: [key] - Guests not allowed")
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
if (config.panic_bunker && dbcon && dbcon.IsConnected())
log_access("Failed Login: [key] - Guests not allowed during panic bunker")
return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.")
//Population Cap Checking
if(config.extreme_popcap && living_player_count() >= config.extreme_popcap && !admin)
log_access("Failed Login: [key] - Population cap reached")
return list("reason"="popcap", "desc"= "\nReason: [config.extreme_popcap_message]")
if(config.ban_legacy_system)
//Ban Checking
. = CheckBan( ckey(key), computer_id, address )
if(.)
if (admin)
log_admin("The admin [key] has been allowed to bypass a matching ban on [.["key"]]")
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching ban on [.["key"]]</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching ban on [.["key"]]</span>")
else
log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]")
return .
else
var/ckeytext = ckey(key)
if(!establish_db_connection())
world.log << "Ban database connection failure. Key [ckeytext] not checked"
diary << "Ban database connection failure. Key [ckeytext] not checked"
return
var/ipquery = ""
var/cidquery = ""
if(address)
ipquery = " OR ip = '[address]' "
if(computer_id)
cidquery = " OR computerid = '[computer_id]' "
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)")
query.Execute()
while(query.NextRow())
var/pckey = query.item[1]
//var/pip = query.item[2]
//var/pcid = query.item[3]
var/ackey = query.item[4]
var/reason = query.item[5]
var/expiration = query.item[6]
var/duration = query.item[7]
var/bantime = query.item[8]
var/bantype = query.item[9]
if (bantype == "ADMIN_PERMABAN" || bantype == "ADMIN_TEMPBAN")
//admin bans MUST match on ckey to prevent cid-spoofing attacks
// as well as dynamic ip abuse
if (pckey != ckey)
continue
if (admin)
if (bantype == "ADMIN_PERMABAN" || bantype == "ADMIN_TEMPBAN")
log_admin("The admin [key] is admin banned, and has been disallowed access")
message_admins("<span class='adminnotice'>The admin [key] is admin banned, and has been disallowed access</span>")
else
log_admin("The admin [key] has been allowed to bypass a matching ban on [pckey]")
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching ban on [pckey]</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching ban on [pckey]</span>")
continue
var/expires = ""
if(text2num(duration) > 0)
expires = " The ban is for [duration] minutes and expires on [expiration] (server time)."
else
expires = " The is a permanent ban."
var/desc = "\nReason: You, or another user of this computer or connection ([pckey]) is banned from playing here. The ban reason is:\n[reason]\nThis ban was applied by [ackey] on [bantime], [expires]"
. = list("reason"="[bantype]", "desc"="[desc]")
log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]")
return .
. = ..() //default pager ban stuff
if (.)
//byond will not trigger isbanned() for "global" host bans,
//ie, ones where the "apply to this game only" checkbox is not checked (defaults to not checked)
//So it's safe to let admins walk thru host/sticky bans here
if (admin)
log_admin("The admin [key] has been allowed to bypass a matching host/sticky ban")
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching host/sticky ban</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching host/sticky ban</span>")
return null
else
log_access("Failed Login: [key] [computer_id] [address] - Banned [.["message"]]")
return .
+232
View File
@@ -0,0 +1,232 @@
var/CMinutes = null
var/savefile/Banlist
/proc/CheckBan(ckey, id, address)
if(!Banlist) // if Banlist cannot be located for some reason
LoadBans() // try to load the bans
if(!Banlist) // uh oh, can't find bans!
return 0 // ABORT ABORT ABORT
. = list()
var/appeal
if(config && config.banappeals)
appeal = "\nFor more information on your ban, or to appeal, head to <a href='[config.banappeals]'>[config.banappeals]</a>"
Banlist.cd = "/base"
if( "[ckey][id]" in Banlist.dir )
Banlist.cd = "[ckey][id]"
if (Banlist["temp"])
if (!GetExp(Banlist["minutes"]))
ClearTempbans()
return 0
else
.["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]"
else
Banlist.cd = "/base/[ckey][id]"
.["desc"] = "\nReason: [Banlist["reason"]]\nExpires: <B>PERMENANT</B>\nBy: [Banlist["bannedby"]][appeal]"
.["reason"] = "ckey/id"
return .
else
for (var/A in Banlist.dir)
Banlist.cd = "/base/[A]"
var/matches
if( ckey == Banlist["key"] )
matches += "ckey"
if( id == Banlist["id"] )
if(matches)
matches += "/"
matches += "id"
if( address == Banlist["ip"] )
if(matches)
matches += "/"
matches += "ip"
if(matches)
if(Banlist["temp"])
if (!GetExp(Banlist["minutes"]))
ClearTempbans()
return 0
else
.["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]"
else
.["desc"] = "\nReason: [Banlist["reason"]]\nExpires: <B>PERMENANT</B>\nBy: [Banlist["bannedby"]][appeal]"
.["reason"] = matches
return .
return 0
/proc/UpdateTime() //No idea why i made this a proc.
CMinutes = (world.realtime / 10) / 60
return 1
/proc/LoadBans()
Banlist = new("data/banlist.bdb")
log_admin("Loading Banlist")
if (!length(Banlist.dir)) log_admin("Banlist is empty.")
if (!Banlist.dir.Find("base"))
log_admin("Banlist missing base dir.")
Banlist.dir.Add("base")
Banlist.cd = "/base"
else if (Banlist.dir.Find("base"))
Banlist.cd = "/base"
ClearTempbans()
return 1
/proc/ClearTempbans()
UpdateTime()
Banlist.cd = "/base"
for (var/A in Banlist.dir)
Banlist.cd = "/base/[A]"
if (!Banlist["key"] || !Banlist["id"])
RemoveBan(A)
log_admin("Invalid Ban.")
message_admins("Invalid Ban.")
continue
if (!Banlist["temp"]) continue
if (CMinutes >= Banlist["minutes"]) RemoveBan(A)
return 1
/proc/AddBan(ckey, computerid, reason, bannedby, temp, minutes, address)
var/bantimestamp
if (temp)
UpdateTime()
bantimestamp = CMinutes + minutes
Banlist.cd = "/base"
if ( Banlist.dir.Find("[ckey][computerid]") )
usr << text("<span class='danger'>Ban already exists.</span>")
return 0
else
Banlist.dir.Add("[ckey][computerid]")
Banlist.cd = "/base/[ckey][computerid]"
Banlist["key"] << ckey
Banlist["id"] << computerid
Banlist["ip"] << address
Banlist["reason"] << reason
Banlist["bannedby"] << bannedby
Banlist["temp"] << temp
if (temp)
Banlist["minutes"] << bantimestamp
if(!temp)
add_note(ckey, "Permanently banned - [reason]", null, bannedby, 0)
else
add_note(ckey, "Banned for [minutes] minutes - [reason]", null, bannedby, 0)
return 1
/proc/RemoveBan(foldername)
var/key
var/id
Banlist.cd = "/base/[foldername]"
Banlist["key"] >> key
Banlist["id"] >> id
Banlist.cd = "/base"
if (!Banlist.dir.Remove(foldername)) return 0
if(!usr)
log_admin("Ban Expired: [key]")
message_admins("Ban Expired: [key]")
else
ban_unban_log_save("[key_name(usr)] unbanned [key]")
log_admin("[key_name(usr)] unbanned [key]")
message_admins("[key_name_admin(usr)] unbanned: [key]")
feedback_inc("ban_unban",1)
usr.client.holder.DB_ban_unban( ckey(key), BANTYPE_ANY_FULLBAN)
for (var/A in Banlist.dir)
Banlist.cd = "/base/[A]"
if (key == Banlist["key"] /*|| id == Banlist["id"]*/)
Banlist.cd = "/base"
Banlist.dir.Remove(A)
continue
return 1
/proc/GetExp(minutes as num)
UpdateTime()
var/exp = minutes - CMinutes
if (exp <= 0)
return 0
else
var/timeleftstring
if (exp >= 1440) //1440 = 1 day in minutes
timeleftstring = "[round(exp / 1440, 0.1)] Days"
else if (exp >= 60) //60 = 1 hour in minutes
timeleftstring = "[round(exp / 60, 0.1)] Hours"
else
timeleftstring = "[exp] Minutes"
return timeleftstring
/datum/admins/proc/unbanpanel()
var/count = 0
var/dat
//var/dat = "<HR><B>Unban Player:</B> \blue(U) = Unban , (E) = Edit Ban\green (Total<HR><table border=1 rules=all frame=void cellspacing=0 cellpadding=3 >"
Banlist.cd = "/base"
for (var/A in Banlist.dir)
count++
Banlist.cd = "/base/[A]"
var/ref = "\ref[src]"
var/key = Banlist["key"]
var/id = Banlist["id"]
var/ip = Banlist["ip"]
var/reason = Banlist["reason"]
var/by = Banlist["bannedby"]
var/expiry
if(Banlist["temp"])
expiry = GetExp(Banlist["minutes"])
if(!expiry)
expiry = "Removal Pending"
else
expiry = "Permaban"
dat += text("<tr><td><A href='?src=[ref];unbanf=[key][id]'>(U)</A><A href='?src=[ref];unbane=[key][id]'>(E)</A> Key: <B>[key]</B></td><td>ComputerID: <B>[id]</B></td><td>IP: <B>[ip]</B></td><td> [expiry]</td><td>(By: [by])</td><td>(Reason: [reason])</td></tr>")
dat += "</table>"
dat = "<HR><B>Bans:</B> <FONT COLOR=blue>(U) = Unban , (E) = Edit Ban</FONT> - <FONT COLOR=green>([count] Bans)</FONT><HR><table border=1 rules=all frame=void cellspacing=0 cellpadding=3 >[dat]"
usr << browse(dat, "window=unbanp;size=875x400")
//////////////////////////////////// DEBUG ////////////////////////////////////
/proc/CreateBans()
UpdateTime()
var/i
var/last
for(i=0, i<1001, i++)
var/a = pick(1,0)
var/b = pick(1,0)
if(b)
Banlist.cd = "/base"
Banlist.dir.Add("trash[i]trashid[i]")
Banlist.cd = "/base/trash[i]trashid[i]"
Banlist["key"] << "trash[i]"
else
Banlist.cd = "/base"
Banlist.dir.Add("[last]trashid[i]")
Banlist.cd = "/base/[last]trashid[i]"
Banlist["key"] << last
Banlist["id"] << "trashid[i]"
Banlist["reason"] << "Trashban[i]."
Banlist["temp"] << a
Banlist["minutes"] << CMinutes + rand(1,2000)
Banlist["bannedby"] << "trashmin"
last = "trash[i]"
Banlist.cd = "/base"
/proc/ClearAllBans()
Banlist.cd = "/base"
for (var/A in Banlist.dir)
RemoveBan(A)
+822
View File
@@ -0,0 +1,822 @@
var/global/BSACooldown = 0
////////////////////////////////
/proc/message_admins(msg)
msg = "<span class=\"admin\"><span class=\"prefix\">ADMIN LOG:</span> <span class=\"message\">[msg]</span></span>"
admins << msg
/proc/relay_msg_admins(msg)
msg = "<span class=\"admin\"><span class=\"prefix\">RELAY:</span> <span class=\"message\">[msg]</span></span>"
admins << msg
///////////////////////////////////////////////////////////////////////////////////////////////Panels
/datum/admins/proc/show_player_panel(mob/M in mob_list)
set category = "Admin"
set name = "Show Player Panel"
set desc="Edit player (respawn, ban, heal, etc)"
if(!check_rights())
return
if(!isobserver(usr))
log_game("[key_name_admin(usr)] checked the player panel while in game.")
if(!M)
usr << "You seem to be selecting a mob that doesn't exist anymore."
return
var/body = "<html><head><title>Options for [M.key]</title></head>"
body += "<body>Options panel for <b>[M]</b>"
if(M.client)
body += " played by <b>[M.client]</b> "
body += "\[<A href='?_src_=holder;editrights=rank;ckey=[M.ckey]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\]"
if(istype(M, /mob/new_player))
body += " <B>Hasn't Entered Game</B> "
else
body += " \[<A href='?_src_=holder;revive=\ref[M]'>Heal</A>\] "
body += "<br><br>\[ "
body += "<a href='?_src_=vars;Vars=\ref[M]'>VV</a> - "
body += "<a href='?_src_=holder;traitor=\ref[M]'>TP</a> - "
body += "<a href='?priv_msg=[M.ckey]'>PM</a> - "
body += "<a href='?_src_=holder;subtlemessage=\ref[M]'>SM</a> - "
body += "<a href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a>\] </b><br>"
body += "<b>Mob type</b> = [M.type]<br><br>"
body += "<A href='?_src_=holder;boot2=\ref[M]'>Kick</A> | "
body += "<A href='?_src_=holder;newban=\ref[M]'>Ban</A> | "
body += "<A href='?_src_=holder;jobban2=\ref[M]'>Jobban</A> | "
body += "<A href='?_src_=holder;appearanceban=\ref[M]'>Identity Ban</A> | "
if(jobban_isbanned(M, "OOC"))
body+= "<A href='?_src_=holder;jobban3=OOC;jobban4=\ref[M]'><font color=red>OOCBan</font></A> | "
else
body+= "<A href='?_src_=holder;jobban3=OOC;jobban4=\ref[M]'>OOCBan</A> | "
if(jobban_isbanned(M, "emote"))
body+= "<A href='?_src_=holder;jobban3=emote;jobban4=\ref[M]'><font color=red>EmoteBan</font></A> | "
else
body+= "<A href='?_src_=holder;jobban3=emote;jobban4=\ref[M]'>Emoteban</A> | "
body += "<A href='?_src_=holder;shownoteckey=[M.ckey]'>Notes</A> | "
if(M.client)
if(M.client.check_watchlist(M.client.ckey))
body += "<A href='?_src_=holder;watchremove=[M.ckey]'>Remove from Watchlist</A> | "
body += "<A href='?_src_=holder;watchedit=[M.ckey]'>Edit Watchlist reason</A> "
else
body += "<A href='?_src_=holder;watchadd=\ref[M.ckey]'>Add to Watchlist</A> "
body += "| <A href='?_src_=holder;sendtoprison=\ref[M]'>Prison</A> | "
body += "\ <A href='?_src_=holder;sendbacktolobby=\ref[M]'>Send back to Lobby</A> | "
var/muted = M.client.prefs.muted
body += "<br><b>Mute: </b> "
body += "\[<A href='?_src_=holder;mute=[M.ckey];mute_type=[MUTE_IC]'><font color='[(muted & MUTE_IC)?"red":"blue"]'>IC</font></a> | "
body += "<A href='?_src_=holder;mute=[M.ckey];mute_type=[MUTE_OOC]'><font color='[(muted & MUTE_OOC)?"red":"blue"]'>OOC</font></a> | "
body += "<A href='?_src_=holder;mute=[M.ckey];mute_type=[MUTE_PRAY]'><font color='[(muted & MUTE_PRAY)?"red":"blue"]'>PRAY</font></a> | "
body += "<A href='?_src_=holder;mute=[M.ckey];mute_type=[MUTE_ADMINHELP]'><font color='[(muted & MUTE_ADMINHELP)?"red":"blue"]'>ADMINHELP</font></a> | "
body += "<A href='?_src_=holder;mute=[M.ckey];mute_type=[MUTE_DEADCHAT]'><font color='[(muted & MUTE_DEADCHAT)?"red":"blue"]'>DEADCHAT</font></a>\]"
body += "(<A href='?_src_=holder;mute=[M.ckey];mute_type=[MUTE_ALL]'><font color='[(muted & MUTE_ALL)?"red":"blue"]'>toggle all</font></a>)"
body += "<br><br>"
body += "<A href='?_src_=holder;jumpto=\ref[M]'><b>Jump to</b></A> | "
body += "<A href='?_src_=holder;getmob=\ref[M]'>Get</A> | "
body += "<A href='?_src_=holder;sendmob=\ref[M]'>Send To</A>"
body += "<br><br>"
body += "<A href='?_src_=holder;traitor=\ref[M]'>Traitor panel</A> | "
body += "<A href='?_src_=holder;narrateto=\ref[M]'>Narrate to</A> | "
body += "<A href='?_src_=holder;subtlemessage=\ref[M]'>Subtle message</A>"
if (M.client)
if(!istype(M, /mob/new_player))
body += "<br><br>"
body += "<b>Transformation:</b>"
body += "<br>"
//Human
if(ishuman(M))
body += "<B>Human</B> | "
else
body += "<A href='?_src_=holder;humanone=\ref[M]'>Humanize</A> | "
//Monkey
if(ismonkey(M))
body += "<B>Monkeyized</B> | "
else
body += "<A href='?_src_=holder;monkeyone=\ref[M]'>Monkeyize</A> | "
//Corgi
if(iscorgi(M))
body += "<B>Corgized</B> | "
else
body += "<A href='?_src_=holder;corgione=\ref[M]'>Corgize</A> | "
//AI / Cyborg
if(isAI(M))
body += "<B>Is an AI</B> "
else if(ishuman(M))
body += "<A href='?_src_=holder;makeai=\ref[M]'>Make AI</A> | "
body += "<A href='?_src_=holder;makerobot=\ref[M]'>Make Robot</A> | "
body += "<A href='?_src_=holder;makealien=\ref[M]'>Make Alien</A> | "
body += "<A href='?_src_=holder;makeslime=\ref[M]'>Make Slime</A> | "
body += "<A href='?_src_=holder;makeblob=\ref[M]'>Make Blob</A> | "
//Simple Animals
if(isanimal(M))
body += "<A href='?_src_=holder;makeanimal=\ref[M]'>Re-Animalize</A> | "
else
body += "<A href='?_src_=holder;makeanimal=\ref[M]'>Animalize</A> | "
body += "<br><br>"
body += "<b>Rudimentary transformation:</b><font size=2><br>These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.</font><br>"
body += "<A href='?_src_=holder;simplemake=observer;mob=\ref[M]'>Observer</A> | "
body += "\[ Alien: <A href='?_src_=holder;simplemake=drone;mob=\ref[M]'>Drone</A>, "
body += "<A href='?_src_=holder;simplemake=hunter;mob=\ref[M]'>Hunter</A>, "
body += "<A href='?_src_=holder;simplemake=sentinel;mob=\ref[M]'>Sentinel</A>, "
body += "<A href='?_src_=holder;simplemake=praetorian;mob=\ref[M]'>Praetorian</A>, "
body += "<A href='?_src_=holder;simplemake=queen;mob=\ref[M]'>Queen</A>, "
body += "<A href='?_src_=holder;simplemake=larva;mob=\ref[M]'>Larva</A> \] "
body += "<A href='?_src_=holder;simplemake=human;mob=\ref[M]'>Human</A> "
body += "\[ slime: <A href='?_src_=holder;simplemake=slime;mob=\ref[M]'>Baby</A>, "
body += "<A href='?_src_=holder;simplemake=adultslime;mob=\ref[M]'>Adult</A> \] "
body += "<A href='?_src_=holder;simplemake=monkey;mob=\ref[M]'>Monkey</A> | "
body += "<A href='?_src_=holder;simplemake=robot;mob=\ref[M]'>Cyborg</A> | "
body += "<A href='?_src_=holder;simplemake=cat;mob=\ref[M]'>Cat</A> | "
body += "<A href='?_src_=holder;simplemake=runtime;mob=\ref[M]'>Runtime</A> | "
body += "<A href='?_src_=holder;simplemake=corgi;mob=\ref[M]'>Corgi</A> | "
body += "<A href='?_src_=holder;simplemake=ian;mob=\ref[M]'>Ian</A> | "
body += "<A href='?_src_=holder;simplemake=crab;mob=\ref[M]'>Crab</A> | "
body += "<A href='?_src_=holder;simplemake=coffee;mob=\ref[M]'>Coffee</A> | "
//body += "<A href='?_src_=holder;simplemake=parrot;mob=\ref[M]'>Parrot</A> | "
//body += "<A href='?_src_=holder;simplemake=polyparrot;mob=\ref[M]'>Poly</A> | "
body += "\[ Construct: <A href='?_src_=holder;simplemake=constructarmored;mob=\ref[M]'>Juggernaut</A> , "
body += "<A href='?_src_=holder;simplemake=constructbuilder;mob=\ref[M]'>Artificer</A> , "
body += "<A href='?_src_=holder;simplemake=constructwraith;mob=\ref[M]'>Wraith</A> \] "
body += "<A href='?_src_=holder;simplemake=shade;mob=\ref[M]'>Shade</A>"
body += "<br>"
if (M.client)
body += "<br><br>"
body += "<b>Other actions:</b>"
body += "<br>"
body += "<A href='?_src_=holder;forcespeech=\ref[M]'>Forcesay</A> | "
body += "<A href='?_src_=holder;tdome1=\ref[M]'>Thunderdome 1</A> | "
body += "<A href='?_src_=holder;tdome2=\ref[M]'>Thunderdome 2</A> | "
body += "<A href='?_src_=holder;tdomeadmin=\ref[M]'>Thunderdome Admin</A> | "
body += "<A href='?_src_=holder;tdomeobserve=\ref[M]'>Thunderdome Observer</A> | "
body += "<br>"
body += "</body></html>"
usr << browse(body, "window=adminplayeropts-\ref[M];size=550x515")
feedback_add_details("admin_verb","SPP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/access_news_network() //MARKER
set category = "Fun"
set name = "Access Newscaster Network"
set desc = "Allows you to view, add and edit news feeds."
if (!istype(src,/datum/admins))
src = usr.client.holder
if (!istype(src,/datum/admins))
usr << "Error: you are not an admin!"
return
var/dat
dat = text("<HEAD><TITLE>Admin Newscaster</TITLE></HEAD><H3>Admin Newscaster Unit</H3>")
switch(admincaster_screen)
if(0)
dat += "Welcome to the admin newscaster.<BR> Here you can add, edit and censor every newspiece on the network."
dat += "<BR>Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units."
dat += "<BR>Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!</FONT>"
if(news_network.wanted_issue.active)
dat+= "<HR><A href='?src=\ref[src];ac_view_wanted=1'>Read Wanted Issue</A>"
dat+= "<HR><BR><A href='?src=\ref[src];ac_create_channel=1'>Create Feed Channel</A>"
dat+= "<BR><A href='?src=\ref[src];ac_view=1'>View Feed Channels</A>"
dat+= "<BR><A href='?src=\ref[src];ac_create_feed_story=1'>Submit new Feed story</A>"
dat+= "<BR><BR><A href='?src=\ref[usr];mach_close=newscaster_main'>Exit</A>"
var/wanted_already = 0
if(news_network.wanted_issue.active)
wanted_already = 1
dat+="<HR><B>Feed Security functions:</B><BR>"
dat+="<BR><A href='?src=\ref[src];ac_menu_wanted=1'>[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue</A>"
dat+="<BR><A href='?src=\ref[src];ac_menu_censor_story=1'>Censor Feed Stories</A>"
dat+="<BR><A href='?src=\ref[src];ac_menu_censor_channel=1'>Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel.</A>"
dat+="<BR><HR><A href='?src=\ref[src];ac_set_signature=1'>The newscaster recognises you as:<BR> <FONT COLOR='green'>[src.admin_signature]</FONT></A>"
if(1)
dat+= "Station Feed Channels<HR>"
if( isemptylist(news_network.network_channels) )
dat+="<I>No active channels found...</I>"
else
for(var/datum/newscaster/feed_channel/CHANNEL in news_network.network_channels)
if(CHANNEL.is_admin_channel)
dat+="<B><FONT style='BACKGROUND-COLOR: LightGreen'><A href='?src=\ref[src];ac_show_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A></FONT></B><BR>"
else
dat+="<B><A href='?src=\ref[src];ac_show_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ()]<BR></B>"
dat+="<BR><HR><A href='?src=\ref[src];ac_refresh=1'>Refresh</A>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Back</A>"
if(2)
dat+="Creating new Feed Channel..."
dat+="<HR><B><A href='?src=\ref[src];ac_set_channel_name=1'>Channel Name</A>:</B> [src.admincaster_feed_channel.channel_name]<BR>"
dat+="<B><A href='?src=\ref[src];ac_set_signature=1'>Channel Author</A>:</B> <FONT COLOR='green'>[src.admin_signature]</FONT><BR>"
dat+="<B><A href='?src=\ref[src];ac_set_channel_lock=1'>Will Accept Public Feeds</A>:</B> [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]<BR><BR>"
dat+="<BR><A href='?src=\ref[src];ac_submit_new_channel=1'>Submit</A><BR><BR><A href='?src=\ref[src];ac_setScreen=[0]'>Cancel</A><BR>"
if(3)
dat+="Creating new Feed Message..."
dat+="<HR><B><A href='?src=\ref[src];ac_set_channel_receiving=1'>Receiving Channel</A>:</B> [src.admincaster_feed_channel.channel_name]<BR>" //MARK
dat+="<B>Message Author:</B> <FONT COLOR='green'>[src.admin_signature]</FONT><BR>"
dat+="<B><A href='?src=\ref[src];ac_set_new_message=1'>Message Body</A>:</B> [src.admincaster_feed_message.returnBody(-1)] <BR>"
dat+="<BR><A href='?src=\ref[src];ac_submit_new_message=1'>Submit</A><BR><BR><A href='?src=\ref[src];ac_setScreen=[0]'>Cancel</A><BR>"
if(4)
dat+="Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].<BR><BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Return</A><BR>"
if(5)
dat+="Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.<BR><BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Return</A><BR>"
if(6)
dat+="<B><FONT COLOR='maroon'>ERROR: Could not submit Feed story to Network.</B></FONT><HR><BR>"
if(src.admincaster_feed_channel.channel_name=="")
dat+="<FONT COLOR='maroon'>•Invalid receiving channel name.</FONT><BR>"
if(src.admincaster_feed_message.returnBody(-1) == "" || src.admincaster_feed_message.returnBody(-1) == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid message body.</FONT><BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[3]'>Return</A><BR>"
if(7)
dat+="<B><FONT COLOR='maroon'>ERROR: Could not submit Feed Channel to Network.</B></FONT><HR><BR>"
if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid channel name.</FONT><BR>"
var/check = 0
for(var/datum/newscaster/feed_channel/FC in news_network.network_channels)
if(FC.channel_name == src.admincaster_feed_channel.channel_name)
check = 1
break
if(check)
dat+="<FONT COLOR='maroon'>•Channel name already in use.</FONT><BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[2]'>Return</A><BR>"
if(9)
dat+="<B>[admincaster_feed_channel.channel_name]: </B><FONT SIZE=1>\[created by: <FONT COLOR='maroon'>[admincaster_feed_channel.returnAuthor(-1)]</FONT>\]</FONT><HR>"
if(src.admincaster_feed_channel.censored)
dat+="<FONT COLOR='red'><B>ATTENTION: </B></FONT>This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.<BR>"
dat+="No further feed story additions are allowed while the D-Notice is in effect.</FONT><BR><BR>"
else
if( isemptylist(src.admincaster_feed_channel.messages) )
dat+="<I>No feed messages found in channel...</I><BR>"
else
var/i = 0
for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
i++
dat+="-[MESSAGE.returnBody(-1)] <BR>"
if(MESSAGE.img)
usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
dat+="<img src='tmp_photo[i].png' width = '180'><BR><BR>"
dat+="<FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.returnAuthor(-1)]</FONT>\]</FONT><BR>"
dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]:<br>"
for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments)
dat+="[comment.body]<br><font size=1>[comment.author] [comment.time_stamp]</font><br>"
dat+="<br>"
dat+="<BR><HR><A href='?src=\ref[src];ac_refresh=1'>Refresh</A>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[1]'>Back</A>"
if(10)
dat+="<B>Nanotrasen Feed Censorship Tool</B><BR>"
dat+="<FONT SIZE=1>NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.<BR>"
dat+="Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.</FONT>"
dat+="<HR>Select Feed channel to get Stories from:<BR>"
if(isemptylist(news_network.network_channels))
dat+="<I>No feed channels found active...</I><BR>"
else
for(var/datum/newscaster/feed_channel/CHANNEL in news_network.network_channels)
dat+="<A href='?src=\ref[src];ac_pick_censor_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ()]<BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Cancel</A>"
if(11)
dat+="<B>Nanotrasen D-Notice Handler</B><HR>"
dat+="<FONT SIZE=1>A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's"
dat+="morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed"
dat+="stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.</FONT><HR>"
if(isemptylist(news_network.network_channels))
dat+="<I>No feed channels found active...</I><BR>"
else
for(var/datum/newscaster/feed_channel/CHANNEL in news_network.network_channels)
dat+="<A href='?src=\ref[src];ac_pick_d_notice=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ()]<BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Back</A>"
if(12)
dat+="<B>[src.admincaster_feed_channel.channel_name]: </B><FONT SIZE=1>\[ created by: <FONT COLOR='maroon'>[src.admincaster_feed_channel.returnAuthor(-1)]</FONT> \]</FONT><BR>"
dat+="<FONT SIZE=2><A href='?src=\ref[src];ac_censor_channel_author=\ref[src.admincaster_feed_channel]'>[(src.admincaster_feed_channel.authorCensor) ? ("Undo Author censorship") : ("Censor channel Author")]</A></FONT><HR>"
if( isemptylist(src.admincaster_feed_channel.messages) )
dat+="<I>No feed messages found in channel...</I><BR>"
else
for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
dat+="-[MESSAGE.returnBody(-1)] <BR><FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.returnAuthor(-1)]</FONT>\]</FONT><BR>"
dat+="<FONT SIZE=2><A href='?src=\ref[src];ac_censor_channel_story_body=\ref[MESSAGE]'>[(MESSAGE.bodyCensor) ? ("Undo story censorship") : ("Censor story")]</A> - <A href='?src=\ref[src];ac_censor_channel_story_author=\ref[MESSAGE]'>[(MESSAGE.authorCensor) ? ("Undo Author Censorship") : ("Censor message Author")]</A></FONT><BR>"
dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]: <a href='?src=\ref[src];ac_lock_comment=\ref[MESSAGE]'>[MESSAGE.locked ? "Unlock" : "Lock"]</a><br>"
for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments)
dat+="[comment.body] <a href='?src=\ref[src];ac_del_comment=\ref[comment];ac_del_comment_msg=\ref[MESSAGE]'>X</a><br><font size=1>[comment.author] [comment.time_stamp]</font><br>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[10]'>Back</A>"
if(13)
dat+="<B>[src.admincaster_feed_channel.channel_name]: </B><FONT SIZE=1>\[ created by: <FONT COLOR='maroon'>[src.admincaster_feed_channel.returnAuthor(-1)]</FONT> \]</FONT><BR>"
dat+="Channel messages listed below. If you deem them dangerous to the station, you can <A href='?src=\ref[src];ac_toggle_d_notice=\ref[src.admincaster_feed_channel]'>Bestow a D-Notice upon the channel</A>.<HR>"
if(src.admincaster_feed_channel.censored)
dat+="<FONT COLOR='red'><B>ATTENTION: </B></FONT>This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.<BR>"
dat+="No further feed story additions are allowed while the D-Notice is in effect.</FONT><BR><BR>"
else
if( isemptylist(src.admincaster_feed_channel.messages) )
dat+="<I>No feed messages found in channel...</I><BR>"
else
for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
dat+="-[MESSAGE.returnBody(-1)] <BR><FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.returnAuthor(-1)]</FONT>\]</FONT><BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[11]'>Back</A>"
if(14)
dat+="<B>Wanted Issue Handler:</B>"
var/wanted_already = 0
var/end_param = 1
if(news_network.wanted_issue.active)
wanted_already = 1
end_param = 2
if(wanted_already)
dat+="<FONT SIZE=2><BR><I>A wanted issue is already in Feed Circulation. You can edit or cancel it below.</FONT></I>"
dat+="<HR>"
dat+="<A href='?src=\ref[src];ac_set_wanted_name=1'>Criminal Name</A>: [src.admincaster_wanted_message.criminal] <BR>"
dat+="<A href='?src=\ref[src];ac_set_wanted_desc=1'>Description</A>: [src.admincaster_wanted_message.body] <BR>"
if(wanted_already)
dat+="<B>Wanted Issue created by:</B><FONT COLOR='green'>[news_network.wanted_issue.scannedUser]</FONT><BR>"
else
dat+="<B>Wanted Issue will be created under prosecutor:</B><FONT COLOR='green'>[src.admin_signature]</FONT><BR>"
dat+="<BR><A href='?src=\ref[src];ac_submit_wanted=[end_param]'>[(wanted_already) ? ("Edit Issue") : ("Submit")]</A>"
if(wanted_already)
dat+="<BR><A href='?src=\ref[src];ac_cancel_wanted=1'>Take down Issue</A>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Cancel</A>"
if(15)
dat+="<FONT COLOR='green'>Wanted issue for [src.admincaster_wanted_message.criminal] is now in Network Circulation.</FONT><BR><BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Return</A><BR>"
if(16)
dat+="<B><FONT COLOR='maroon'>ERROR: Wanted Issue rejected by Network.</B></FONT><HR><BR>"
if(src.admincaster_wanted_message.criminal =="" || src.admincaster_wanted_message.criminal == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid name for person wanted.</FONT><BR>"
if(src.admincaster_wanted_message.body == "" || src.admincaster_wanted_message.body == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid description.</FONT><BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Return</A><BR>"
if(17)
dat+="<B>Wanted Issue successfully deleted from Circulation</B><BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Return</A><BR>"
if(18)
dat+="<B><FONT COLOR ='maroon'>-- STATIONWIDE WANTED ISSUE --</B></FONT><BR><FONT SIZE=2>\[Submitted by: <FONT COLOR='green'>[news_network.wanted_issue.scannedUser]</FONT>\]</FONT><HR>"
dat+="<B>Criminal</B>: [news_network.wanted_issue.criminal]<BR>"
dat+="<B>Description</B>: [news_network.wanted_issue.body]<BR>"
dat+="<B>Photo:</B>: "
if(news_network.wanted_issue.img)
usr << browse_rsc(news_network.wanted_issue.img, "tmp_photow.png")
dat+="<BR><img src='tmp_photow.png' width = '180'>"
else
dat+="None"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Back</A><BR>"
if(19)
dat+="<FONT COLOR='green'>Wanted issue for [src.admincaster_wanted_message.criminal] successfully edited.</FONT><BR><BR>"
dat+="<BR><A href='?src=\ref[src];ac_setScreen=[0]'>Return</A><BR>"
else
dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com"
//world << "Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]"
//world << "Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]"
usr << browse(dat, "window=admincaster_main;size=400x600")
onclose(usr, "admincaster_main")
/datum/admins/proc/Game()
if(!check_rights(0))
return
var/dat = {"
<center><B>Game Panel</B></center><hr>\n
<A href='?src=\ref[src];c_mode=1'>Change Game Mode</A><br>
"}
if(master_mode == "secret")
dat += "<A href='?src=\ref[src];f_secret=1'>(Force Secret Mode)</A><br>"
dat += {"
<BR>
<A href='?src=\ref[src];create_object=1'>Create Object</A><br>
<A href='?src=\ref[src];quick_create_object=1'>Quick Create Object</A><br>
<A href='?src=\ref[src];create_turf=1'>Create Turf</A><br>
<A href='?src=\ref[src];create_mob=1'>Create Mob</A><br>
"}
if(marked_datum && istype(marked_datum, /atom))
dat += "<A href='?src=\ref[src];dupe_marked_datum=1'>Duplicate Marked Datum</A><br>"
usr << browse(dat, "window=admin2;size=210x200")
return
/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge
//i.e. buttons/verbs
/datum/admins/proc/restart()
set category = "Server"
set name = "Hard Restart"
set desc="Restarts the world immediately"
if (!usr.client.holder)
return
var/confirm = alert("Restart the game world?", "Restart", "Yes", "Cancel")
if(confirm == "Cancel")
return
if(confirm == "Yes")
ticker.delay_end = 0
feedback_add_details("admin_verb","R") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
world.Reboot("Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key].", "end_error", "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10)
/datum/admins/proc/end_round()
set category = "Server"
set name = "End Round"
set desc = "Attempts to produce a round end report and then restart the server organically."
if (!usr.client.holder)
return
var/confirm = alert("End the round and restart the game world?", "End Round", "Yes", "Cancel")
if(confirm == "Cancel")
return
if(confirm == "Yes")
ticker.force_ending = 1
feedback_add_details("admin_verb","ER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/announce()
set category = "Special Verbs"
set name = "Announce"
set desc="Announce your desires to the world"
if(!check_rights(0))
return
var/message = input("Global message to send:", "Admin Announce", null, null) as message
if(message)
if(!check_rights(R_SERVER,0))
message = adminscrub(message,500)
world << "<span class='adminnotice'><b>[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:</b></span>\n \t [message]"
log_admin("Announce: [key_name(usr)] : [message]")
feedback_add_details("admin_verb","A") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/set_admin_notice()
set category = "Special Verbs"
set name = "Set Admin Notice"
set desc ="Set an announcement that appears to everyone who joins the server. Only lasts this round"
if(!check_rights(0))
return
var/new_admin_notice = input(src,"Set a public notice for this round. Everyone who joins the server will see it.\n(Leaving it blank will delete the current notice):","Set Notice",admin_notice) as message|null
if(new_admin_notice == null)
return
if(new_admin_notice == admin_notice)
return
if(new_admin_notice == "")
message_admins("[key_name(usr)] removed the admin notice.")
log_admin("[key_name(usr)] removed the admin notice:\n[admin_notice]")
else
message_admins("[key_name(usr)] set the admin notice.")
log_admin("[key_name(usr)] set the admin notice:\n[new_admin_notice]")
world << "<span class ='adminnotice'><b>Admin Notice:</b>\n \t [new_admin_notice]</span>"
feedback_add_details("admin_verb","SAN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
admin_notice = new_admin_notice
return
/datum/admins/proc/toggleooc()
set category = "Server"
set desc="Toggle dis bitch"
set name="Toggle OOC"
toggle_ooc()
log_admin("[key_name(usr)] toggled OOC.")
message_admins("[key_name_admin(usr)] toggled OOC.")
feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleoocdead()
set category = "Server"
set desc="Toggle dis bitch"
set name="Toggle Dead OOC"
dooc_allowed = !( dooc_allowed )
log_admin("[key_name(usr)] toggled OOC.")
message_admins("[key_name_admin(usr)] toggled Dead OOC.")
feedback_add_details("admin_verb","TDOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/*
/datum/admins/proc/toggletraitorscaling()
set category = "Server"
set desc="Toggle traitor scaling"
set name="Toggle Traitor Scaling"
traitor_scaling = !traitor_scaling
log_admin("[key_name(usr)] toggled Traitor Scaling to [traitor_scaling].")
message_admins("[key_name_admin(usr)] toggled Traitor Scaling [traitor_scaling ? "on" : "off"].")
feedback_add_details("admin_verb","TTS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
*/
/datum/admins/proc/startnow()
set category = "Server"
set desc="Start the round RIGHT NOW"
set name="Start Now"
if(ticker.current_state == GAME_STATE_PREGAME)
ticker.can_fire = 1
ticker.timeLeft = 0
log_admin("[usr.key] has started the game.")
message_admins("<font color='blue'>[usr.key] has started the game.</font>")
feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return 1
else if (ticker.current_state == GAME_STATE_STARTUP)
usr << "<font color='red'>Error: Start Now: Game is in startup, please wait until it has finished.</font>"
else
usr << "<font color='red'>Error: Start Now: Game has already started.</font>"
return 0
/datum/admins/proc/toggleenter()
set category = "Server"
set desc="People can't enter"
set name="Toggle Entering"
enter_allowed = !( enter_allowed )
if (!( enter_allowed ))
world << "<B>New players may no longer enter the game.</B>"
else
world << "<B>New players may now enter the game.</B>"
log_admin("[key_name(usr)] toggled new player game entering.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled new player game entering.</span>")
world.update_status()
feedback_add_details("admin_verb","TE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleAI()
set category = "Server"
set desc="People can't be AI"
set name="Toggle AI"
config.allow_ai = !( config.allow_ai )
if (!( config.allow_ai ))
world << "<B>The AI job is no longer chooseable.</B>"
else
world << "<B>The AI job is chooseable now.</B>"
log_admin("[key_name(usr)] toggled AI allowed.")
world.update_status()
feedback_add_details("admin_verb","TAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleaban()
set category = "Server"
set desc="Respawn basically"
set name="Toggle Respawn"
abandon_allowed = !( abandon_allowed )
if (abandon_allowed)
world << "<B>You may now respawn.</B>"
else
world << "<B>You may no longer respawn :(</B>"
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled respawn to [abandon_allowed ? "On" : "Off"].</span>")
log_admin("[key_name(usr)] toggled respawn to [abandon_allowed ? "On" : "Off"].")
world.update_status()
feedback_add_details("admin_verb","TR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/delay()
set category = "Server"
set desc="Delay the game start"
set name="Delay pre-game"
var/newtime = input("Set a new time in seconds. Set -1 for indefinite delay.","Set Delay",round(ticker.timeLeft/10)) as num|null
if(ticker.current_state > GAME_STATE_PREGAME)
return alert("Too late... The game has already started!")
if(newtime)
ticker.timeLeft = newtime * 10
if(newtime < 0)
world << "<b>The game start has been delayed.</b>"
log_admin("[key_name(usr)] delayed the round start.")
else
world << "<b>The game will start in [newtime] seconds.</b>"
world << 'sound/ai/attention.ogg'
log_admin("[key_name(usr)] set the pre-game delay to [newtime] seconds.")
feedback_add_details("admin_verb","DELAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/unprison(mob/M in mob_list)
set category = "Admin"
set name = "Unprison"
if (M.z == ZLEVEL_CENTCOM)
M.loc = pick(latejoin)
message_admins("[key_name_admin(usr)] has unprisoned [key_name_admin(M)]")
log_admin("[key_name(usr)] has unprisoned [key_name(M)]")
else
alert("[M.name] is not prisoned.")
feedback_add_details("admin_verb","UP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
////////////////////////////////////////////////////////////////////////////////////////////////ADMIN HELPER PROCS
/*
/datum/admins/proc/get_sab_desc(var/target)
switch(target)
if(1)
return "Destroy at least 70% of the plasma canisters on the station"
if(2)
return "Destroy the AI"
if(3)
var/count = 0
for(var/mob/living/carbon/monkey/Monkey in world)
if(Monkey.z == 1)
count++
return "Kill all [count] of the monkeys on the station"
if(4)
return "Cut power to at least 80% of the station"
else
return "Error: Invalid sabotage target: [target]"
*/
/datum/admins/proc/spawn_atom(object as text)
set category = "Debug"
set desc = "(atom path) Spawn an atom"
set name = "Spawn"
if(!check_rights(R_SPAWN))
return
var/chosen = pick_closest_path(object)
if(!chosen)
return
if(ispath(chosen,/turf))
var/turf/T = get_turf(usr.loc)
T.ChangeTurf(chosen)
else
new chosen(usr.loc)
log_admin("[key_name(usr)] spawned [chosen] at ([usr.x],[usr.y],[usr.z])")
feedback_add_details("admin_verb","SA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/show_traitor_panel(mob/M in mob_list)
set category = "Admin"
set desc = "Edit mobs's memory and role"
set name = "Show Traitor Panel"
if(!istype(M))
usr << "This can only be used on instances of type /mob"
return
if(!M.mind)
usr << "This mob has no mind!"
return
M.mind.edit_memory()
feedback_add_details("admin_verb","STP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggletintedweldhelmets()
set category = "Debug"
set desc="Reduces view range when wearing welding helmets"
set name="Toggle tinted welding helmes"
tinted_weldhelh = !( tinted_weldhelh )
if (tinted_weldhelh)
world << "<B>The tinted_weldhelh has been enabled!</B>"
else
world << "<B>The tinted_weldhelh has been disabled!</B>"
log_admin("[key_name(usr)] toggled tinted_weldhelh.")
message_admins("[key_name_admin(usr)] toggled tinted_weldhelh.")
feedback_add_details("admin_verb","TTWH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleguests()
set category = "Server"
set desc="Guests can't enter"
set name="Toggle guests"
guests_allowed = !( guests_allowed )
if (!( guests_allowed ))
world << "<B>Guests may no longer enter the game.</B>"
else
world << "<B>Guests may now enter the game.</B>"
log_admin("[key_name(usr)] toggled guests game entering [guests_allowed?"":"dis"]allowed.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled guests game entering [guests_allowed?"":"dis"]allowed.</span>")
feedback_add_details("admin_verb","TGU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/output_ai_laws()
var/ai_number = 0
for(var/mob/living/silicon/S in mob_list)
ai_number++
if(isAI(S))
usr << "<b>AI [key_name(S, usr)]'s laws:</b>"
else if(isrobot(S))
var/mob/living/silicon/robot/R = S
usr << "<b>CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [R.connected_ai])":"(Independant)"]: laws:</b>"
else if (ispAI(S))
usr << "<b>pAI [key_name(S, usr)]'s laws:</b>"
else
usr << "<b>SOMETHING SILICON [key_name(S, usr)]'s laws:</b>"
if (S.laws == null)
usr << "[key_name(S, usr)]'s laws are null?? Contact a coder."
else
S.laws.show_laws(usr)
if(!ai_number)
usr << "<b>No AIs located</b>" //Just so you know the thing is actually working and not just ignoring you.
/datum/admins/proc/output_devil_info()
var/devil_number = 0
for(var/D in ticker.mode.devils)
devil_number++
usr << "Devil #[devil_number]:<br><br>" + ticker.mode.printdevilinfo(D)
if(!devil_number)
usr << "<b>No Devils located</b>" //Just so you know the thing is actually working and not just ignoring you.
/datum/admins/proc/manage_free_slots()
if(!check_rights())
return
var/dat = "<html><head><title>Manage Free Slots</title></head><body>"
var/count = 0
if(ticker && !ticker.mode)
alert(usr, "You cannot manage jobs before the round starts!")
return
if(SSjob)
for(var/datum/job/job in SSjob.occupations)
count++
var/J_title = html_encode(job.title)
var/J_opPos = html_encode(job.total_positions - (job.total_positions - job.current_positions))
var/J_totPos = html_encode(job.total_positions)
if(job.total_positions < 0)
dat += "[J_title]: [J_opPos] (unlimited)"
else
dat += "[J_title]: [J_opPos]/[J_totPos]"
if(job.title == "AI" || job.title == "Cyborg")
dat += " (Cannot Late Join)<br>"
continue
if(job.total_positions >= 0)
dat += " <A href='?src=\ref[src];addjobslot=[job.title]'>Add</A> | "
if(job.total_positions > job.current_positions)
dat += "<A href='?src=\ref[src];removejobslot=[job.title]'>Remove</A> | "
else
dat += "Remove | "
dat += "<A href='?src=\ref[src];unlimitjobslot=[job.title]'>Unlimit</A>"
else
dat += " <A href='?src=\ref[src];limitjobslot=[job.title]'>Limit</A>"
dat += "<br>"
dat += "</body>"
var/winheight = 100 + (count * 20)
winheight = min(winheight, 690)
usr << browse(dat, "window=players;size=375x[winheight]")
//
//
//ALL DONE
//*********************************************************************************************************
//TO-DO:
//
//
//RIP ferry snowflakes
//Kicks all the clients currently in the lobby. The second parameter (kick_only_afk) determins if an is_afk() check is ran, or if all clients are kicked
//defaults to kicking everyone (afk + non afk clients in the lobby)
//returns a list of ckeys of the kicked clients
/proc/kick_clients_in_lobby(message, kick_only_afk = 0)
var/list/kicked_client_names = list()
for(var/client/C in clients)
if(istype(C.mob, /mob/new_player))
if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk
continue
if(message)
C << message
kicked_client_names.Add("[C.ckey]")
del(C)
return kicked_client_names
//returns 1 to let the dragdrop code know we are trapping this event
//returns 0 if we don't plan to trap the event
/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, mob/living/tomob)
//this is the exact two check rights checks required to edit a ckey with vv.
if (!check_rights(R_VAREDIT,0) || !check_rights(R_SPAWN|R_DEBUG,0))
return 0
if (!frommob.ckey)
return 0
var/question = ""
if (tomob.ckey)
question = "This mob already has a user ([tomob.key]) in control of it! "
question += "Are you sure you want to place [frommob.name]([frommob.key]) in control of [tomob.name]?"
var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No")
if (ask != "Yes")
return 1
if (!frommob || !tomob) //make sure the mobs don't go away while we waited for a response
return 1
tomob.ghostize(0)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].</span>")
log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].")
feedback_add_details("admin_verb","CGD")
tomob.ckey = frommob.ckey
qdel(frommob)
return 1
/client/proc/adminGreet(logout)
if(ticker && ticker.current_state == GAME_STATE_PLAYING)
var/string
if(logout && config && config.announce_admin_logout)
string = pick(
"Admin logout: [key_name(src)]")
else if(!logout && config && config.announce_admin_login && (prefs.toggles & ANNOUNCE_LOGIN))
string = pick(
"Admin login: [key_name(src)]")
if(string)
message_admins("[string]")
+54
View File
@@ -0,0 +1,54 @@
//By Carnwennan
//This system was made as an alternative to all the in-game lists and variables used to log stuff in-game.
//lists and variables are great. However, they have several major flaws:
//Firstly, they use memory. TGstation has one of the highest memory usage of all the ss13 branches.
//Secondly, they are usually stored in an object. This means that they aren't centralised. It also means that
//the data is lost when the object is deleted! This is especially annoying for things like the singulo engine!
#define INVESTIGATE_DIR "data/investigate/"
//SYSTEM
/proc/investigate_subject2file(subject)
return file("[INVESTIGATE_DIR][subject].html")
/proc/investigate_reset()
if(fdel(INVESTIGATE_DIR))
return 1
return 0
/atom/proc/investigate_log(message, subject)
if(!message)
return
var/F = investigate_subject2file(subject)
if(!F)
return
F << "<small>[time_stamp()] \ref[src] ([x],[y],[z])</small> || [src] [message]<br>"
//ADMINVERBS
/client/proc/investigate_show( subject in list("hrefs","notes","watchlist","singulo","wires","telesci", "gravity", "records", "cargo", "supermatter", "atmos", "experimentor", "kudzu") )
set name = "Investigate"
set category = "Admin"
if(!holder)
return
switch(subject)
if("singulo", "wires", "telesci", "gravity", "records", "cargo", "supermatter", "atmos", "kudzu") //general one-round-only stuff
var/F = investigate_subject2file(subject)
if(!F)
src << "<font color='red'>Error: admin_investigate: [INVESTIGATE_DIR][subject] is an invalid path or cannot be accessed.</font>"
return
src << browse(F,"window=investigate[subject];size=800x300")
if("hrefs") //persistant logs and stuff
if(config && config.log_hrefs)
if(href_logfile)
src << browse(href_logfile,"window=investigate[subject];size=800x300")
else
src << "<font color='red'>Error: admin_investigate: No href logfile found.</font>"
return
else
src << "<font color='red'>Error: admin_investigate: Href Logging is not on.</font>"
return
if("notes")
show_note()
if("watchlist")
watchlist_show()
+131
View File
@@ -0,0 +1,131 @@
/client/proc/admin_memo()
set name = "Memo"
set category = "Server"
if(!check_rights(0))
return
if(!dbcon.IsConnected())
src << "<span class='danger'>Failed to establish database connection.</span>"
return
var/memotask = input(usr,"Choose task.","Memo") in list("Show","Write","Edit","Remove")
if(!memotask)
return
admin_memo_output(memotask)
/client/proc/admin_memo_output(task)
if(!task)
return
if(!dbcon.IsConnected())
src << "<span class='danger'>Failed to establish database connection.</span>"
return
var/sql_ckey = sanitizeSQL(src.ckey)
switch(task)
if("Write")
var/DBQuery/query_memocheck = dbcon.NewQuery("SELECT ckey FROM [format_table_name("memo")] WHERE ckey = '[sql_ckey]'")
if(!query_memocheck.Execute())
var/err = query_memocheck.ErrorMsg()
log_game("SQL ERROR obtaining ckey from memo table. Error : \[[err]\]\n")
return
if(query_memocheck.NextRow())
src << "You already have set a memo."
return
var/memotext = input(src,"Write your Memo","Memo") as message
if(!memotext)
return
memotext = sanitizeSQL(memotext)
var/timestamp = SQLtime()
var/DBQuery/query_memoadd = dbcon.NewQuery("INSERT INTO [format_table_name("memo")] (ckey, memotext, timestamp) VALUES ('[sql_ckey]', '[memotext]', '[timestamp]')")
if(!query_memoadd.Execute())
var/err = query_memoadd.ErrorMsg()
log_game("SQL ERROR adding new memo. Error : \[[err]\]\n")
return
log_admin("[key_name(src)] has set a memo: [memotext]")
message_admins("[key_name_admin(src)] has set a memo:<br>[memotext]")
if("Edit")
var/DBQuery/query_memolist = dbcon.NewQuery("SELECT ckey FROM [format_table_name("memo")]")
if(!query_memolist.Execute())
var/err = query_memolist.ErrorMsg()
log_game("SQL ERROR obtaining ckey from memo table. Error : \[[err]\]\n")
return
var/list/memolist = list()
while(query_memolist.NextRow())
var/lkey = query_memolist.item[1]
memolist += "[lkey]"
if(!memolist.len)
src << "No memos found in database."
return
var/target_ckey = input(src, "Select whose memo to edit", "Select memo") as null|anything in memolist
if(!target_ckey)
return
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/DBQuery/query_memofind = dbcon.NewQuery("SELECT memotext FROM [format_table_name("memo")] WHERE ckey = '[target_sql_ckey]'")
if(!query_memofind.Execute())
var/err = query_memofind.ErrorMsg()
log_game("SQL ERROR obtaining memotext from memo table. Error : \[[err]\]\n")
return
if(query_memofind.NextRow())
var/old_memo = query_memofind.item[1]
var/new_memo = input("Input new memo", "New Memo", "[old_memo]", null) as message
if(!new_memo)
return
new_memo = sanitizeSQL(new_memo)
var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from<br>[old_memo]<br>to<br>[new_memo]<hr>"
edit_text = sanitizeSQL(edit_text)
var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("memo")] SET memotext = '[new_memo]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE ckey = '[target_sql_ckey]'")
if(!update_query.Execute())
var/err = update_query.ErrorMsg()
log_game("SQL ERROR editing memo. Error : \[[err]\]\n")
return
if(target_sql_ckey == sql_ckey)
log_admin("[key_name(src)] has edited their memo from [old_memo] to [new_memo]")
message_admins("[key_name_admin(src)] has edited their memo from<br>[old_memo]<br>to<br>[new_memo]")
else
log_admin("[key_name(src)] has edited [target_sql_ckey]'s memo from [old_memo] to [new_memo]")
message_admins("[key_name_admin(src)] has edited [target_sql_ckey]'s memo from<br>[old_memo]<br>to<br>[new_memo]")
if("Show")
var/DBQuery/query_memoshow = dbcon.NewQuery("SELECT ckey, memotext, timestamp, last_editor FROM [format_table_name("memo")]")
if(!query_memoshow.Execute())
var/err = query_memoshow.ErrorMsg()
log_game("SQL ERROR obtaining ckey, memotext, timestamp, last_editor from memo table. Error : \[[err]\]\n")
return
var/output = null
while(query_memoshow.NextRow())
var/ckey = query_memoshow.item[1]
var/memotext = query_memoshow.item[2]
var/timestamp = query_memoshow.item[3]
var/last_editor = query_memoshow.item[4]
output += "<span class='memo'>Memo by <span class='prefix'>[ckey]</span> on [timestamp]"
if(last_editor)
output += "<br><span class='memoedit'>Last edit by [last_editor] <A href='?_src_=holder;memoeditlist=[ckey]'>(Click here to see edit log)</A></span>"
output += "<br>[memotext]</span><br>"
if(!output)
src << "No memos found in database."
return
src << output
if("Remove")
var/DBQuery/query_memodellist = dbcon.NewQuery("SELECT ckey FROM [format_table_name("memo")]")
if(!query_memodellist.Execute())
var/err = query_memodellist.ErrorMsg()
log_game("SQL ERROR obtaining ckey from memo table. Error : \[[err]\]\n")
return
var/list/memolist = list()
while(query_memodellist.NextRow())
var/ckey = query_memodellist.item[1]
memolist += "[ckey]"
if(!memolist.len)
src << "No memos found in database."
return
var/target_ckey = input(src, "Select whose memo to delete", "Select memo") as null|anything in memolist
if(!target_ckey)
return
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/DBQuery/query_memodel = dbcon.NewQuery("DELETE FROM [format_table_name("memo")] WHERE ckey = '[target_sql_ckey]'")
if(!query_memodel.Execute())
var/err = query_memodel.ErrorMsg()
log_game("SQL ERROR removing memo. Error : \[[err]\]\n")
return
if(target_sql_ckey == sql_ckey)
log_admin("[key_name(src)] has removed their memo.")
message_admins("[key_name_admin(src)] has removed their memo.")
else
log_admin("[key_name(src)] has removed [target_sql_ckey]'s memo.")
message_admins("[key_name_admin(src)] has removed [target_sql_ckey]'s memo.")
+372
View File
@@ -0,0 +1,372 @@
var/list/admin_ranks = list() //list of all admin_rank datums
/datum/admin_rank
var/name = "NoRank"
var/rights = 0
var/list/adds
var/list/subs
/datum/admin_rank/New(init_name, init_rights, list/init_adds, list/init_subs)
name = init_name
switch(name)
if("Removed",null,"")
spawn(0)
qdel(src)
throw EXCEPTION("invalid admin-rank name")
return
if(init_rights)
rights = init_rights
if(!init_adds)
init_adds = list()
if(!init_subs)
init_subs = list()
adds = init_adds
subs = init_subs
/proc/admin_keyword_to_flag(word, previous_rights=0)
var/flag = 0
switch(ckey(word))
if("buildmode","build")
flag = R_BUILDMODE
if("admin")
flag = R_ADMIN
if("ban")
flag = R_BAN
if("fun")
flag = R_FUN
if("server")
flag = R_SERVER
if("debug")
flag = R_DEBUG
if("permissions","rights")
flag = R_PERMISSIONS
if("possess")
flag = R_POSSESS
if("stealth")
flag = R_STEALTH
if("rejuv","rejuvinate")
flag = R_REJUVINATE
if("varedit")
flag = R_VAREDIT
if("everything","host","all")
flag = 65535
if("sound","sounds")
flag = R_SOUNDS
if("spawn","create")
flag = R_SPAWN
if("@","prev")
flag = previous_rights
return flag
/proc/admin_keyword_to_path(word) //use this with verb keywords eg +/client/proc/blah
return text2path(copytext(word, 2, findtext(word, " ", 2, 0)))
// Adds/removes rights to this admin_rank
/datum/admin_rank/proc/process_keyword(word, previous_rights=0)
var/flag = admin_keyword_to_flag(word, previous_rights)
if(flag)
switch(text2ascii(word,1))
if(43)
rights |= flag //+
if(45)
rights &= ~flag //-
else
//isn't a keyword so maybe it's a verbpath?
var/path = admin_keyword_to_path(word)
if(path)
switch(text2ascii(word,1))
if(43)
if(!subs.Remove(path))
adds += path //+
if(45)
if(!adds.Remove(path))
subs += path //-
// Checks for (keyword-formatted) rights on this admin
/datum/admins/proc/check_keyword(word)
var/flag = admin_keyword_to_flag(word)
if(flag)
return ((rank.rights & flag) == flag) //true only if right has everything in flag
else
var/path = admin_keyword_to_path(word)
for(var/i in owner.verbs) //this needs to be a foreach loop for some reason. in operator and verbs.Find() don't work
if(i == path)
return 1
return 0
//load our rank - > rights associations
/proc/load_admin_ranks()
admin_ranks.Cut()
if(config.admin_legacy_system)
var/previous_rights = 0
//load text from file and process each line seperately
for(var/line in file2list("config/admin_ranks.txt"))
if(!line)
continue
if(findtextEx(line,"#",1,2))
continue
var/next = findtext(line, "=")
var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next)))
if(!R)
continue
admin_ranks += R
var/prev = findchar(line, "+-", next, 0)
while(prev)
next = findchar(line, "+-", prev + 1, 0)
R.process_keyword(copytext(line, prev, next), previous_rights)
prev = next
previous_rights = R.rights
else
establish_db_connection()
if(!dbcon.IsConnected())
world.log << "Failed to connect to database in load_admin_ranks(). Reverting to legacy system."
diary << "Failed to connect to database in load_admin_ranks(). Reverting to legacy system."
config.admin_legacy_system = 1
load_admin_ranks()
return
var/DBQuery/query = dbcon.NewQuery("SELECT rank, flags FROM [format_table_name("admin_ranks")]")
query.Execute()
while(query.NextRow())
var/rank_name = ckeyEx(query.item[1])
var/flags = query.item[2]
if(istext(flags))
flags = text2num(flags)
var/datum/admin_rank/R = new(rank_name, flags)
if(!R)
continue
admin_ranks += R
#ifdef TESTING
var/msg = "Permission Sets Built:\n"
for(var/datum/admin_rank/R in admin_ranks)
msg += "\t[R.name]"
var/rights = rights2text(R.rights,"\n\t\t",R.adds,R.subs)
if(rights)
msg += "\t\t[rights]\n"
testing(msg)
#endif
/proc/load_admins(target = null)
//clear the datums references
if(!target)
admin_datums.Cut()
for(var/client/C in admins)
C.remove_admin_verbs()
C.holder = null
admins.Cut()
load_admin_ranks()
var/list/rank_names = list()
for(var/datum/admin_rank/R in admin_ranks)
rank_names[R.name] = R
if(config.admin_legacy_system)
//load text from file
var/list/lines = file2list("config/admins.txt")
//process each line seperately
for(var/line in lines)
if(!length(line))
continue
if(findtextEx(line, "#", 1, 2))
continue
var/list/entry = splittext(line, "=")
if(entry.len < 2)
continue
var/ckey = ckey(entry[1])
var/rank = ckeyEx(entry[2])
if(!ckey || !rank || (target && ckey != target))
continue
var/datum/admins/D = new(rank_names[rank], ckey) //create the admin datum and store it for later use
if(!D)
continue //will occur if an invalid rank is provided
D.associate(directory[ckey]) //find the client for a ckey if they are connected and associate them with the new admin datum
else
establish_db_connection()
if(!dbcon.IsConnected())
world.log << "Failed to connect to database in load_admins(). Reverting to legacy system."
diary << "Failed to connect to database in load_admins(). Reverting to legacy system."
config.admin_legacy_system = 1
load_admins()
return
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank FROM [format_table_name("admin")]")
query.Execute()
while(query.NextRow())
var/ckey = ckey(query.item[1])
var/rank = ckeyEx(query.item[2])
if(target && ckey != target)
continue
if(rank_names[rank] == null)
WARNING("Admin rank ([rank]) does not exist.")
continue
var/datum/admins/D = new(rank_names[rank], ckey) //create the admin datum and store it for later use
if(!D)
continue //will occur if an invalid rank is provided
D.associate(directory[ckey]) //find the client for a ckey if they are connected and associate them with the new admin datum
#ifdef TESTING
var/msg = "Admins Built:\n"
for(var/ckey in admin_datums)
var/datum/admins/D = admin_datums[ckey]
msg += "\t[ckey] - [D.rank.name]\n"
testing(msg)
#endif
#ifdef TESTING
/client/verb/changerank(newrank in admin_ranks)
if(holder)
holder.rank = newrank
else
holder = new /datum/admins(newrank, ckey)
remove_admin_verbs()
holder.associate(src)
/client/verb/changerights(newrights as num)
if(holder)
holder.rank.rights = newrights
else
holder = new /datum/admins("testing", newrights, ckey)
remove_admin_verbs()
holder.associate(src)
#endif
/datum/admins/proc/edit_rights_topic(list/href_list)
if(!check_rights(R_PERMISSIONS))
message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.")
log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.")
return
var/adm_ckey
var/task = href_list["editrights"]
switch(task)
if("add")
var/new_ckey = ckey(input(usr,"New admin's ckey","Admin ckey", null) as text|null)
if(!new_ckey)
return
if(new_ckey in admin_datums)
usr << "<font color='red'>Error: Topic 'editrights': [new_ckey] is already an admin</font>"
return
adm_ckey = new_ckey
task = "rank"
else
adm_ckey = ckey(href_list["ckey"])
if(!adm_ckey)
usr << "<font color='red'>Error: Topic 'editrights': No valid ckey</font>"
return
var/datum/admins/D = admin_datums[adm_ckey]
switch(task)
if("remove")
if(alert("Are you sure you want to remove [adm_ckey]?","Message","Yes","Cancel") == "Yes")
if(!D)
return
if(!check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to remove [adm_ckey] from the admins list without sufficient rights.")
log_admin("[key_name(usr)] attempted to remove [adm_ckey] from the admins list without sufficient rights.")
return
admin_datums -= adm_ckey
D.disassociate()
updateranktodb(adm_ckey, "player")
message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list")
log_admin("[key_name(usr)] removed [adm_ckey] from the admins list")
log_admin_rank_modification(adm_ckey, "Removed")
if("rank")
var/datum/admin_rank/R
var/list/rank_names = list("*New Rank*")
for(R in admin_ranks)
rank_names[R.name] = R
var/new_rank = input("Please select a rank", "New rank", null, null) as null|anything in rank_names
switch(new_rank)
if(null)
return
if("*New Rank*")
new_rank = ckeyEx(input("Please input a new rank", "New custom rank", null, null) as null|text)
if(!new_rank)
return
if(D)
if(!check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to change the rank of [adm_ckey] to [new_rank] without sufficient rights.")
log_admin("[key_name(usr)] attempted to change the rank of [adm_ckey] to [new_rank] without sufficient rights.")
return
R = rank_names[new_rank]
if(!R) //rank with that name doesn't exist yet - make it
if(D)
R = new(new_rank, D.rank.rights, D.rank.adds, D.rank.subs) //duplicate our previous admin_rank but with a new name
else
R = new(new_rank) //blank new admin_rank
admin_ranks += R
if(D) //they were previously an admin
D.disassociate() //existing admin needs to be disassociated
D.rank = R //set the admin_rank as our rank
else
D = new(R,adm_ckey) //new admin
var/client/C = directory[adm_ckey] //find the client with the specified ckey (if they are logged in)
D.associate(C) //link up with the client and add verbs
updateranktodb(adm_ckey, new_rank)
message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
log_admin_rank_modification(adm_ckey, new_rank)
if("permissions")
if(!D)
return //they're not an admin!
var/keyword = input("Input permission keyword (one at a time):\ne.g. +BAN or -FUN or +/client/proc/someverb", "Permission toggle", null, null) as null|text
if(!keyword)
return
if(!check_keyword(keyword) || !check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to give [adm_ckey] the keyword [keyword] without sufficient rights.")
log_admin("[key_name(usr)] attempted to give [adm_ckey] the keyword [keyword] without sufficient rights.")
return
D.disassociate()
if(!findtext(D.rank.name, "([adm_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too
D.rank = new("[D.rank.name]([adm_ckey])", D.rank.rights, D.rank.adds, D.rank.subs) //duplicate our previous admin_rank but with a new name
//we don't add this clone to the admin_ranks list, as it is unique to that ckey
D.rank.process_keyword(keyword)
var/client/C = directory[adm_ckey] //find the client with the specified ckey (if they are logged in)
D.associate(C) //link up with the client and add verbs
message_admins("[key_name(usr)] added keyword [keyword] to permission of [adm_ckey]")
log_admin("[key_name(usr)] added keyword [keyword] to permission of [adm_ckey]")
log_admin_permission_modification(adm_ckey, D.rank.rights)
edit_admin_permissions()
/datum/admins/proc/updateranktodb(ckey,newrank)
establish_db_connection()
if (!dbcon.IsConnected())
return
var/sql_ckey = sanitizeSQL(ckey)
var/sql_admin_rank = sanitizeSQL(newrank)
var/DBQuery/query_update = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'")
query_update.Execute()
+668
View File
@@ -0,0 +1,668 @@
//admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless
var/list/admin_verbs_default = list(
/client/proc/toggleadminhelpsound, /*toggles whether we hear a sound when adminhelps/PMs are used*/
/client/proc/toggleannouncelogin, /*toggles if an admin's login is announced during a round*/
/client/proc/deadmin, /*destroys our own admin datum so we can play as a regular player*/
/client/proc/cmd_admin_say, /*admin-only ooc chat*/
/client/proc/hide_verbs, /*hides all our adminverbs*/
/client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/
/client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
/client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/
/client/proc/deadchat, /*toggles deadchat on/off*/
/client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/
/client/proc/toggleprayers, /*toggles prayers on/off*/
/client/verb/toggleprayersounds, /*Toggles prayer sounds (HALLELUJAH!)*/
/client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/
/client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/
/client/proc/secrets,
/client/proc/reload_admins,
/client/proc/reestablish_db_connection,/*reattempt a connection to the database*/
/client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
/client/proc/stop_sounds
)
var/list/admin_verbs_admin = list(
/client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/
/client/proc/invisimin, /*allows our mob to go invisible/visible*/
// /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage
/datum/admins/proc/show_player_panel, /*shows an interface for individual players, with various links (links require additional flags*/
/client/proc/game_panel, /*game panel, allows to change game-mode etc*/
/client/proc/check_ai_laws, /*shows AI and borg laws*/
/datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/
/datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/
/datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
/datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/
/datum/admins/proc/announce, /*priority announce something to all clients.*/
/datum/admins/proc/set_admin_notice,/*announcement all clients see when joining the server.*/
/client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/
/client/proc/toggle_view_range, /*changes how far we can see*/
/datum/admins/proc/view_txt_log, /*shows the server log (diary) for today*/
/datum/admins/proc/view_atk_log, /*shows the server combat-log, doesn't do anything presently*/
/client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
/client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/
/client/proc/check_antagonists, /*shows all antags*/
/datum/admins/proc/access_news_network, /*allows access of newscasters*/
/client/proc/giveruntimelog, /*allows us to give access to runtime logs to somebody*/
/client/proc/getruntimelog, /*allows us to access runtime logs to somebody*/
/client/proc/getserverlog, /*allows us to fetch server logs (diary) for other days*/
/client/proc/jumptocoord, /*we ghost and jump to a coordinate*/
/client/proc/Getmob, /*teleports a mob to our location*/
/client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/
// /client/proc/sendmob, /*sends a mob somewhere*/ -Removed due to it needing two sorting procs to work, which were executed every time an admin right-clicked. ~Errorage
/client/proc/jumptoarea,
/client/proc/jumptokey, /*allows us to jump to the location of a mob with a certain ckey*/
/client/proc/jumptomob, /*allows us to jump to a specific mob*/
/client/proc/jumptoturf, /*allows us to jump to a specific turf*/
/client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/
/client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcom*/
/client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/
/client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/
/client/proc/cmd_admin_local_narrate, /*sends text to all mobs within view of atom*/
/client/proc/cmd_admin_create_centcom_report,
/client/proc/cmd_change_command_name,
/client/proc/toggle_antag_hud, /*toggle display of the admin antag hud*/
/client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/
/client/proc/customiseSNPC, /* Customise any interactive crewmembers in the world */
/client/proc/resetSNPC, /* Resets any interactive crewmembers in the world */
/client/proc/toggleSNPC, /* Toggles an npc's processing mode */
/client/proc/open_shuttle_manipulator /* Opens shuttle manipulator UI */
)
var/list/admin_verbs_ban = list(
/client/proc/unban_panel,
/client/proc/DB_ban_panel,
/client/proc/stickybanpanel
)
var/list/admin_verbs_sounds = list(
/client/proc/play_local_sound,
/client/proc/play_sound,
/client/proc/set_round_end_sound,
)
var/list/admin_verbs_fun = list(
/client/proc/cmd_admin_dress,
/client/proc/cmd_admin_gib_self,
/client/proc/drop_bomb,
/client/proc/cinematic,
/client/proc/one_click_antag,
/client/proc/send_space_ninja,
/client/proc/cmd_admin_add_freeform_ai_law,
/client/proc/cmd_admin_add_random_ai_law,
/client/proc/object_say,
/client/proc/toggle_random_events,
/client/proc/set_ooc,
/client/proc/reset_ooc,
/client/proc/forceEvent,
/client/proc/bluespace_artillery,
/client/proc/admin_change_sec_level,
/client/proc/toggle_nuke,
/client/proc/mass_zombie_infection,
/client/proc/mass_zombie_cure,
/client/proc/polymorph_all,
/client/proc/show_tip
)
var/list/admin_verbs_spawn = list(
/datum/admins/proc/spawn_atom, /*allows us to spawn instances*/
/client/proc/respawn_character
)
var/list/admin_verbs_server = list(
/datum/admins/proc/startnow,
/datum/admins/proc/restart,
/datum/admins/proc/end_round,
/datum/admins/proc/delay,
/datum/admins/proc/toggleaban,
/client/proc/toggle_log_hrefs,
/client/proc/everyone_random,
/datum/admins/proc/toggleAI,
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
/client/proc/cmd_debug_del_all,
/client/proc/toggle_random_events,
#if SERVERTOOLS
/client/proc/forcerandomrotate,
/client/proc/adminchangemap,
#endif
/client/proc/panicbunker
)
var/list/admin_verbs_debug = list(
/client/proc/restart_controller,
/client/proc/cmd_admin_list_open_jobs,
/client/proc/Debug2,
/client/proc/cmd_debug_make_powernets,
/client/proc/cmd_debug_mob_lists,
/client/proc/cmd_admin_delete,
/client/proc/cmd_debug_del_all,
/client/proc/restart_controller,
/client/proc/enable_debug_verbs,
/client/proc/callproc,
/client/proc/callproc_datum,
/client/proc/SDQL2_query,
/client/proc/test_movable_UI,
/client/proc/test_snap_UI,
/client/proc/debugNatureMapGenerator,
/client/proc/check_bomb_impacts,
/proc/machine_upgrade,
/client/proc/populate_world,
/client/proc/cmd_display_del_log,
/client/proc/reset_latejoin_spawns,
/client/proc/create_outfits,
/client/proc/debug_huds,
/client/proc/map_template_load,
/client/proc/map_template_upload,
/client/proc/jump_to_ruin
)
var/list/admin_verbs_possess = list(
/proc/possess,
/proc/release
)
var/list/admin_verbs_permissions = list(
/client/proc/edit_admin_permissions,
/client/proc/create_poll
)
var/list/admin_verbs_rejuv = list(
/client/proc/respawn_character
)
//verbs which can be hidden - needs work
var/list/admin_verbs_hideable = list(
/client/proc/set_ooc,
/client/proc/reset_ooc,
/client/proc/deadmin,
/client/proc/deadchat,
/client/proc/toggleprayers,
/client/proc/toggle_hear_radio,
/datum/admins/proc/show_traitor_panel,
/datum/admins/proc/toggleenter,
/datum/admins/proc/toggleguests,
/datum/admins/proc/announce,
/datum/admins/proc/set_admin_notice,
/client/proc/admin_ghost,
/client/proc/toggle_view_range,
/datum/admins/proc/view_txt_log,
/datum/admins/proc/view_atk_log,
/client/proc/cmd_admin_subtle_message,
/client/proc/cmd_admin_check_contents,
/datum/admins/proc/access_news_network,
/client/proc/admin_call_shuttle,
/client/proc/admin_cancel_shuttle,
/client/proc/cmd_admin_direct_narrate,
/client/proc/cmd_admin_world_narrate,
/client/proc/cmd_admin_local_narrate,
/client/proc/play_local_sound,
/client/proc/play_sound,
/client/proc/set_round_end_sound,
/client/proc/cmd_admin_dress,
/client/proc/cmd_admin_gib_self,
/client/proc/drop_bomb,
/client/proc/cinematic,
/client/proc/send_space_ninja,
/client/proc/cmd_admin_add_freeform_ai_law,
/client/proc/cmd_admin_add_random_ai_law,
/client/proc/cmd_admin_create_centcom_report,
/client/proc/cmd_change_command_name,
/client/proc/object_say,
/client/proc/toggle_random_events,
/client/proc/cmd_admin_add_random_ai_law,
/datum/admins/proc/startnow,
/datum/admins/proc/restart,
/datum/admins/proc/delay,
/datum/admins/proc/toggleaban,
/client/proc/toggle_log_hrefs,
/client/proc/everyone_random,
/datum/admins/proc/toggleAI,
/client/proc/restart_controller,
/client/proc/cmd_admin_list_open_jobs,
/client/proc/callproc,
/client/proc/callproc_datum,
/client/proc/Debug2,
/client/proc/reload_admins,
/client/proc/cmd_debug_make_powernets,
/client/proc/startSinglo,
/client/proc/cmd_debug_mob_lists,
/client/proc/cmd_debug_del_all,
/client/proc/enable_debug_verbs,
/proc/possess,
/proc/release,
/client/proc/reload_admins,
/client/proc/panicbunker,
/client/proc/admin_change_sec_level,
/client/proc/toggle_nuke,
/client/proc/cmd_display_del_log,
/client/proc/toggle_antag_hud,
/client/proc/debug_huds,
/client/proc/customiseSNPC,
/client/proc/resetSNPC,
/client/proc/toggleSNPC
)
/client/proc/add_admin_verbs()
if(holder)
control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS
var/rights = holder.rank.rights
verbs += admin_verbs_default
if(rights & R_BUILDMODE)
verbs += /client/proc/togglebuildmodeself
if(rights & R_ADMIN)
verbs += admin_verbs_admin
if(rights & R_BAN)
verbs += admin_verbs_ban
if(rights & R_FUN)
verbs += admin_verbs_fun
if(rights & R_SERVER)
verbs += admin_verbs_server
if(rights & R_DEBUG)
verbs += admin_verbs_debug
if(rights & R_POSSESS)
verbs += admin_verbs_possess
if(rights & R_PERMISSIONS)
verbs += admin_verbs_permissions
if(rights & R_STEALTH)
verbs += /client/proc/stealth
if(rights & R_REJUVINATE)
verbs += admin_verbs_rejuv
if(rights & R_SOUNDS)
verbs += admin_verbs_sounds
if(rights & R_SPAWN)
verbs += admin_verbs_spawn
for(var/path in holder.rank.adds)
verbs += path
for(var/path in holder.rank.subs)
verbs -= path
/client/proc/remove_admin_verbs()
verbs.Remove(
admin_verbs_default,
/client/proc/togglebuildmodeself,
admin_verbs_admin,
admin_verbs_ban,
admin_verbs_fun,
admin_verbs_server,
admin_verbs_debug,
admin_verbs_possess,
admin_verbs_permissions,
/client/proc/stealth,
admin_verbs_rejuv,
admin_verbs_sounds,
admin_verbs_spawn,
/*Debug verbs added by "show debug verbs"*/
/client/proc/Cell,
/client/proc/do_not_use_these,
/client/proc/camera_view,
/client/proc/sec_camera_report,
/client/proc/intercom_view,
/client/proc/air_status,
/client/proc/atmosscan,
/client/proc/powerdebug,
/client/proc/count_objects_on_z_level,
/client/proc/count_objects_all,
/client/proc/cmd_assume_direct_control,
/client/proc/startSinglo,
/client/proc/fps,
/client/proc/cmd_admin_grantfullaccess,
/client/proc/cmd_admin_areatest,
/client/proc/readmin
)
if(holder)
verbs.Remove(holder.rank.adds)
/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs
set name = "Adminverbs - Hide Most"
set category = "Admin"
verbs.Remove(/client/proc/hide_most_verbs, admin_verbs_hideable)
verbs += /client/proc/show_verbs
src << "<span class='interface'>Most of your adminverbs have been hidden.</span>"
feedback_add_details("admin_verb","HMV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/hide_verbs()
set name = "Adminverbs - Hide All"
set category = "Admin"
remove_admin_verbs()
verbs += /client/proc/show_verbs
src << "<span class='interface'>Almost all of your adminverbs have been hidden.</span>"
feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/show_verbs()
set name = "Adminverbs - Show"
set category = "Admin"
verbs -= /client/proc/show_verbs
add_admin_verbs()
src << "<span class='interface'>All of your adminverbs are now visible.</span>"
feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/admin_ghost()
set category = "Admin"
set name = "Aghost"
if(!holder)
return
if(istype(mob,/mob/dead/observer))
//re-enter
var/mob/dead/observer/ghost = mob
if(!ghost.mind || !ghost.mind.current) //won't do anything if there is no body
return
if(!ghost.can_reenter_corpse)
log_admin("[key_name(usr)] re-entered corpse")
message_admins("[key_name_admin(usr)] re-entered corpse")
ghost.can_reenter_corpse = 1 //force re-entering even when otherwise not possible
ghost.reenter_corpse()
feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
else if(istype(mob,/mob/new_player))
src << "<font color='red'>Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.</font>"
else
//ghostize
log_admin("[key_name(usr)] admin ghosted.")
message_admins("[key_name_admin(usr)] admin ghosted.")
var/mob/body = mob
body.ghostize(1)
if(body && !body.key)
body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus
feedback_add_details("admin_verb","O") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/invisimin()
set name = "Invisimin"
set category = "Admin"
set desc = "Toggles ghost-like invisibility (Don't abuse this)"
if(holder && mob)
if(mob.invisibility == INVISIBILITY_OBSERVER)
mob.invisibility = initial(mob.invisibility)
mob << "<span class='boldannounce'>Invisimin off. Invisibility reset.</span>"
else
mob.invisibility = INVISIBILITY_OBSERVER
mob << "<span class='adminnotice'><b>Invisimin on. You are now as invisible as a ghost.</b></span>"
/client/proc/player_panel_new()
set name = "Player Panel"
set category = "Admin"
if(holder)
holder.player_panel_new()
feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/check_antagonists()
set name = "Check Antagonists"
set category = "Admin"
if(holder)
holder.check_antagonists()
log_admin("[key_name(usr)] checked antagonists.") //for tsar~
if(!isobserver(usr))
message_admins("[key_name_admin(usr)] checked antagonists.")
feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/unban_panel()
set name = "Unban Panel"
set category = "Admin"
if(holder)
if(config.ban_legacy_system)
holder.unbanpanel()
else
holder.DB_ban_panel()
feedback_add_details("admin_verb","UBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/game_panel()
set name = "Game Panel"
set category = "Admin"
if(holder)
holder.Game()
feedback_add_details("admin_verb","GP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/secrets()
set name = "Secrets"
set category = "Admin"
if (holder)
holder.Secrets()
feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/findStealthKey(txt)
if(txt)
for(var/P in stealthminID)
if(stealthminID[P] == txt)
return P
txt = stealthminID[ckey]
return txt
/client/proc/createStealthKey()
var/num = (rand(0,1000))
var/i = 0
while(i == 0)
i = 1
for(var/P in stealthminID)
if(num == stealthminID[P])
num++
i = 0
stealthminID["[ckey]"] = "@[num2text(num)]"
/client/proc/stealth()
set category = "Admin"
set name = "Stealth Mode"
if(holder)
if(holder.fakekey)
holder.fakekey = null
if(isobserver(mob))
mob.invisibility = initial(mob.invisibility)
mob.alpha = initial(mob.alpha)
mob.name = initial(mob.name)
mob.mouse_opacity = initial(mob.mouse_opacity)
else
var/new_key = ckeyEx(input("Enter your desired display name.", "Fake Key", key) as text|null)
if(!new_key)
return
if(length(new_key) >= 26)
new_key = copytext(new_key, 1, 26)
holder.fakekey = new_key
createStealthKey()
if(isobserver(mob))
mob.invisibility = INVISIBILITY_ABSTRACT //JUST IN CASE
mob.alpha = 0 //JUUUUST IN CASE
mob.name = " "
mob.mouse_opacity = 0
log_admin("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]")
message_admins("[key_name_admin(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]")
feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/drop_bomb()
set category = "Special Verbs"
set name = "Drop Bomb"
set desc = "Cause an explosion of varying strength at your location."
var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb")
var/choice = input("What size explosion would you like to produce?") in choices
var/turf/epicenter = mob.loc
switch(choice)
if(null)
return 0
if("Small Bomb")
explosion(epicenter, 1, 2, 3, 3)
if("Medium Bomb")
explosion(epicenter, 2, 3, 4, 4)
if("Big Bomb")
explosion(epicenter, 3, 5, 7, 5)
if("Custom Bomb")
var/devastation_range = input("Devastation range (in tiles):") as null|num
if(devastation_range == null)
return
var/heavy_impact_range = input("Heavy impact range (in tiles):") as null|num
if(heavy_impact_range == null)
return
var/light_impact_range = input("Light impact range (in tiles):") as null|num
if(light_impact_range == null)
return
var/flash_range = input("Flash range (in tiles):") as null|num
if(flash_range == null)
return
epicenter = mob.loc //We need to reupdate as they may have moved again
explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range)
message_admins("<span class='adminnotice'>[ckey] creating an admin explosion at [epicenter.loc].</span>")
feedback_add_details("admin_verb","DB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/give_spell(mob/T in mob_list)
set category = "Fun"
set name = "Give Spell"
set desc = "Gives a spell to a mob."
var/list/spell_list = list()
var/type_length = length("/obj/effect/proc_holder/spell") + 2
for(var/A in spells)
spell_list[copytext("[A]", type_length)] = A
var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_list
if(!S)
return
S = spell_list[S]
feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name(T)] the spell [S].</span>")
if(T.mind)
T.mind.AddSpell(new S)
else
T.AddSpell(new S)
message_admins("<span class='danger'>Spells given to mindless mobs will not be transferred in mindswap or cloning!</span>")
/client/proc/give_disease(mob/T in mob_list)
set category = "Fun"
set name = "Give Disease"
set desc = "Gives a Disease to a mob."
var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in diseases
if(!D) return
T.ForceContractDisease(new D)
feedback_add_details("admin_verb","GD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name(T)] the disease [D].</span>")
/client/proc/object_say(obj/O in world)
set category = "Special Verbs"
set name = "OSay"
set desc = "Makes an object say something."
var/message = input(usr, "What do you want the message to be?", "Make Sound") as text | null
if(!message)
return
var/templanguages = O.languages_spoken
O.languages_spoken |= ALL
O.say(message)
O.languages_spoken = templanguages
log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z] say \"[message]\"")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. say \"[message]\"</span>")
feedback_add_details("admin_verb","OS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/togglebuildmodeself()
set name = "Toggle Build Mode Self"
set category = "Special Verbs"
if(src.mob)
togglebuildmode(src.mob)
feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/toggle_log_hrefs()
set name = "Toggle href logging"
set category = "Server"
if(!holder)
return
if(config)
if(config.log_hrefs)
config.log_hrefs = 0
src << "<b>Stopped logging hrefs</b>"
else
config.log_hrefs = 1
src << "<b>Started logging hrefs</b>"
/client/proc/check_ai_laws()
set name = "Check AI Laws"
set category = "Admin"
if(holder)
src.holder.output_ai_laws()
/client/proc/deadmin()
set name = "Deadmin"
set category = "Admin"
set desc = "Shed your admin powers."
if(!holder)
return
holder.disassociate()
qdel(holder)
deadmins += ckey
admin_datums -= ckey
verbs += /client/proc/readmin
src << "<span class='interface'>You are now a normal player.</span>"
log_admin("[src] deadmined themself.")
message_admins("[src] deadmined themself.")
feedback_add_details("admin_verb","DAS")
/client/proc/readmin()
set name = "Readmin"
set category = "Admin"
set desc = "Regain your admin powers."
load_admins(ckey)
if(!holder) // Something went wrong...
return
deadmins -= ckey
verbs -= /client/proc/readmin
src << "<span class='interface'>You are now an admin.</span>"
message_admins("[src] re-adminned themselves.")
log_admin("[src] re-adminned themselves.")
feedback_add_details("admin_verb","RAS")
/client/proc/populate_world(amount = 50 as num)
set name = "Populate World"
set category = "Debug"
set desc = "(\"Amount of mobs to create\") Populate the world with test mobs."
if (amount > 0)
var/area/area
var/list/candidates
var/turf/open/floor/tile
var/j,k
var/mob/living/carbon/human/mob
for (var/i = 1 to amount)
j = 100
do
area = pick(the_station_areas)
if (area)
candidates = get_area_turfs(area)
if (candidates.len)
k = 100
do
tile = pick(candidates)
while ((!tile || !istype(tile)) && --k > 0)
if (tile)
mob = new/mob/living/carbon/human/interactive(tile)
testing("Spawned test mob with name \"[mob.name]\" at [tile.x],[tile.y],[tile.z]")
while (!area && --j > 0)
/client/proc/toggle_AI_interact()
set name = "Toggle Admin AI Interact"
set category = "Admin"
set desc = "Allows you to interact with most machines as an AI would as a ghost"
AI_Interact = !AI_Interact
log_admin("[key_name(usr)] has [AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
message_admins("[key_name_admin(usr)] has [AI_Interact ? "activated" : "deactivated"] their AI interaction")
+110
View File
@@ -0,0 +1,110 @@
//ban people from using custom names and appearances. that'll show 'em.
var/appearanceban_runonce //Updates legacy bans with new info
var/appearance_keylist[0] //to store the keys
/proc/appearance_fullban(mob/M, reason)
if (!M || !M.key) return
appearance_keylist.Add(text("[M.ckey] ## [reason]"))
appearance_savebanfile()
/proc/appearance_client_fullban(ckey)
if (!ckey) return
appearance_keylist.Add(text("[ckey]"))
appearance_savebanfile()
//returns a reason if M is banned, returns 0 otherwise
/proc/appearance_isbanned(mob/M)
if(M)
for(var/s in appearance_keylist)
if(findtext(s, "[M.ckey]") == 1)
var/startpos = findtext(s, "## ") + 3
if(startpos && startpos < length(s))
var/text = copytext(s, startpos, 0)
if(text)
return text
return "Reason Unspecified"
return 0
/*
DEBUG
/mob/verb/list_all_appearances()
set name = "list all appearances"
for(var/s in appearance_keylist)
world << s
/mob/verb/reload_appearances()
set name = "reload appearances"
appearance_loadbanfile()
*/
/proc/appearance_loadbanfile()
if(config.ban_legacy_system)
var/savefile/S=new("data/appearance_full.ban")
S["keys[0]"] >> appearance_keylist
log_admin("Loading appearance_rank")
S["runonce"] >> appearanceban_runonce
if (!length(appearance_keylist))
appearance_keylist=list()
log_admin("appearance_keylist was empty")
else
if(!establish_db_connection())
world.log << "Database connection failed. Reverting to the legacy ban system."
diary << "Database connection failed. Reverting to the legacy ban system."
config.ban_legacy_system = 1
appearance_loadbanfile()
return
//appearance bans
var/DBQuery/query = dbcon.NewQuery("SELECT ckey FROM [format_table_name("ban")] WHERE bantype = 'APPEARANCE_PERMABAN' AND NOT unbanned = 1")
query.Execute()
while(query.NextRow())
var/ckey = query.item[1]
appearance_keylist.Add("[ckey]")
/proc/appearance_savebanfile()
var/savefile/S=new("data/appearance_full.ban")
S["keys[0]"] << appearance_keylist
/proc/appearance_unban(mob/M)
appearance_remove("[M.ckey]")
appearance_savebanfile()
/proc/appearance_updatelegacybans()
if(!appearanceban_runonce)
log_admin("Updating appearancefile!")
// Updates bans.. Or fixes them. Either way.
for(var/T in appearance_keylist)
if(!T)
continue
appearanceban_runonce++ //don't run this update again
/proc/appearance_remove(X)
for (var/i = 1; i <= length(appearance_keylist); i++)
if( findtext(appearance_keylist[i], "[X]") )
appearance_keylist.Remove(appearance_keylist[i])
appearance_savebanfile()
return 1
return 0
/*
/proc/DB_ban_isappearancebanned(var/playerckey)
establish_db_connection()
if(!dbcon.IsConnected())
return
var/sqlplayerckey = sanitizeSQL(ckey(playerckey))
var/DBQuery/query = dbcon.NewQuery("SELECT id FROM [format_table_name("ban")] WHERE CKEY = '[sqlplayerckey]' AND ((bantype = 'APPEARANCE_PERMABAN') OR (bantype = 'APPEARANCE_TEMPBAN' AND expiration_time > Now())) AND unbanned != 1")
query.Execute()
while(query.NextRow())
return 1
return 0
*/
+36
View File
@@ -0,0 +1,36 @@
//returns a reason if M is banned from rank, returns 0 otherwise
/proc/jobban_isbanned(mob/M, rank)
if(!M || !istype(M) || !M.ckey)
return 0
if(!M.client) //no cache. fallback to a DBQuery
var/DBQuery/query = dbcon.NewQuery("SELECT reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(M.ckey)]' AND job = '[sanitizeSQL(rank)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
if(!query.Execute())
log_game("SQL ERROR obtaining jobbans. Error : \[[query.ErrorMsg()]\]\n")
return
if(query.NextRow())
var/reason = query.item[1]
return reason ? reason : 1 //we don't want to return "" if there is no ban reason, as that would evaluate to false
else
return 0
if(!M.client.jobbancache)
jobban_buildcache(M.client)
if(rank in M.client.jobbancache)
var/reason = M.client.jobbancache[rank]
return (reason) ? reason : 1 //see above for why we need to do this
return 0
/proc/jobban_buildcache(client/C)
if(C && istype(C))
C.jobbancache = list()
var/DBQuery/query = dbcon.NewQuery("SELECT job, reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(C.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
if(!query.Execute())
log_game("SQL ERROR obtaining jobbans. Error : \[[query.ErrorMsg()]\]\n")
return
while(query.NextRow())
C.jobbancache[query.item[1]] = query.item[2]
/proc/ban_unban_log_save(var/formatted_log)
text2file(formatted_log,"data/ban_unban_log.txt")
+25
View File
@@ -0,0 +1,25 @@
/var/create_mob_html = null
/datum/admins/proc/create_mob(mob/user)
if (!create_mob_html)
var/mobjs = null
mobjs = jointext(typesof(/mob), ";")
create_mob_html = file2text('html/create_object.html')
create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"")
user << browse(replacetext(create_mob_html, "/* ref src */", "\ref[src]"), "window=create_mob;size=425x475")
/proc/randomize_human(mob/living/carbon/human/H)
H.gender = pick(MALE, FEMALE)
H.real_name = random_unique_name(H.gender)
H.name = H.real_name
H.underwear = random_underwear(H.gender)
H.skin_tone = random_skin_tone()
H.hair_style = random_hair_style(H.gender)
H.facial_hair_style = random_facial_hair_style(H.gender)
H.hair_color = random_short_color()
H.facial_hair_color = H.hair_color
H.eye_color = random_eye_color()
H.dna.blood_type = random_blood_type()
H.update_body()
H.update_hair()
H.update_body_parts()
+27
View File
@@ -0,0 +1,27 @@
var/create_object_html = null
var/list/create_object_forms = list(
/obj, /obj/structure, /obj/machinery, /obj/effect,
/obj/item, /obj/item/clothing, /obj/item/stack, /obj/item/device,
/obj/item/weapon, /obj/item/weapon/reagent_containers, /obj/item/weapon/gun)
/datum/admins/proc/create_object(mob/user)
if (!create_object_html)
var/objectjs = null
objectjs = jointext(typesof(/obj), ";")
create_object_html = file2text('html/create_object.html')
create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"")
user << browse(replacetext(create_object_html, "/* ref src */", "\ref[src]"), "window=create_object;size=425x475")
/datum/admins/proc/quick_create_object(mob/user)
var/path = input("Select the path of the object you wish to create.", "Path", /obj) in create_object_forms
var/html_form = create_object_forms[path]
if (!html_form)
var/objectjs = jointext(typesof(path), ";")
html_form = file2text('html/create_object.html')
html_form = replacetext(html_form, "null /* object types */", "\"[objectjs]\"")
create_object_forms[path] = html_form
user << browse(replacetext(html_form, "/* ref src */", "\ref[src]"), "window=qco[path];size=425x475")
+160
View File
@@ -0,0 +1,160 @@
/client/proc/create_poll()
set name = "Create Poll"
set category = "Special Verbs"
if(!check_rights(R_PERMISSIONS))
return
if(!dbcon.IsConnected())
src << "<span class='danger'>Failed to establish database connection.</span>"
return
var/returned = create_poll_function()
if(returned)
var/DBQuery/query_check_option = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE pollid = [returned]")
if(!query_check_option.Execute())
var/err = query_check_option.ErrorMsg()
log_game("SQL ERROR obtaining id from poll_option table. Error : \[[err]\]\n")
return
if(query_check_option.NextRow())
var/DBQuery/query_log_get = dbcon.NewQuery("SELECT polltype, question, adminonly FROM [format_table_name("poll_question")] WHERE id = [returned]")
if(!query_log_get.Execute())
var/err = query_log_get.ErrorMsg()
log_game("SQL ERROR obtaining polltype, question, adminonly from poll_question table. Error : \[[err]\]\n")
return
if(query_log_get.NextRow())
var/polltype = query_log_get.item[1]
var/question = query_log_get.item[2]
var/adminonly = text2num(query_log_get.item[3])
log_admin("[key_name(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"] - Question: [question]")
message_admins("[key_name_admin(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"]<br>Question: [question]")
else
src << "Poll question created without any options, poll will be deleted."
var/DBQuery/query_del_poll = dbcon.NewQuery("DELETE FROM [format_table_name("poll_question")] WHERE id = [returned]")
if(!query_del_poll.Execute())
var/err = query_del_poll.ErrorMsg()
log_game("SQL ERROR deleting poll question [returned]. Error : \[[err]\]\n")
return
/client/proc/create_poll_function()
var/polltype = input("Choose poll type.","Poll Type") in list("Single Option","Text Reply","Rating","Multiple Choice")
var/choice_amount = 0
switch(polltype)
if("Single Option")
polltype = POLLTYPE_OPTION
if("Text Reply")
polltype = POLLTYPE_TEXT
if("Rating")
polltype = POLLTYPE_RATING
if("Multiple Choice")
polltype = POLLTYPE_MULTI
choice_amount = input("How many choices should be allowed?","Select choice amount") as num|null
if(!choice_amount)
return
var/starttime = SQLtime()
var/endtime = input("Set end time for poll as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than starting time for obvious reasons.", "Set end time", SQLtime()) as text
if(!endtime)
return
endtime = sanitizeSQL(endtime)
var/DBQuery/query_validate_time = dbcon.NewQuery("SELECT STR_TO_DATE('[endtime]','%Y-%c-%d %T')")
if(!query_validate_time.Execute())
var/err = query_validate_time.ErrorMsg()
log_game("SQL ERROR validating endtime. Error : \[[err]\]\n")
return
if(query_validate_time.NextRow())
endtime = query_validate_time.item[1]
if(!endtime)
src << "Datetime entered is invalid."
return
var/DBQuery/query_time_later = dbcon.NewQuery("SELECT TIMESTAMP('[endtime]') < NOW()")
if(!query_time_later.Execute())
var/err = query_time_later.ErrorMsg()
log_game("SQL ERROR comparing endtime to NOW(). Error : \[[err]\]\n")
return
if(query_time_later.NextRow())
var/checklate = text2num(query_time_later.item[1])
if(checklate)
src << "Datetime entered is not later than current server time."
return
var/adminonly
switch(alert("Admin only poll?",,"Yes","No","Cancel"))
if("Yes")
adminonly = 1
if("No")
adminonly = 0
else
return
var/sql_ckey = sanitizeSQL(ckey)
var/question = input("Write your question","Question") as message|null
if(!question)
return
question = sanitizeSQL(question)
var/DBQuery/query_polladd_question = dbcon.NewQuery("INSERT INTO [format_table_name("poll_question")] (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip) VALUES ('[polltype]', '[starttime]', '[endtime]', '[question]', '[adminonly]', '[choice_amount]', '[sql_ckey]', '[address]')")
if(!query_polladd_question.Execute())
var/err = query_polladd_question.ErrorMsg()
log_game("SQL ERROR adding new poll question to table. Error : \[[err]\]\n")
return
if(polltype == POLLTYPE_TEXT)
log_admin("[key_name(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"] - Question: [question]")
message_admins("[key_name_admin(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"]<br>Question: [question]")
return
var/pollid = 0
var/DBQuery/query_get_id = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE question = '[question]' AND starttime = '[starttime]' AND endtime = '[endtime]' AND createdby_ckey = '[sql_ckey]' AND createdby_ip = '[address]'")
if(!query_get_id.Execute())
var/err = query_get_id.ErrorMsg()
log_game("SQL ERROR obtaining id from poll_question table. Error : \[[err]\]\n")
return
if(query_get_id.NextRow())
pollid = query_get_id.item[1]
var/add_option = 1
while(add_option)
var/option = input("Write your option","Option") as message|null
if(!option)
return pollid
option = sanitizeSQL(option)
var/percentagecalc
switch(alert("Calculate option results as percentage?",,"Yes","No","Cancel"))
if("Yes")
percentagecalc = 1
if("No")
percentagecalc = 0
else
return pollid
var/minval = 0
var/maxval = 0
var/descmin = ""
var/descmid = ""
var/descmax = ""
if(polltype == POLLTYPE_RATING)
minval = input("Set minimum rating value.","Minimum rating") as num|null
if(!minval)
return pollid
maxval = input("Set maximum rating value.","Maximum rating") as num|null
if(!maxval)
return pollid
if(minval >= maxval)
src << "Minimum rating value can't be more than maximum rating value"
return pollid
descmin = input("Optional: Set description for minimum rating","Minimum rating description") as message|null
if(descmin)
descmin = sanitizeSQL(descmin)
else if(descmin == null)
return pollid
descmid = input("Optional: Set description for median rating","Median rating description") as message|null
if(descmid)
descmid = sanitizeSQL(descmid)
else if(descmid == null)
return pollid
descmax = input("Optional: Set description for maximum rating","Maximum rating description") as message|null
if(descmax)
descmax = sanitizeSQL(descmax)
else if(descmax == null)
return pollid
var/DBQuery/query_polladd_option = dbcon.NewQuery("INSERT INTO [format_table_name("poll_option")] (pollid, text, percentagecalc, minval, maxval, descmin, descmid, descmax) VALUES ('[pollid]', '[option]', '[percentagecalc]', '[minval]', '[maxval]', '[descmin]', '[descmid]', '[descmax]')")
if(!query_polladd_option.Execute())
var/err = query_polladd_option.ErrorMsg()
log_game("SQL ERROR adding new poll option to table. Error : \[[err]\]\n")
return pollid
switch(alert(" ",,"Add option","Finish"))
if("Add option")
add_option = 1
if("Finish")
add_option = 0
return pollid
+9
View File
@@ -0,0 +1,9 @@
/var/create_turf_html = null
/datum/admins/proc/create_turf(mob/user)
if (!create_turf_html)
var/turfjs = null
turfjs = jointext(typesof(/turf), ";")
create_turf_html = file2text('html/create_object.html')
create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"")
user << browse(replacetext(create_turf_html, "/* ref src */", "\ref[src]"), "window=create_turf;size=425x475")
+85
View File
@@ -0,0 +1,85 @@
/obj/effect/fun_balloon
name = "fun balloon"
desc = "This is going to be a laugh riot."
icon = 'icons/obj/weapons.dmi'
icon_state = "syndballoon"
anchored = TRUE
var/popped = FALSE
/obj/effect/fun_balloon/New()
. = ..()
SSobj.processing |= src
/obj/effect/fun_balloon/Destroy()
SSobj.processing -= src
. = ..()
/obj/effect/fun_balloon/process()
if(!popped && check() && !qdeleted(src))
popped = TRUE
effect()
pop()
/obj/effect/fun_balloon/proc/check()
return FALSE
/obj/effect/fun_balloon/proc/effect()
return
/obj/effect/fun_balloon/proc/pop()
visible_message("[src] pops!")
playsound(get_turf(src), 'sound/items/party_horn.ogg', 50, 1, -1)
qdel(src)
/obj/effect/fun_balloon/attack_ghost(mob/user)
if(!user.client || !user.client.holder || popped)
return
switch(alert("Pop [src]?","Fun Balloon","Yes","No"))
if("Yes")
effect()
pop()
/obj/effect/fun_balloon/sentience
name = "sentience fun balloon"
desc = "When this pops, things are gonna get more aware around here."
var/effect_range = 3
var/group_name = "a bunch of giant spiders"
/obj/effect/fun_balloon/sentience/effect()
var/list/bodies = list()
for(var/mob/living/M in range(effect_range, get_turf(src)))
bodies += M
var/question = "Would you like to be [group_name]?"
var/list/candidates = pollCandidates(question, "pAI", null, FALSE, 100)
while(candidates.len && bodies.len)
var/mob/dead/observer/ghost = pick_n_take(candidates)
var/mob/living/body = pick_n_take(bodies)
body << "Your mob has been taken over by a ghost!"
message_admins("[key_name_admin(ghost)] has taken control \
of ([key_name_admin(body)])")
body.ghostize(0)
body.key = ghost.key
PoolOrNew(/obj/effect/overlay/temp/sparkle, body)
/obj/effect/fun_balloon/sentience/emergency_shuttle
name = "shuttle sentience fun balloon"
var/trigger_time = 60
/obj/effect/fun_balloon/sentience/emergency_shuttle/check()
. = FALSE
if(SSshuttle.emergency && (SSshuttle.emergency.timeLeft() <= trigger_time) && (SSshuttle.emergency.mode == SHUTTLE_CALL))
. = TRUE
/obj/effect/fun_balloon/scatter
name = "scatter fun balloon"
desc = "When this pops, you're not going to be around here anymore."
var/effect_range = 5
/obj/effect/fun_balloon/scatter/effect()
for(var/mob/living/M in range(effect_range, get_turf(src)))
var/turf/T = find_safe_turf()
PoolOrNew(/obj/effect/overlay/temp/sparkle, M)
M.forceMove(T)
M << "<span class='notice'>Pop!</span>"
+96
View File
@@ -0,0 +1,96 @@
var/list/admin_datums = list()
/datum/admins
var/datum/admin_rank/rank
var/client/owner = null
var/fakekey = null
var/datum/marked_datum
var/admincaster_screen = 0 //TODO: remove all these 5 variables, they are completly unacceptable
var/datum/newscaster/feed_message/admincaster_feed_message = new /datum/newscaster/feed_message
var/datum/newscaster/wanted_message/admincaster_wanted_message = new /datum/newscaster/wanted_message
var/datum/newscaster/feed_channel/admincaster_feed_channel = new /datum/newscaster/feed_channel
var/admin_signature
/datum/admins/New(datum/admin_rank/R, ckey)
if(!ckey)
spawn(0)
del(src)
throw EXCEPTION("Admin datum created without a ckey")
return
if(!istype(R))
spawn(0)
del(src)
throw EXCEPTION("Admin datum created without a rank")
return
rank = R
admin_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]"
admin_datums[ckey] = src
/datum/admins/proc/associate(client/C)
if(istype(C))
owner = C
owner.holder = src
owner.add_admin_verbs() //TODO
owner.verbs -= /client/proc/readmin
admins |= C
/datum/admins/proc/disassociate()
if(owner)
admins -= owner
owner.remove_admin_verbs()
owner.holder = null
owner = null
/datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other)
if(!other)
return 1 //they have no rights
if(rank.rights == 65535)
return 1 //we have all the rights
if(src == other)
return 1 //you always have more rights than yourself
if(rank.rights != other.rank.rights)
if( (rank.rights & other.rank.rights) == other.rank.rights )
return 1 //we have all the rights they have and more
return 0
/*
checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags)
if rights_required == 0, then it simply checks if they are an admin.
if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed
generally it would be used like so:
proc/admin_proc()
if(!check_rights(R_ADMIN)) return
world << "you have enough rights!"
NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call
you will have to do something like if(client.rights & R_ADMIN) yourself.
*/
/proc/check_rights(rights_required, show_msg=1)
if(usr && usr.client)
if (check_rights_for(usr.client, rights_required))
return 1
else
if(show_msg)
usr << "<font color='red'>Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].</font>"
return 0
//probably a bit iffy - will hopefully figure out a better solution
/proc/check_if_greater_rights_than(client/other)
if(usr && usr.client)
if(usr.client.holder)
if(!other || !other.holder)
return 1
return usr.client.holder.check_if_greater_rights_than_holder(other.holder)
return 0
//This proc checks whether subject has at least ONE of the rights specified in rights_required.
/proc/check_rights_for(client/subject, rights_required)
if(subject && subject.holder && subject.holder.rank)
if(rights_required && !(rights_required & subject.holder.rank.rights))
return 0
return 1
return 0
+134
View File
@@ -0,0 +1,134 @@
/datum/ipintel
var/ip
var/intel = 0
var/cache = FALSE
var/cacheminutesago = 0
var/cachedate = ""
var/cacherealtime = 0
/datum/ipintel/New()
cachedate = SQLtime()
cacherealtime = world.realtime
/datum/ipintel/proc/is_valid()
. = FALSE
if (intel < 0)
return
if (intel <= config.ipintel_rating_bad)
if (world.realtime < cacherealtime+(config.ipintel_save_good*60*60*10))
return TRUE
else
if (world.realtime < cacherealtime+(config.ipintel_save_bad*60*60*10))
return TRUE
/proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE)
var/datum/ipintel/res = new()
res.ip = ip
. = res
if (!ip || !config.ipintel_email || !SSipintel.enabled)
return
if (!bypasscache)
var/datum/ipintel/cachedintel = SSipintel.cache[ip]
if (cachedintel && cachedintel.is_valid())
cachedintel.cache = TRUE
return cachedintel
if (establish_db_connection())
var/DBQuery/query = dbcon.NewQuery({"
SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW())
FROM [format_table_name("ipintel")]
WHERE
ip = INET_ATON('[ip]')
AND ((
intel < [config.ipintel_rating_bad]
AND
date + INTERVAL [config.ipintel_save_good] HOUR > NOW()
) OR (
intel >= [config.ipintel_rating_bad]
AND
date + INTERVAL [config.ipintel_save_bad] HOUR > NOW()
))
"})
query.Execute()
if (query.NextRow())
res.cache = TRUE
res.cachedate = query.item[1]
res.intel = query.item[2]
res.cacheminutesago = query.item[3]
res.cacherealtime = world.realtime - (query.item[3]*10*60)
SSipintel.cache[ip] = res
return
res.intel = ip_intel_query(ip)
if (updatecache && res.intel >= 0)
SSipintel.cache[ip] = res
if (establish_db_connection())
var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("ipintel")] (ip, intel) VALUES (INET_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()")
query.Execute()
return
/proc/ip_intel_query(ip, var/retryed=0)
. = -1 //default
if (!ip)
return
if (SSipintel.throttle > world.timeofday)
return
if (!SSipintel.enabled)
return
var/list/http[] = world.Export("http://[config.ipintel_domain]/check.php?ip=[ip]&contact=[config.ipintel_email]&format=json&flags=f")
if (http)
var/status = text2num(http["STATUS"])
if (status == 200)
var/response = json_decode(file2text(http["CONTENT"]))
if (response)
if (response["status"] == "success")
var/intelnum = text2num(response["result"])
if (isnum(intelnum))
return text2num(response["result"])
else
ipintel_handle_error("Bad intel from server: [response["result"]].", ip, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
else
ipintel_handle_error("Bad response from server: [response["status"]].", ip, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
else if (status == 429)
ipintel_handle_error("Error #429: We have exceeded the rate limit.", ip, 1)
return
else
ipintel_handle_error("Unknown status code: [status].", ip, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
else
ipintel_handle_error("Unable to connect to API.", ip, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
/proc/ipintel_handle_error(error, ip, retryed)
if (retryed)
SSipintel.errors++
error += " Could not check [ip]. Disabling IPINTEL for [SSipintel.errors] minute[( SSipintel.errors == 1 ? "" : "s" )]"
SSipintel.throttle = world.timeofday + (10 * 120 * SSipintel.errors)
else
error += " Attempting retry on [ip]."
log_ipintel(error)
/proc/log_ipintel(text)
log_game("IPINTEL: [text]")
debug_admins("IPINTEL: [text]")
@@ -0,0 +1,133 @@
/client/proc/edit_admin_permissions()
set category = "Admin"
set name = "Permissions Panel"
set desc = "Edit admin permissions"
if(!check_rights(R_PERMISSIONS))
return
usr.client.holder.edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions()
if(!check_rights(R_PERMISSIONS))
return
var/output = {"<!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:125px;text-align:right;'>CKEY <a class='small' href='?src=\ref[src];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th>
<th style='width:375px;'>PERMISSIONS</th>
<th style='width:100%;'>VERB-OVERRIDES</th>
</tr>
"}
for(var/adm_ckey in admin_datums)
var/datum/admins/D = admin_datums[adm_ckey]
if(!D)
continue
var/rights = rights2text(D.rank.rights," ")
if(!rights) rights = "*none*"
output += "<tr>"
output += "<td style='text-align:right;'>[adm_ckey] <a class='small' href='?src=\ref[src];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td><a href='?src=\ref[src];editrights=rank;ckey=[adm_ckey]'>[D.rank.name]</a></td>"
output += "<td><a class='small' href='?src=\ref[src];editrights=permissions;ckey=[adm_ckey]'>[rights]</a></td>"
output += "<td><a class='small' href='?src=\ref[src];editrights=permissions;ckey=[adm_ckey]'>[rights2text(0," ",D.rank.adds,D.rank.subs)]</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(output,"window=editrights;size=900x650")
/datum/admins/proc/log_admin_rank_modification(adm_ckey, new_rank)
if(config.admin_legacy_system)
return
if(!usr.client)
return
if (!check_rights(R_PERMISSIONS))
return
establish_db_connection()
if(!dbcon.IsConnected())
usr << "<span class='danger'>Failed to establish database connection.</span>"
return
if(!adm_ckey || !new_rank)
return
adm_ckey = ckey(adm_ckey)
if(!adm_ckey)
return
if(!istext(adm_ckey) || !istext(new_rank))
return
var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'")
select_query.Execute()
var/new_admin = 1
var/admin_id
while(select_query.NextRow())
new_admin = 0
admin_id = text2num(select_query.item[1])
if(new_admin)
var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO `[format_table_name("admin")]` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)")
insert_query.Execute()
var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');")
log_query.Execute()
usr << "<span class='adminnotice'>New admin added.</span>"
else
if(!isnull(admin_id) && isnum(admin_id))
var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `[format_table_name("admin")]` SET rank = '[new_rank]' WHERE id = [admin_id]")
insert_query.Execute()
var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');")
log_query.Execute()
usr << "<span class='adminnnotice'>Admin rank changed.</span>"
/datum/admins/proc/log_admin_permission_modification(adm_ckey, new_permission)
if(config.admin_legacy_system)
return
if(!usr.client)
return
if(check_rights(R_PERMISSIONS))
return
establish_db_connection()
if(!dbcon.IsConnected())
usr << "<span class='danger'>Failed to establish database connection.</span>"
return
if(!adm_ckey || !istext(adm_ckey) || !isnum(new_permission))
return
var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'")
select_query.Execute()
var/admin_id
while(select_query.NextRow())
admin_id = text2num(select_query.item[1])
if(!admin_id)
return
var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `[format_table_name("admin")]` SET flags = [new_permission] WHERE id = [admin_id]")
insert_query.Execute()
var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edit permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');")
log_query.Execute()
+653
View File
@@ -0,0 +1,653 @@
/datum/admins/proc/player_panel_new()//The new one
if(!check_rights())
return
var/dat = "<html><head><title>Player Panel</title></head>"
//javascript, the part that does most of the work~
dat += {"
<head>
<script type='text/javascript'>
var locked_tabs = new Array();
function updateSearch(){
var filter_text = document.getElementById('filter');
var filter = filter_text.value.toLowerCase();
if(complete_list != null && complete_list != ""){
var mtbl = document.getElementById("maintable_data_archive");
mtbl.innerHTML = complete_list;
}
if(filter.value == ""){
return;
}else{
var maintable_data = document.getElementById('maintable_data');
var ltr = maintable_data.getElementsByTagName("tr");
for ( var i = 0; i < ltr.length; ++i )
{
try{
var tr = ltr\[i\];
if(tr.getAttribute("id").indexOf("data") != 0){
continue;
}
var ltd = tr.getElementsByTagName("td");
var td = ltd\[0\];
var lsearch = td.getElementsByTagName("b");
var search = lsearch\[0\];
//var inner_span = li.getElementsByTagName("span")\[1\] //Should only ever contain one element.
//document.write("<p>"+search.innerText+"<br>"+filter+"<br>"+search.innerText.indexOf(filter))
if ( search.innerText.toLowerCase().indexOf(filter) == -1 )
{
//document.write("a");
//ltr.removeChild(tr);
td.innerHTML = "";
i--;
}
}catch(err) { }
}
}
var count = 0;
var index = -1;
var debug = document.getElementById("debug");
locked_tabs = new Array();
}
function expand(id,job,name,real_name,image,key,ip,antagonist,ref){
clearAll();
var span = document.getElementById(id);
var ckey = key.toLowerCase().replace(/\[^a-z@0-9\]+/g,"");
body = "<table><tr><td>";
body += "</td><td align='center'>";
body += "<font size='2'><b>"+job+" "+name+"</b><br><b>Real name "+real_name+"</b><br><b>Played by "+key+" ("+ip+")</b></font>"
body += "</td><td align='center'>";
body += "<a href='?_src_=holder;adminplayeropts="+ref+"'>PP</a> - "
body += "<a href='?_src_=holder;shownoteckey="+ckey+"'>N</a> - "
body += "<a href='?_src_=vars;Vars="+ref+"'>VV</a> - "
body += "<a href='?_src_=holder;traitor="+ref+"'>TP</a> - "
body += "<a href='?priv_msg="+ckey+"'>PM</a> - "
body += "<a href='?_src_=holder;subtlemessage="+ref+"'>SM</a> - "
body += "<a href='?_src_=holder;adminplayerobservefollow="+ref+"'>FLW</a><br>"
if(antagonist > 0)
body += "<font size='2'><a href='?_src_=holder;secrets=check_antagonist'><font color='red'><b>Antagonist</b></font></a></font>";
body += "</td></tr></table>";
span.innerHTML = body
}
function clearAll(){
var spans = document.getElementsByTagName('span');
for(var i = 0; i < spans.length; i++){
var span = spans\[i\];
var id = span.getAttribute("id");
if(!(id.indexOf("item")==0))
continue;
var pass = 1;
for(var j = 0; j < locked_tabs.length; j++){
if(locked_tabs\[j\]==id){
pass = 0;
break;
}
}
if(pass != 1)
continue;
span.innerHTML = "";
}
}
function addToLocked(id,link_id,notice_span_id){
var link = document.getElementById(link_id);
var decision = link.getAttribute("name");
if(decision == "1"){
link.setAttribute("name","2");
}else{
link.setAttribute("name","1");
removeFromLocked(id,link_id,notice_span_id);
return;
}
var pass = 1;
for(var j = 0; j < locked_tabs.length; j++){
if(locked_tabs\[j\]==id){
pass = 0;
break;
}
}
if(!pass)
return;
locked_tabs.push(id);
var notice_span = document.getElementById(notice_span_id);
notice_span.innerHTML = "<font color='red'>Locked</font> ";
//link.setAttribute("onClick","attempt('"+id+"','"+link_id+"','"+notice_span_id+"');");
//document.write("removeFromLocked('"+id+"','"+link_id+"','"+notice_span_id+"')");
//document.write("aa - "+link.getAttribute("onClick"));
}
function attempt(ab){
return ab;
}
function removeFromLocked(id,link_id,notice_span_id){
//document.write("a");
var index = 0;
var pass = 0;
for(var j = 0; j < locked_tabs.length; j++){
if(locked_tabs\[j\]==id){
pass = 1;
index = j;
break;
}
}
if(!pass)
return;
locked_tabs\[index\] = "";
var notice_span = document.getElementById(notice_span_id);
notice_span.innerHTML = "";
//var link = document.getElementById(link_id);
//link.setAttribute("onClick","addToLocked('"+id+"','"+link_id+"','"+notice_span_id+"')");
}
function selectTextField(){
var filter_text = document.getElementById('filter');
filter_text.focus();
filter_text.select();
}
</script>
</head>
"}
//body tag start + onload and onkeypress (onkeyup) javascript event calls
dat += "<body onload='selectTextField(); updateSearch();' onkeyup='updateSearch();'>"
//title + search bar
dat += {"
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable'>
<tr id='title_tr'>
<td align='center'>
<font size='5'><b>Player panel</b></font><br>
Hover over a line to see more information - <a href='?_src_=holder;check_antagonist=1'>Check antagonists</a> - Kick <a href='?_src_=holder;kick_all_from_lobby=1;afkonly=0'>everyone</a>/<a href='?_src_=holder;kick_all_from_lobby=1;afkonly=1'>AFKers</a> in lobby
<p>
</td>
</tr>
<tr id='search_tr'>
<td align='center'>
<b>Search:</b> <input type='text' id='filter' value='' style='width:300px;'>
</td>
</tr>
</table>
"}
//player table header
dat += {"
<span id='maintable_data_archive'>
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable_data'>"}
var/list/mobs = sortmobs()
var/i = 1
for(var/mob/M in mobs)
if(M.ckey)
var/color = "#e6e6e6"
if(i%2 == 0)
color = "#f2f2f2"
var/is_antagonist = is_special_character(M)
var/M_job = ""
if(isliving(M))
if(iscarbon(M)) //Carbon stuff
if(ishuman(M))
M_job = M.job
else if(ismonkey(M))
M_job = "Monkey"
else if(isalien(M)) //aliens
if(islarva(M))
M_job = "Alien larva"
else
M_job = "Alien"
else
M_job = "Carbon-based"
else if(issilicon(M)) //silicon
if(isAI(M))
M_job = "AI"
else if(ispAI(M))
M_job = "pAI"
else if(isrobot(M))
M_job = "Cyborg"
else
M_job = "Silicon-based"
else if(isanimal(M)) //simple animals
if(iscorgi(M))
M_job = "Corgi"
else if(isslime(M))
M_job = "slime"
else
M_job = "Animal"
else
M_job = "Living"
else if(istype(M,/mob/new_player))
M_job = "New player"
else if(isobserver(M))
var/mob/dead/observer/O = M
if(O.started_as_observer)//Did they get BTFO or are they just not trying?
M_job = "Observer"
else
M_job = "Ghost"
var/M_name = html_encode(M.name)
var/M_rname = html_encode(M.real_name)
var/M_key = html_encode(M.key)
//output for each mob
dat += {"
<tr id='data[i]' name='[i]' onClick="addToLocked('item[i]','data[i]','notice_span[i]')">
<td align='center' bgcolor='[color]'>
<span id='notice_span[i]'></span>
<a id='link[i]'
onmouseover='expand("item[i]","[M_job]","[M_name]","[M_rname]","--unused--","[M_key]","[M.lastKnownIP]",[is_antagonist],"\ref[M]")'
>
<b id='search[i]'>[M_name] - [M_rname] - [M_key] ([M_job])</b>
</a>
<br><span id='item[i]'></span>
</td>
</tr>
"}
i++
//player table ending
dat += {"
</table>
</span>
<script type='text/javascript'>
var maintable = document.getElementById("maintable_data_archive");
var complete_list = maintable.innerHTML;
</script>
</body></html>
"}
usr << browse(dat, "window=players;size=600x480")
/datum/admins/proc/check_antagonists()
if (ticker && ticker.current_state >= GAME_STATE_PLAYING)
var/dat = "<html><head><title>Round Status</title></head><body><h1><B>Round Status</B></h1>"
if(ticker.mode.replacementmode)
dat += "Former Game Mode: <B>[ticker.mode.name]</B><BR>"
dat += "Replacement Game Mode: <B>[ticker.mode.replacementmode.name]</B><BR>"
else
dat += "Current Game Mode: <B>[ticker.mode.name]</B><BR>"
dat += "Round Duration: <B>[round(world.time / 36000)]:[add_zero("[world.time / 600 % 60]", 2)]:[world.time / 100 % 6][world.time / 100 % 10]</B><BR>"
dat += "<B>Emergency shuttle</B><BR>"
if(EMERGENCY_IDLE_OR_RECALLED)
dat += "<a href='?_src_=holder;call_shuttle=1'>Call Shuttle</a><br>"
else
var/timeleft = SSshuttle.emergency.timeLeft()
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
dat += "ETA: <a href='?_src_=holder;edit_shuttle_time=1'>[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]</a><BR>"
dat += "<a href='?_src_=holder;call_shuttle=2'>Send Back</a><br>"
else
dat += "ETA: <a href='?_src_=holder;edit_shuttle_time=1'>[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]</a><BR>"
dat += "<B>Continuous Round Status</B><BR>"
dat += "<a href='?_src_=holder;toggle_continuous=1'>[config.continuous[ticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]</a>"
if(config.continuous[ticker.mode.config_tag])
dat += ", <a href='?_src_=holder;toggle_midround_antag=1'>[config.midround_antag[ticker.mode.config_tag] ? "creating replacement antagonists" : "not creating new antagonists"]</a><BR>"
else
dat += "<BR>"
if(config.midround_antag[ticker.mode.config_tag])
dat += "Time limit: <a href='?_src_=holder;alter_midround_time_limit=1'>[config.midround_antag_time_check] minutes into round</a><BR>"
dat += "Living crew limit: <a href='?_src_=holder;alter_midround_life_limit=1'>[config.midround_antag_life_check * 100]% of crew alive</a><BR>"
dat += "If limits past: <a href='?_src_=holder;toggle_noncontinuous_behavior=1'>[ticker.mode.round_ends_with_antag_death ? "End The Round" : "Continue As Extended"]</a><BR>"
dat += "<BR>"
dat += "<a href='?_src_=holder;end_round=\ref[usr]'>End Round Now</a><br>"
dat += "<a href='?_src_=holder;delay_round_end=1'>[ticker.delay_end ? "End Round Normally" : "Delay Round End"]</a><br>"
if(ticker.mode.syndicates.len)
dat += "<br><table cellspacing=5><tr><td><B>Syndicates</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.syndicates)
var/mob/M = N.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
else
dat += "<tr><td><i><a href='?_src_=vars;Vars=\ref[N]'>[N.name]([N.key])</a> Nuclear Operative Body destroyed!</i></td>"
dat += "<td><A href='?priv_msg=[N.key]'>PM</A></td></tr>"
dat += "</table><br><table><tr><td><B>Nuclear Disk(s)</B></td></tr>"
for(var/obj/item/weapon/disk/nuclear/N in poi_list)
dat += "<tr><td>[N.name], "
var/atom/disk_loc = N.loc
while(!istype(disk_loc, /turf))
if(istype(disk_loc, /mob))
var/mob/M = disk_loc
dat += "carried by <a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a> "
if(istype(disk_loc, /obj))
var/obj/O = disk_loc
dat += "in \a [O.name] "
disk_loc = disk_loc.loc
dat += "in [disk_loc.loc] at ([disk_loc.x], [disk_loc.y], [disk_loc.z])</td></tr>"
dat += "</table>"
if(ticker.mode.head_revolutionaries.len || ticker.mode.revolutionaries.len)
dat += "<br><table cellspacing=5><tr><td><B>Revolutionaries</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.head_revolutionaries)
var/mob/M = N.current
if(!M)
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[N]'>[N.name]([N.key])</a><i>Head Revolutionary body destroyed!</i></td>"
dat += "<td><A href='?priv_msg=[N.key]'>PM</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a> <b>(Leader)</b>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
for(var/datum/mind/N in ticker.mode.revolutionaries)
var/mob/M = N.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
dat += "</table><table cellspacing=5><tr><td><B>Target(s)</B></td><td></td><td><B>Location</B></td></tr>"
for(var/datum/mind/N in ticker.mode.get_living_heads())
var/mob/M = N.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td>"
var/turf/mob_loc = get_turf(M)
dat += "<td>[mob_loc.loc]</td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[N]'>[N.name]([N.key])</a><i>Head body destroyed!</i></td>"
dat += "<td><A href='?priv_msg=[N.key]'>PM</A></td></tr>"
dat += "</table>"
for(var/datum/gang/G in ticker.mode.gangs)
dat += "<br><table cellspacing=5><tr><td><B>[G.name] Gang: <a href='?_src_=holder;gangpoints=\ref[G]'>[G.points] Influence</a> | [round((G.territory.len/start_state.num_territories)*100, 1)]% Control</B></td><td></td></tr>"
for(var/datum/mind/N in G.bosses)
var/mob/M = N.current
if(!M)
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[N]'>[N.name]([N.key])</a><i>Gang Boss body destroyed!</i></td>"
dat += "<td><A href='?priv_msg=[N.key]'>PM</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a> <b>(Boss)</b>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
for(var/datum/mind/N in G.gangsters)
var/mob/M = N.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td></tr>"
dat += "</table>"
if(ticker.mode.changelings.len > 0)
dat += "<br><table cellspacing=5><tr><td><B>Changelings</B></td><td></td><td></td></tr>"
for(var/datum/mind/changeling in ticker.mode.changelings)
var/mob/M = changeling.current
if(M)
dat += "<tr><td>[M.mind.changeling.changelingID] as <a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td>"
dat += "<td><A HREF='?_src_=holder;traitor=\ref[M]'>Show Objective</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[changeling]'>[changeling.name]([changeling.key])</a><i>Changeling body destroyed!</i></td>"
dat += "<td><A href='?priv_msg=[changeling.key]'>PM</A></td></tr>"
dat += "</table>"
if(ticker.mode.wizards.len > 0)
dat += "<br><table cellspacing=5><tr><td><B>Wizards</B></td><td></td><td></td></tr>"
for(var/datum/mind/wizard in ticker.mode.wizards)
var/mob/M = wizard.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td>"
dat += "<td><A HREF='?_src_=holder;traitor=\ref[M]'>Show Objective</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[wizard]'>[wizard.name]([wizard.key])</a><i>Wizard body destroyed!</i></td></tr>"
dat += "<td><A href='?priv_msg=[wizard.key]'>PM</A></td></tr>"
dat += "</table>"
if(ticker.mode.apprentices.len > 0)
dat += "<br><table cellspacing=5><tr><td><B>Apprentice</B></td><td></td><td></td></tr>"
for(var/datum/mind/apprentice in ticker.mode.apprentices)
var/mob/M = apprentice.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td>"
dat += "<td><A HREF='?_src_=holder;traitor=\ref[M]'>Show Objective</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[apprentice]'>[apprentice.name]([apprentice.key])</a><i>Apprentice body destroyed!!</i></td></tr>"
dat += "<td><A href='?priv_msg=[apprentice.key]'>PM</A></td></tr>"
dat += "</table>"
if(ticker.mode.cult.len)
dat += "<br><table cellspacing=5><tr><td><B>Cultists</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.cult)
var/mob/M = N.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
dat += "</table>"
if(ticker.mode.servants_of_ratvar.len)
dat += "<br><table cellspacing=5><tr><td><B>Servants of Ratvar</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.servants_of_ratvar)
var/mob/M = N.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
dat += "</table>"
if(ticker.mode.red_deities.len || ticker.mode.red_deity_prophets.len || ticker.mode.blue_deity_prophets.len || ticker.mode.red_deity_followers.len || ticker.mode.blue_deity_followers.len)
dat += "<br><table cellspacing=5><tr><td><B>Red Deity</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.red_deities)
var/mob/M = N.current
if(M)
dat += "<tr><td>Red Deity: <a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
dat += "</table>"
if(ticker.mode.blue_deities.len)
dat += "<br><table cellspacing=5><tr><td><B>Blue Deity</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.blue_deities)
var/mob/M = N.current
if(M)
dat += "<tr><td>Blue Deity: <a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
dat += "</table>"
if(ticker.mode.red_deity_prophets.len)
dat += "<br><table cellspacing=5><tr><td><B>Red Deity Prophets</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.red_deity_prophets)
var/mob/M = N.current
if(M)
dat += "<tr><td>Red Deity Prophet: <a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
dat += "</table>"
if(ticker.mode.blue_deity_prophets.len)
dat += "<br><table cellspacing=5><tr><td><B>Blue Deity Prophets</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.blue_deity_prophets)
var/mob/M = N.current
if(M)
dat += "<tr><td>Blue Deity Prophet: <a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
dat += "</table>"
if(ticker.mode.red_deity_followers.len)
dat += "<br><table cellspacing=5><tr><td><B>Red Deity Followers</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.red_deity_followers)
var/mob/M = N.current
if(M)
dat += "<tr><td>Red Deity Followers: <a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
dat += "</table>"
if(ticker.mode.blue_deity_followers.len)
dat += "<br><table cellspacing=5><tr><td><B>Blue Deity Followers</B></td><td></td></tr>"
for(var/datum/mind/N in ticker.mode.blue_deity_followers)
var/mob/M = N.current
if(M)
dat += "<tr><td>Blue Deity Followers: <a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
dat += "</table>"
if(ticker.mode.traitors.len > 0)
dat += "<br><table cellspacing=5><tr><td><B>Traitors</B></td><td></td><td></td></tr>"
for(var/datum/mind/traitor in ticker.mode.traitors)
var/mob/M = traitor.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td>"
dat += "<td><A HREF='?_src_=holder;traitor=\ref[M]'>Show Objective</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[traitor]'>[traitor.name]([traitor.key])</a><i>Traitor body destroyed!</i></td>"
dat += "<td><A href='?priv_msg=[traitor.key]'>PM</A></td></tr>"
dat += "</table>"
if(ticker.mode.abductors.len)
dat += "<br><table cellspacing=5><tr><td><B>Abductors</B></td><td></td><td></td></tr>"
for(var/datum/mind/abductor in ticker.mode.abductors)
var/mob/M = abductor.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td>"
dat += "<td><A HREF='?_src_=holder;traitor=\ref[M]'>Show Objective</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[abductor]'>[abductor.name]([abductor.key])</a><i>Abductor body destroyed!</i></td></tr>"
dat += "<td><A href='?priv_msg=[abductor.key]'>PM</A></td>"
dat += "</table>"
dat += "<br><table cellspacing=5><tr><td><B>Abductees</B></td><td></td><td></td></tr>"
for(var/obj/machinery/abductor/experiment/E in machines)
for(var/datum/mind/abductee in E.abductee_minds)
var/mob/M = abductee.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td>"
dat += "<td><A HREF='?_src_=holder;traitor=\ref[M]'>Show Objective</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[abductee]'>[abductee.name]([abductee.key])</a><i>Abductee body destroyed!</i></td>"
dat += "<td><A href='?priv_msg=[abductee.key]'>PM</A></td></tr>"
dat += "</table>"
if(ticker.mode.devils.len)
dat += "<br><table cellspacing=5><tr><td><B>devils</B></td><td></td><td></td></tr>"
for(var/X in ticker.mode.devils)
var/datum/mind/devil = X
var/mob/M = devil.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name] : [devil.devilinfo.truename]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A HREF='?_src_=holder;traitor=\ref[M]'>Show Objective</A></td></tr>"
dat += "<td><A HREF='?_src_=holder;admincheckdevilinfo=\ref[M]'>Show all devil info</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[devil]'>[devil.name] : [devil.devilinfo.truename] ([devil.key])</a><i>devil body destroyed!</i></td></tr>"
dat += "<td><A href='?priv_msg=[devil.key]'>PM</A></td>"
dat += "</table>"
if(ticker.mode.sintouched.len)
dat += "<br><table cellspacing=5><tr><td><B>sintouched</B></td><td></td><td></td></tr>"
for(var/X in ticker.mode.sintouched)
var/datum/mind/sintouched = X
var/mob/M = sintouched.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A HREF='?_src_=holder;traitor=\ref[M]'>Show Objective</A></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[sintouched]'>[sintouched.name]([sintouched.key])</a><i>sintouched body destroyed!</i></td></tr>"
dat += "<td><A href='?priv_msg=[sintouched.key]'>PM</A></td>"
dat += "</table>"
var/list/blob_minds = list()
for(var/mob/camera/blob/B in mob_list)
blob_minds |= B.mind
if(istype(ticker.mode, /datum/game_mode/blob) || blob_minds.len)
dat += "<br><table cellspacing=5><tr><td><B>Blob</B></td><td></td><td></td></tr>"
if(istype(ticker.mode,/datum/game_mode/blob))
var/datum/game_mode/blob/mode = ticker.mode
blob_minds |= mode.blob_overminds
dat += "<tr><td><i>Progress: [blobs_legit.len]/[mode.blobwincount]</i></td></tr>"
for(var/datum/mind/blob in blob_minds)
var/mob/M = blob.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[blob]'>[blob.name]([blob.key])</a><i>Blob not found!</i></td>"
dat += "<td><A href='?priv_msg=[blob.key]'>PM</A></td></tr>"
dat += "</table>"
if(istype(ticker.mode, /datum/game_mode/monkey))
var/datum/game_mode/monkey/mode = ticker.mode
dat += "<br><table cellspacing=5><tr><td><B>Monkey</B></td><td></td><td></td></tr>"
for(var/datum/mind/eek in mode.ape_infectees)
var/mob/M = eek.current
if(M)
dat += "<tr><td><a href='?_src_=holder;adminplayeropts=\ref[M]'>[M.real_name]</a>[M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
dat += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
dat += "<td><A href='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</a></td></tr>"
else
dat += "<tr><td><a href='?_src_=vars;Vars=\ref[eek]'>[eek.name]([eek.key])</a><i>Monkey not found!</i></td>"
dat += "<td><A href='?priv_msg=[eek.key]'>PM</A></td></tr>"
dat += "</table>"
dat += "</body></html>"
usr << browse(dat, "window=roundstatus;size=420x500")
else
alert("The game hasn't started yet!")
+600
View File
@@ -0,0 +1,600 @@
/datum/admins/proc/Secrets()
if(!check_rights(0)) return
var/dat = "<B>The first rule of adminbuse is: you don't talk about the adminbuse.</B><HR>"
dat +={"
<B>General Secrets</B><BR>
<BR>
<A href='?src=\ref[src];secrets=list_job_debug'>Show Job Debug</A><BR>
<A href='?src=\ref[src];secrets=admin_log'>Admin Log</A><BR>
<A href='?src=\ref[src];secrets=show_admins'>Show Admin List</A><BR>
<BR>
"}
if(check_rights(R_ADMIN,0))
dat += {"
<B>Admin Secrets</B><BR>
<BR>
<A href='?src=\ref[src];secrets=clear_virus'>Cure all diseases currently in existence</A><BR>
<A href='?src=\ref[src];secrets=list_bombers'>Bombing List</A><BR>
<A href='?src=\ref[src];secrets=check_antagonist'>Show current traitors and objectives</A><BR>
<A href='?src=\ref[src];secrets=list_signalers'>Show last [length(lastsignalers)] signalers</A><BR>
<A href='?src=\ref[src];secrets=list_lawchanges'>Show last [length(lawchanges)] law changes</A><BR>
<A href='?src=\ref[src];secrets=showailaws'>Show AI Laws</A><BR>
<A href='?src=\ref[src];secrets=showgm'>Show Game Mode</A><BR>
<A href='?src=\ref[src];secrets=manifest'>Show Crew Manifest</A><BR>
<A href='?src=\ref[src];secrets=DNA'>List DNA (Blood)</A><BR>
<A href='?src=\ref[src];secrets=fingerprints'>List Fingerprints</A><BR>
<A href='?src=\ref[src];secrets=ctfbutton'>Enable/Disable CTF</A><BR><BR>
<A href='?src=\ref[src];secrets=tdomereset'>Reset Thunderdome to default state</A><BR>
<A href='?src=\ref[src];secrets=reset_name'>Reset Station Name</A><BR>
<BR>
<B>Shuttles</B><BR>
<BR>
<A href='?src=\ref[src];secrets=moveferry'>Move Ferry</A><BR>
<A href='?src=\ref[src];secrets=moveminingshuttle'>Move Mining Shuttle</A><BR>
<A href='?src=\ref[src];secrets=movelaborshuttle'>Move Labor Shuttle</A><BR>
<BR>
"}
if(check_rights(R_FUN,0))
dat += {"
<B>Fun Secrets</B><BR>
<BR>
<A href='?src=\ref[src];secrets=virus'>Trigger a Virus Outbreak</A><BR>
<A href='?src=\ref[src];secrets=monkey'>Turn all humans into monkeys</A><BR>
<A href='?src=\ref[src];secrets=anime'>Chinese Cartoons</A><BR>
<A href='?src=\ref[src];secrets=allspecies'>Change the species of all humans</A><BR>
<A href='?src=\ref[src];secrets=power'>Make all areas powered</A><BR>
<A href='?src=\ref[src];secrets=unpower'>Make all areas unpowered</A><BR>
<A href='?src=\ref[src];secrets=quickpower'>Power all SMES</A><BR>
<A href='?src=\ref[src];secrets=tripleAI'>Triple AI mode (needs to be used in the lobby)</A><BR>
<A href='?src=\ref[src];secrets=traitor_all'>Everyone is the traitor</A><BR>
<A href='?src=\ref[src];secrets=guns'>Summon Guns</A><BR>
<A href='?src=\ref[src];secrets=magic'>Summon Magic</A><BR>
<A href='?src=\ref[src];secrets=events'>Summon Events (Toggle)</A><BR>
<A href='?src=\ref[src];secrets=onlyone'>There can only be one!</A><BR>
<A href='?src=\ref[src];secrets=onlyme'>There can only be me!</A><BR>
<A href='?src=\ref[src];secrets=retardify'>Make all players retarded</A><BR>
<A href='?src=\ref[src];secrets=eagles'>Egalitarian Station Mode</A><BR>
<A href='?src=\ref[src];secrets=blackout'>Break all lights</A><BR>
<A href='?src=\ref[src];secrets=whiteout'>Fix all lights</A><BR>
<A href='?src=\ref[src];secrets=floorlava'>The floor is lava! (DANGEROUS: extremely lame)</A><BR>
<BR>
<A href='?src=\ref[src];secrets=changebombcap'>Change bomb cap</A><BR>
<A href='?src=\ref[src];secrets=masspurrbation'>Mass Purrbation</A><BR>
<A href='?src=\ref[src];secrets=massremovepurrbation'>Mass Remove Purrbation</A><BR>
"}
dat += "<BR>"
if(check_rights(R_DEBUG,0))
dat += {"
<B>Security Level Elevated</B><BR>
<BR>
<A href='?src=\ref[src];secrets=maint_access_engiebrig'>Change all maintenance doors to engie/brig access only</A><BR>
<A href='?src=\ref[src];secrets=maint_access_brig'>Change all maintenance doors to brig access only</A><BR>
<A href='?src=\ref[src];secrets=infinite_sec'>Remove cap on security officers</A><BR>
<BR>
"}
usr << browse(dat, "window=secrets")
return
/datum/admins/proc/Secrets_topic(item,href_list)
var/datum/round_event/E
var/ok = 0
switch(item)
if("admin_log")
var/dat = "<B>Admin Log<HR></B>"
for(var/l in admin_log)
dat += "<li>[l]</li>"
if(!admin_log.len)
dat += "No-one has done anything this round!"
usr << browse(dat, "window=admin_log")
if("list_job_debug")
var/dat = "<B>Job Debug info.</B><HR>"
if(SSjob)
for(var/line in SSjob.job_debug)
dat += "[line]<BR>"
dat+= "*******<BR><BR>"
for(var/datum/job/job in SSjob.occupations)
if(!job)
continue
dat += "job: [job.title], current_positions: [job.current_positions], total_positions: [job.total_positions] <BR>"
usr << browse(dat, "window=jobdebug;size=600x500")
if("show_admins")
var/dat = "<B>Current admins:</B><HR>"
if(admin_datums)
for(var/ckey in admin_datums)
var/datum/admins/D = admin_datums[ckey]
dat += "[ckey] - [D.rank.name]<br>"
usr << browse(dat, "window=showadmins;size=600x500")
if("tdomereset")
if(!check_rights(R_ADMIN))
return
var/delete_mobs = alert("Clear all mobs?","Confirm","Yes","No","Cancel")
if(delete_mobs == "Cancel")
return
log_admin("[key_name(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].", 1)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].</span>")
var/area/thunderdome = locate(/area/tdome/arena)
if(delete_mobs == "Yes")
for(var/mob/living/mob in thunderdome)
qdel(mob) //Clear mobs
for(var/obj/obj in thunderdome)
if(!istype(obj,/obj/machinery/camera))
qdel(obj) //Clear objects
var/area/template = locate(/area/tdome/arena_source)
template.copy_contents_to(thunderdome)
if("clear_virus")
var/choice = input("Are you sure you want to cure all disease?") in list("Yes", "Cancel")
if(choice == "Yes")
message_admins("[key_name_admin(usr)] has cured all diseases.")
for(var/datum/disease/D in SSdisease.processing)
D.cure(D)
if("reset_name")
if(!check_rights(R_ADMIN))
return
world.name = new_station_name()
station_name = world.name
log_admin("[key_name(usr)] reset the station name.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] reset the station name.</span>")
if("list_bombers")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Bombing List<HR>"
for(var/l in bombers)
dat += text("[l]<BR>")
usr << browse(dat, "window=bombers")
if("list_signalers")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing last [length(lastsignalers)] signalers.</B><HR>"
for(var/sig in lastsignalers)
dat += "[sig]<BR>"
usr << browse(dat, "window=lastsignalers;size=800x500")
if("list_lawchanges")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing last [length(lawchanges)] law changes.</B><HR>"
for(var/sig in lawchanges)
dat += "[sig]<BR>"
usr << browse(dat, "window=lawchanges;size=800x500")
if("moveminingshuttle")
if(!check_rights(R_ADMIN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","ShM")
if(!SSshuttle.toggleShuttle("mining","mining_home","mining_away"))
message_admins("[key_name_admin(usr)] moved mining shuttle")
log_admin("[key_name(usr)] moved the mining shuttle")
if("movelaborshuttle")
if(!check_rights(R_ADMIN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","ShL")
if(!SSshuttle.toggleShuttle("laborcamp","laborcamp_home","laborcamp_away"))
message_admins("[key_name_admin(usr)] moved labor shuttle")
log_admin("[key_name(usr)] moved the labor shuttle")
if("moveferry")
if(!check_rights(R_ADMIN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","ShF")
if(!SSshuttle.toggleShuttle("ferry","ferry_home","ferry_away"))
message_admins("[key_name_admin(usr)] moved the centcom ferry")
log_admin("[key_name(usr)] moved the centcom ferry")
if("showailaws")
if(!check_rights(R_ADMIN))
return
output_ai_laws()
if("showgm")
if(!check_rights(R_ADMIN))
return
if(!ticker || !ticker.mode)
alert("The game hasn't started yet!")
else if (ticker.mode)
alert("The game mode is [ticker.mode.name]")
else alert("For some reason there's a ticker, but not a game mode")
if("manifest")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing Crew Manifest.</B><HR>"
dat += "<table cellspacing=5><tr><th>Name</th><th>Position</th></tr>"
for(var/datum/data/record/t in data_core.general)
dat += "<tr><td>[t.fields["name"]]</td><td>[t.fields["rank"]]</td></tr>"
dat += "</table>"
usr << browse(dat, "window=manifest;size=440x410")
if("DNA")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing DNA from blood.</B><HR>"
dat += "<table cellspacing=5><tr><th>Name</th><th>DNA</th><th>Blood Type</th></tr>"
for(var/mob/living/carbon/human/H in mob_list)
if(H.ckey)
dat += "<tr><td>[H]</td><td>[H.dna.unique_enzymes]</td><td>[H.dna.blood_type]</td></tr>"
dat += "</table>"
usr << browse(dat, "window=DNA;size=440x410")
if("fingerprints")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing Fingerprints.</B><HR>"
dat += "<table cellspacing=5><tr><th>Name</th><th>Fingerprints</th></tr>"
for(var/mob/living/carbon/human/H in mob_list)
if(H.ckey)
dat += "<tr><td>[H]</td><td>[md5(H.dna.uni_identity)]</td></tr>"
dat += "</table>"
usr << browse(dat, "window=fingerprints;size=440x410")
if("monkey")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","M")
for(var/mob/living/carbon/human/H in mob_list)
spawn(0)
H.monkeyize()
ok = 1
if("allspecies")
if(!check_rights(R_FUN))
return
var/result = input(usr, "Please choose a new species","Species") as null|anything in species_list
if(result)
log_admin("[key_name(usr)] turned all humans into [result]", 1)
message_admins("\blue [key_name_admin(usr)] turned all humans into [result]")
var/newtype = species_list[result]
for(var/mob/living/carbon/human/H in mob_list)
H.set_species(newtype)
if("corgi")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","M")
for(var/mob/living/carbon/human/H in mob_list)
spawn(0)
H.corgize()
ok = 1
if("tripleAI")
if(!check_rights(R_FUN))
return
usr.client.triple_ai()
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","TriAI")
if("power")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","P")
log_admin("[key_name(usr)] made all areas powered", 1)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made all areas powered</span>")
power_restore()
if("unpower")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","UP")
log_admin("[key_name(usr)] made all areas unpowered", 1)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made all areas unpowered</span>")
power_failure()
if("quickpower")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","QP")
log_admin("[key_name(usr)] made all SMESs powered", 1)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made all SMESs powered</span>")
power_restore_quick()
if("traitor_all")
if(!check_rights(R_FUN))
return
if(!ticker || !ticker.mode)
alert("The game hasn't started yet!")
return
var/objective = copytext(sanitize(input("Enter an objective")),1,MAX_MESSAGE_LEN)
if(!objective)
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","TA([objective])")
for(var/mob/living/carbon/human/H in player_list)
if(H.stat == 2 || !H.client || !H.mind) continue
if(is_special_character(H)) continue
//traitorize(H, objective, 0)
ticker.mode.traitors += H.mind
H.mind.special_role = "traitor"
var/datum/objective/new_objective = new
new_objective.owner = H
new_objective.explanation_text = objective
H.mind.objectives += new_objective
ticker.mode.greet_traitor(H.mind)
//ticker.mode.forge_traitor_objectives(H.mind)
ticker.mode.finalize_traitor(H.mind)
for(var/mob/living/silicon/A in player_list)
if(A.stat == 2 || !A.client || !A.mind) continue
if(ispAI(A)) continue
else if(is_special_character(A)) continue
ticker.mode.traitors += A.mind
A.mind.special_role = "traitor"
var/datum/objective/new_objective = new
new_objective.owner = A
new_objective.explanation_text = objective
A.mind.objectives += new_objective
ticker.mode.greet_traitor(A.mind)
ticker.mode.finalize_traitor(A.mind)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]</span>")
log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]")
if("changebombcap")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","BC")
var/newBombCap = input(usr,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be above 4)", "New Bomb Cap", MAX_EX_LIGHT_RANGE) as num|null
if (newBombCap < 4)
return
MAX_EX_DEVESTATION_RANGE = round(newBombCap/4)
MAX_EX_HEAVY_RANGE = round(newBombCap/2)
MAX_EX_LIGHT_RANGE = newBombCap
//I don't know why these are their own variables, but fuck it, they are.
MAX_EX_FLASH_RANGE = newBombCap
MAX_EX_FLAME_RANGE = newBombCap
message_admins("<span class='boldannounce'>[key_name_admin(usr)] changed the bomb cap to [MAX_EX_DEVESTATION_RANGE], [MAX_EX_HEAVY_RANGE], [MAX_EX_LIGHT_RANGE]</span>")
log_admin("[key_name(usr)] changed the bomb cap to [MAX_EX_DEVESTATION_RANGE], [MAX_EX_HEAVY_RANGE], [MAX_EX_LIGHT_RANGE]")
if("lightsout")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","LO")
message_admins("[key_name_admin(usr)] has broke a lot of lights")
E = new /datum/round_event/electrical_storm{lightsoutAmount = 2}()
if("blackout")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","BO")
message_admins("[key_name_admin(usr)] broke all lights")
for(var/obj/machinery/light/L in machines)
L.broken()
if("anime")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","CC")
message_admins("[key_name_admin(usr)] made everything kawaii.")
for(var/mob/living/carbon/human/H in mob_list)
H << sound('sound/AI/animes.ogg')
if(H.dna.species.id == "human")
if(H.dna.features["tail_human"] == "None" || H.dna.features["ears"] == "None")
H.dna.features["tail_human"] = "Cat"
H.dna.features["ears"] = "Cat"
var/seifuku = pick(typesof(/obj/item/clothing/under/schoolgirl))
var/obj/item/clothing/under/schoolgirl/I = new seifuku
var/list/honorifics = list("[MALE]" = list("kun"), "[FEMALE]" = list("chan","tan"), "[NEUTER]" = list("san")) //John Robust -> Robust-kun
var/list/names = splittext(H.real_name," ")
var/forename = names.len > 1 ? names[2] : names[1]
var/newname = "[forename]-[pick(honorifics["[H.gender]"])]"
H.fully_replace_character_name(H.real_name,newname)
H.unEquip(H.w_uniform)
H.equip_to_slot_or_del(I, slot_w_uniform)
I.flags |= NODROP
else
H << "You're not kawaii enough for this."
if("whiteout")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","WO")
message_admins("[key_name_admin(usr)] fixed all lights")
for(var/obj/machinery/light/L in machines)
L.fix()
if("floorlava")
var/datum/weather/floor_is_lava/storm = new /datum/weather/floor_is_lava
storm.weather_start_up()
if("virus")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","V")
switch(alert("Do you want this to be a random disease or do you have something in mind?",,"Make Your Own","Random","Choose"))
if("Make Your Own")
AdminCreateVirus(usr.client)
if("Random")
E = new /datum/round_event/disease_outbreak()
if("Choose")
var/virus = input("Choose the virus to spread", "BIOHAZARD") as null|anything in typesof(/datum/disease)
E = new /datum/round_event/disease_outbreak{}()
var/datum/round_event/disease_outbreak/DO = E
DO.virus_type = virus
if("retardify")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","RET")
for(var/mob/living/carbon/human/H in player_list)
H << "<span class='boldannounce'>You suddenly feel stupid.</span>"
H.setBrainLoss(60)
message_admins("[key_name_admin(usr)] made everybody retarded")
if("eagles")//SCRAW
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","EgL")
for(var/obj/machinery/door/airlock/W in machines)
if(W.z == ZLEVEL_STATION && !istype(get_area(W), /area/bridge) && !istype(get_area(W), /area/crew_quarters) && !istype(get_area(W), /area/security/prison))
W.req_access = list()
message_admins("[key_name_admin(usr)] activated Egalitarian Station mode")
priority_announce("Centcom airlock control override activated. Please take this time to get acquainted with your coworkers.", null, 'sound/AI/commandreport.ogg')
if("guns")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","SG")
var/survivor_probability = 0
switch(alert("Do you want this to create survivors antagonists?",,"No Antags","Some Antags","All Antags!"))
if("Some Antags")
survivor_probability = 25
if("All Antags!")
survivor_probability = 100
rightandwrong(0, usr, survivor_probability)
if("magic")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","SM")
var/survivor_probability = 0
switch(alert("Do you want this to create survivors antagonists?",,"No Antags","Some Antags","All Antags!"))
if("Some Antags")
survivor_probability = 25
if("All Antags!")
survivor_probability = 100
rightandwrong(1, usr, survivor_probability)
if("events")
if(!check_rights(R_FUN))
return
if(!SSevent.wizardmode)
if(alert("Do you want to toggle summon events on?",,"Yes","No") == "Yes")
summonevents()
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","SE")
else
switch(alert("What would you like to do?",,"Intensify Summon Events","Turn Off Summon Events","Nothing"))
if("Intensify Summon Events")
summonevents()
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","SE")
if("Turn Off Summon Events")
SSevent.toggleWizardmode()
SSevent.resetFrequency()
if("dorf")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","DF")
for(var/mob/living/carbon/human/B in mob_list)
B.facial_hair_style = "Dward Beard"
B.update_hair()
message_admins("[key_name_admin(usr)] activated dorf mode")
if("onlyone")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","OO")
usr.client.only_one()
// message_admins("[key_name_admin(usr)] has triggered a battle to the death (only one)")
if("onlyme")
if(!check_rights(R_FUN))
return
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","OM")
only_me()
if("maint_access_brig")
if(!check_rights(R_DEBUG))
return
for(var/obj/machinery/door/airlock/maintenance/M in machines)
M.check_access()
if (access_maint_tunnels in M.req_access)
M.req_access = list(access_brig)
message_admins("[key_name_admin(usr)] made all maint doors brig access-only.")
if("maint_access_engiebrig")
if(!check_rights(R_DEBUG))
return
for(var/obj/machinery/door/airlock/maintenance/M in machines)
M.check_access()
if (access_maint_tunnels in M.req_access)
M.req_access = list()
M.req_one_access = list(access_brig,access_engine)
message_admins("[key_name_admin(usr)] made all maint doors engineering and brig access-only.")
if("infinite_sec")
if(!check_rights(R_DEBUG))
return
var/datum/job/J = SSjob.GetJob("Security Officer")
if(!J) return
J.total_positions = -1
J.spawn_positions = -1
message_admins("[key_name_admin(usr)] has removed the cap on security officers.")
if("ctfbutton")
if(!check_rights(R_ADMIN))
return
var/ctf_enabled = FALSE
for(var/obj/machinery/capture_the_flag/CTF in machines)
ctf_enabled = CTF.toggle_ctf()
message_admins("[key_name_admin(usr)] has [ctf_enabled? "enabled" : "disabled"] CTF!")
notify_ghosts("CTF has been [ctf_enabled? "enabled" : "disabled"]!",'sound/effects/ghost2.ogg')
if("masspurrbation")
if(!check_rights(R_FUN))
return
mass_purrbation()
message_admins("[key_name_admin(usr)] has put everyone on \
purrbation!")
log_admin("[key_name(usr)] has put everyone on purrbation.")
if("massremovepurrbation")
if(!check_rights(R_FUN))
return
mass_remove_purrbation()
message_admins("[key_name_admin(usr)] has removed everyone from \
purrbation.")
log_admin("[key_name(usr)] has removed everyone from purrbation.")
if(E)
E.processing = 0
if(E.announceWhen>0)
if(alert(usr, "Would you like to alert the crew?", "Alert", "Yes", "No") == "No")
E.announceWhen = -1
E.processing = 1
if (usr)
log_admin("[key_name(usr)] used secret [item]")
if (ok)
world << text("<B>A secret has been activated by []!</B>", usr.key)
+217
View File
@@ -0,0 +1,217 @@
/proc/add_note(target_ckey, notetext, timestamp, adminckey, logged = 1, server)
if(!dbcon.IsConnected())
usr << "<span class='danger'>Failed to establish database connection.</span>"
return
if(!target_ckey)
var/new_ckey = ckey(input(usr,"Who would you like to add a note for?","Enter a ckey",null) as text)
if(!new_ckey)
return
new_ckey = sanitizeSQL(new_ckey)
var/DBQuery/query_find_ckey = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[new_ckey]'")
if(!query_find_ckey.Execute())
var/err = query_find_ckey.ErrorMsg()
log_game("SQL ERROR obtaining ckey from player table. Error : \[[err]\]\n")
return
if(!query_find_ckey.NextRow())
if(alert(usr, "[new_ckey] has not been seen before, are you sure you want to add them to the watchlist?", "Unknown ckey", "Yes", "No", "Cancel") != "Yes")
return
target_ckey = new_ckey
var/target_sql_ckey = sanitizeSQL(target_ckey)
if(!notetext)
notetext = input(usr,"Write your Note","Add Note") as message
if(!notetext)
return
notetext = sanitizeSQL(notetext)
if(!timestamp)
timestamp = SQLtime()
if(!adminckey)
adminckey = usr.ckey
if(!adminckey)
return
var/admin_sql_ckey = sanitizeSQL(adminckey)
if(!server)
if (config && config.server_name)
server = config.server_name
server = sanitizeSQL(server)
var/DBQuery/query_noteadd = dbcon.NewQuery("INSERT INTO [format_table_name("notes")] (ckey, timestamp, notetext, adminckey, server) VALUES ('[target_sql_ckey]', '[timestamp]', '[notetext]', '[admin_sql_ckey]', '[server]')")
if(!query_noteadd.Execute())
var/err = query_noteadd.ErrorMsg()
log_game("SQL ERROR adding new note to table. Error : \[[err]\]\n")
return
if(logged)
log_admin("[key_name(usr)] has added a note to [target_ckey]: [notetext]")
message_admins("[key_name_admin(usr)] has added a note to [target_ckey]:<br>[notetext]")
show_note(target_ckey)
/proc/remove_note(note_id)
var/ckey
var/notetext
var/adminckey
if(!dbcon.IsConnected())
usr << "<span class='danger'>Failed to establish database connection.</span>"
return
if(!note_id)
return
note_id = text2num(note_id)
var/DBQuery/query_find_note_del = dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]")
if(!query_find_note_del.Execute())
var/err = query_find_note_del.ErrorMsg()
log_game("SQL ERROR obtaining ckey, notetext, adminckey from notes table. Error : \[[err]\]\n")
return
if(query_find_note_del.NextRow())
ckey = query_find_note_del.item[1]
notetext = query_find_note_del.item[2]
adminckey = query_find_note_del.item[3]
var/DBQuery/query_del_note = dbcon.NewQuery("DELETE FROM [format_table_name("notes")] WHERE id = [note_id]")
if(!query_del_note.Execute())
var/err = query_del_note.ErrorMsg()
log_game("SQL ERROR removing note from table. Error : \[[err]\]\n")
return
log_admin("[key_name(usr)] has removed a note made by [adminckey] from [ckey]: [notetext]")
message_admins("[key_name_admin(usr)] has removed a note made by [adminckey] from [ckey]:<br>[notetext]")
show_note(ckey)
/proc/edit_note(note_id)
if(!dbcon.IsConnected())
usr << "<span class='danger'>Failed to establish database connection.</span>"
return
if(!note_id)
return
note_id = text2num(note_id)
var/target_ckey
var/sql_ckey = sanitizeSQL(usr.ckey)
var/DBQuery/query_find_note_edit = dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]")
if(!query_find_note_edit.Execute())
var/err = query_find_note_edit.ErrorMsg()
log_game("SQL ERROR obtaining notetext from notes table. Error : \[[err]\]\n")
return
if(query_find_note_edit.NextRow())
target_ckey = query_find_note_edit.item[1]
var/old_note = query_find_note_edit.item[2]
var/adminckey = query_find_note_edit.item[3]
var/new_note = input("Input new note", "New Note", "[old_note]") as message
if(!new_note)
return
new_note = sanitizeSQL(new_note)
var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from<br>[old_note]<br>to<br>[new_note]<hr>"
edit_text = sanitizeSQL(edit_text)
var/DBQuery/query_update_note = dbcon.NewQuery("UPDATE [format_table_name("notes")] SET notetext = '[new_note]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [note_id]")
if(!query_update_note.Execute())
var/err = query_update_note.ErrorMsg()
log_game("SQL ERROR editing note. Error : \[[err]\]\n")
return
log_admin("[key_name(usr)] has edited [target_ckey]'s note made by [adminckey] from [old_note] to [new_note]")
message_admins("[key_name_admin(usr)] has edited [target_ckey]'s note made by [adminckey] from<br>[old_note]<br>to<br>[new_note]")
show_note(target_ckey)
/proc/show_note(target_ckey, index, linkless = 0)
var/output
var/navbar
var/ruler
ruler = "<hr style='background:#000000; border:0; height:3px'>"
navbar = "<a href='?_src_=holder;nonalpha=1'>\[All\]</a>|<a href='?_src_=holder;nonalpha=2'>\[#\]</a>"
for(var/letter in alphabet)
navbar += "|<a href='?_src_=holder;shownote=[letter]'>\[[letter]\]</a>"
navbar += "<br><form method='GET' name='search' action='?'>\
<input type='hidden' name='_src_' value='holder'>\
<input type='text' name='notessearch' value='[index]'>\
<input type='submit' value='Search'></form>"
if(!linkless)
output = navbar
if(target_ckey)
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/DBQuery/query_get_notes = dbcon.NewQuery("SELECT id, timestamp, notetext, adminckey, last_editor, server FROM [format_table_name("notes")] WHERE ckey = '[target_sql_ckey]' ORDER BY timestamp")
if(!query_get_notes.Execute())
var/err = query_get_notes.ErrorMsg()
log_game("SQL ERROR obtaining ckey, notetext, adminckey, last_editor, server from notes table. Error : \[[err]\]\n")
return
output += "<h2><center>Notes of [target_ckey]</center></h2>"
if(!linkless)
output += "<center><a href='?_src_=holder;addnote=[target_ckey]'>\[Add Note\]</a></center>"
output += ruler
while(query_get_notes.NextRow())
var/id = query_get_notes.item[1]
var/timestamp = query_get_notes.item[2]
var/notetext = query_get_notes.item[3]
var/adminckey = query_get_notes.item[4]
var/last_editor = query_get_notes.item[5]
var/server = query_get_notes.item[6]
output += "<b>[timestamp] | [server] | [adminckey]</b>"
if(!linkless)
output += " <a href='?_src_=holder;removenote=[id]'>\[Remove Note\]</a> <a href='?_src_=holder;editnote=[id]'>\[Edit Note\]</a>"
if(last_editor)
output += " <font size='2'>Last edit by [last_editor] <a href='?_src_=holder;noteedits=[id]'>(Click here to see edit log)</a></font>"
output += "<br>[notetext]<hr style='background:#000000; border:0; height:1px'>"
else if(index)
var/index_ckey
var/search
output += "<center><a href='?_src_=holder;addnoteempty=1'>\[Add Note\]</a></center>"
output += ruler
if(!isnum(index))
index = sanitizeSQL(index)
switch(index)
if(1)
search = "^."
if(2)
search = "^\[^\[:alpha:\]\]"
else
search = "^[index]"
var/DBQuery/query_list_notes = dbcon.NewQuery("SELECT DISTINCT ckey FROM [format_table_name("notes")] WHERE ckey REGEXP '[search]' ORDER BY ckey")
if(!query_list_notes.Execute())
var/err = query_list_notes.ErrorMsg()
log_game("SQL ERROR obtaining ckey from notes table. Error : \[[err]\]\n")
return
while(query_list_notes.NextRow())
index_ckey = query_list_notes.item[1]
output += "<a href='?_src_=holder;shownoteckey=[index_ckey]'>[index_ckey]</a><br>"
else
output += "<center><a href='?_src_=holder;addnoteempty=1'>\[Add Note\]</a></center>"
output += ruler
usr << browse(output, "window=show_notes;size=900x500")
#define NOTESFILE "data/player_notes.sav"
//if the AUTOCONVERT_NOTES is turned on, anytime a player connects this will be run to try and add all their notes to the databas
/proc/convert_notes_sql(ckey)
var/savefile/notesfile = new(NOTESFILE)
if(!notesfile)
log_game("Error: Cannot access [NOTESFILE]")
return
notesfile.cd = "/[ckey]"
while(!notesfile.eof)
var/notetext
notesfile >> notetext
var/server
if(config && config.server_name)
server = config.server_name
var/regex/note = new("^(\\d{2}-\\w{3}-\\d{4}) \\| (.+) ~(\\w+)$", "i")
note.Find(notetext)
var/timestamp = note.group[1]
notetext = note.group[2]
var/adminckey = note.group[3]
var/DBQuery/query_convert_time = dbcon.NewQuery("SELECT ADDTIME(STR_TO_DATE('[timestamp]','%d-%b-%Y'), '0')")
if(!query_convert_time.Execute())
var/err = query_convert_time.ErrorMsg()
log_game("SQL ERROR converting timestamp. Error : \[[err]\]\n")
return
if(query_convert_time.NextRow())
timestamp = query_convert_time.item[1]
if(ckey && notetext && timestamp && adminckey && server)
add_note(ckey, notetext, timestamp, adminckey, 0, server)
notesfile.cd = "/"
notesfile.dir.Remove(ckey)
/*alternatively this proc can be run once to pass through every note and attempt to convert it before deleting the file, if done then AUTOCONVERT_NOTES should be turned off
this proc can take several minutes to execute fully if converting and cause DD to hang if converting a lot of notes; it's not advised to do so while a server is live
/proc/mass_convert_notes()
world << "Beginning mass note conversion"
var/savefile/notesfile = new(NOTESFILE)
if(!notesfile)
log_game("Error: Cannot access [NOTESFILE]")
return
notesfile.cd = "/"
for(var/ckey in notesfile.dir)
convert_notes_sql(ckey)
world << "Deleting NOTESFILE"
fdel(NOTESFILE)
world << "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES"*/
#undef NOTESFILE
+204
View File
@@ -0,0 +1,204 @@
/datum/admins/proc/stickyban(action,data)
if(!check_rights(R_BAN))
return
switch (action)
if ("show")
stickyban_show()
if ("add")
var/list/ban = list()
var/ckey
ban["admin"] = usr.key
ban["type"] = list("sticky")
ban["reason"] = "(InGameBan)([usr.key])" //this will be displayed in dd only
if (data["ckey"])
ckey = ckey(data["ckey"])
else
ckey = input(usr,"Ckey","Ckey","") as text|null
if (!ckey)
return
ckey = ckey(ckey)
if (get_stickyban_from_ckey(ckey))
usr << "<span class='adminnotice'>Error: Can not add a stickyban: User already has a current sticky ban</span>"
if (data["reason"])
ban["message"] = data["reason"]
else
var/reason = input(usr,"Reason","Reason","Ban Evasion") as text|null
if (!reason)
return
ban["message"] = "[reason]"
world.SetConfig("ban",ckey,list2stickyban(ban))
log_admin("[key_name(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has stickybanned [ckey].\nReason: [ban["message"]]</span>")
if ("remove")
if (!data["ckey"])
return
var/ckey = data["ckey"]
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>"
return
if (alert("Are you sure you want to remove the sticky ban on [ckey]?","Are you sure","Yes","No") == "No")
return
if (!get_stickyban_from_ckey(ckey))
usr << "<span class='adminnotice'>Error: The ban disappeared.</span>"
return
world.SetConfig("ban",ckey, null)
log_admin("[key_name(usr)] removed [ckey]'s stickyban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed [ckey]'s stickyban</span>")
if ("remove_alt")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (!data["alt"])
return
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>"
return
var/found = 0
//we have to do it this way because byond keeps the case in its sticky ban matches WHY!!!
for (var/key in ban["keys"])
if (ckey(key) == alt)
found = 1
break
if (!found)
usr << "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>"
return
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: The ban disappeared.</span>"
return
found = 0
for (var/key in ban["keys"])
if (ckey(key) == alt)
ban["keys"] -= key
found = 1
break
if (!found)
usr << "<span class='adminnotice'>Error: [alt] link to [ckey]'s sticky ban disappeared.</span>"
return
world.SetConfig("ban",ckey,list2stickyban(ban))
log_admin("[key_name(usr)] has disassociated [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has disassociated [alt] from [ckey]'s sticky ban</span>")
if ("edit")
if (!data["ckey"])
return
var/ckey = data["ckey"]
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: No sticky ban for [ckey] found!"
return
var/oldreason = ban["message"]
var/reason = input(usr,"Reason","Reason","[ban["message"]]") as text|null
if (!reason || reason == oldreason)
return
//we have to do this again incase something changed while we waited for input
ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: The ban disappeared.</span>"
return
ban["message"] = "[reason]"
world.SetConfig("ban",ckey,list2stickyban(ban))
log_admin("[key_name(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]</span>")
/datum/admins/proc/stickyban_gethtml(ckey, ban)
. = "<a href='?_src_=holder;stickyban=remove&ckey=[ckey]'>\[-\]</a><b>[ckey]</b><br />"
. += "[ban["message"]] <b><a href='?_src_=holder;stickyban=edit&ckey=[ckey]'>\[Edit\]</a></b><br />"
if (ban["admin"])
. += "[ban["admin"]]<br />"
else
. += "LEGACY<br />"
. += "Caught keys<br />\n<ol>"
for (var/key in ban["keys"])
if (ckey(key) == ckey)
continue
. += "<li><a href='?_src_=holder;stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]</li>"
. += "</ol>\n"
/datum/admins/proc/stickyban_show()
if(!check_rights(R_BAN))
return
var/list/bans = sortList(world.GetConfig("ban"))
var/banhtml = ""
for(var/key in bans)
var/ckey = ckey(key)
var/ban = stickyban2list(world.GetConfig("ban",key))
banhtml += "<br /><hr />\n"
banhtml += stickyban_gethtml(ckey,ban)
var/html = {"
<head>
<title>Sticky Bans</title>
</head>
<body>
<h2>All Sticky Bans:</h2> <a href='?_src_=holder;stickyban=add'>\[+\]</a><br>
[banhtml]
</body>
"}
usr << browse(html,"window=stickybans;size=700x400")
/proc/get_stickyban_from_ckey(var/ckey)
if (!ckey)
return null
ckey = ckey(ckey)
. = null
for (var/key in world.GetConfig("ban"))
if (ckey(key) == ckey)
. = stickyban2list(world.GetConfig("ban",key))
break
/proc/stickyban2list(var/ban)
if (!ban)
return null
. = params2list(ban)
.["keys"] = splittext(.["keys"], ",")
.["type"] = splittext(.["type"], ",")
.["IP"] = splittext(.["IP"], ",")
.["computer_id"] = splittext(.["computer_id"], ",")
/proc/list2stickyban(var/list/ban)
if (!ban || !islist(ban))
return null
. = ban.Copy()
if (.["keys"])
.["keys"] = jointext(.["keys"], ",")
if (.["type"])
.["type"] = jointext(.["type"], ",")
if (.["IP"])
.["IP"] = jointext(.["IP"], ",")
if (.["computer_id"])
.["computer_id"] = jointext(.["computer_id"], ",")
. = list2params(.)
/client/proc/stickybanpanel()
set name = "Sticky Ban Panel"
set category = "Admin"
if (!holder)
return
holder.stickyban_show()
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
/proc/getbrokeninhands()
var/text
for(var/A in typesof(/obj/item))
var/obj/item/O = new A( locate(1,1,1) )
if(!O) continue
var/icon/IL = new(O.lefthand_file)
var/list/Lstates = IL.IconStates()
var/icon/IR = new(O.righthand_file)
var/list/Rstates = IR.IconStates()
var/icon/J = new(O.icon)
var/list/istates = J.IconStates()
if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state))
if(O.icon_state)
text += "[O.type] WANTS IN LEFT HAND CALLED\n\"[O.icon_state]\".\n"
if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state))
if(O.icon_state)
text += "[O.type] WANTS IN RIGHT HAND CALLED\n\"[O.icon_state]\".\n"
if(O.icon_state)
if(!istates.Find(O.icon_state))
text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.icon_state]\" IN \"[O.icon]\"\n"
if(O.item_state)
if(!istates.Find(O.item_state))
text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n"
text+="\n"
qdel(O)
if(text)
var/F = file("broken_icons.txt")
fdel(F)
F << text
world << "Completely successfully and written to [F]"
+501
View File
@@ -0,0 +1,501 @@
// Code taken from /bay/station.
// Modified to allow consequtive querys in one invocation, terminated with ";"
// Examples
/*
-- Will call the proc for all computers in the world, thats dir is 2.
CALL ex_act(1) ON /obj/machinery/computer IN world WHERE dir == 2
-- Will open a window with a list of all the closets in the world, with a link to VV them.
SELECT /obj/structure/closet/secure_closet/security/cargo IN world WHERE icon_off == "secoff"
-- Will change all the tube lights to green
UPDATE /obj/machinery/light IN world SET color = "#0F0" WHERE icon_state == "tube1"
-- Will delete all pickaxes. "IN world" is not required.
DELETE /obj/item/weapon/pickaxe
-- Will flicker the lights once, then turn all mobs green. The semicolon is important to separate the consecutive querys, but is not required for standard one-query use
CALL flicker(1) ON /obj/machinery/light; UPDATE /mob SET color = "#00cc00"
--You can use operators other than ==, such as >, <=, != and etc..
*/
/client/proc/SDQL2_query(query_text as message)
set category = "Debug"
if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe.
message_admins("<span class='danger'>ERROR: Non-admin [key_name(usr, usr.client)] attempted to execute a SDQL query!</span>")
log_admin("Non-admin [usr.ckey]([usr]) attempted to execute a SDQL query!")
if(!query_text || length(query_text) < 1)
return
//world << query_text
var/list/query_list = SDQL2_tokenize(query_text)
if(!query_list || query_list.len < 1)
return
var/list/querys = SDQL_parse(query_list)
if(!querys || querys.len < 1)
return
var/query_log = "executed SDQL query: \"[query_text]\"."
message_admins("[key_name_admin(usr)] [query_log]")
query_log = "[usr.ckey]([usr]) [query_log]"
log_game(query_log)
NOTICE(query_log)
for(var/list/query_tree in querys)
var/list/from_objs = list()
var/list/select_types = list()
switch(query_tree[1])
if("explain")
SDQL_testout(query_tree["explain"])
return
if("call")
if("on" in query_tree)
select_types = query_tree["on"]
else
return
if("select", "delete", "update")
select_types = query_tree[query_tree[1]]
from_objs = SDQL_from_objs(query_tree["from"])
var/list/objs = list()
for(var/type in select_types)
var/char = copytext(type, 1, 2)
if(char == "/" || char == "*")
for(var/from in from_objs)
objs += SDQL_get_all(type, from)
CHECK_TICK
else if(char == "'" || char == "\"")
objs += locate(copytext(type, 2, length(type)))
if("where" in query_tree)
var/objs_temp = objs
objs = list()
for(var/datum/d in objs_temp)
if(SDQL_expression(d, query_tree["where"]))
objs += d
CHECK_TICK
switch(query_tree[1])
if("call")
var/list/call_list = query_tree["call"]
var/list/args_list = query_tree["args"]
for(var/datum/d in objs)
for(var/v in call_list)
SDQL_callproc(d, v, args_list)
CHECK_TICK
if("delete")
for(var/datum/d in objs)
qdel(d)
CHECK_TICK
if("select")
var/text = ""
for(var/datum/t in objs)
text += "<A HREF='?_src_=vars;Vars=\ref[t]'>\ref[t]</A>"
if(istype(t, /atom))
var/atom/a = t
if(a.x)
text += ": [t] at ([a.x], [a.y], [a.z])<br>"
else if(a.loc && a.loc.x)
text += ": [t] in [a.loc] at ([a.loc.x], [a.loc.y], [a.loc.z])<br>"
else
text += ": [t]<br>"
else
text += ": [t]<br>"
usr << browse(text, "window=SDQL-result")
if("update")
if("set" in query_tree)
var/list/set_list = query_tree["set"]
for(var/datum/d in objs)
var/list/vals = list()
for(var/v in set_list)
if(v in d.vars)
vals += v
vals[v] = SDQL_expression(d, set_list[v])
if(istype(d, /turf))
for(var/v in vals)
if(v == "x" || v == "y" || v == "z")
continue
d.vars[v] = vals[v]
else
for(var/v in vals)
d.vars[v] = vals[v]
CHECK_TICK
/proc/SDQL_callproc(thing, procname, args_list)
set waitfor = 0
if(hascall(thing, procname))
call(thing, procname)(arglist(args_list))
/proc/SDQL_parse(list/query_list)
var/datum/SDQL_parser/parser = new()
var/list/querys = list()
var/list/query_tree = list()
var/pos = 1
var/querys_pos = 1
var/do_parse = 0
for(var/val in query_list)
if(val == ";")
do_parse = 1
else if(pos >= query_list.len)
query_tree += val
do_parse = 1
if(do_parse)
parser.query = query_tree
var/list/parsed_tree
parsed_tree = parser.parse()
if(parsed_tree.len > 0)
querys.len = querys_pos
querys[querys_pos] = parsed_tree
querys_pos++
else //There was an error so don't run anything, and tell the user which query has errored.
usr << "<span class='danger'>Parsing error on [querys_pos]\th query. Nothing was executed.</span>"
return list()
query_tree = list()
do_parse = 0
else
query_tree += val
pos++
qdel(parser)
return querys
/proc/SDQL_testout(list/query_tree, indent = 0)
var/spaces = ""
for(var/s = 0, s < indent, s++)
spaces += " "
for(var/item in query_tree)
if(istype(item, /list))
usr << "[spaces]("
SDQL_testout(item, indent + 1)
usr << "[spaces])"
else
usr << "[spaces][item]"
if(!isnum(item) && query_tree[item])
if(istype(query_tree[item], /list))
usr << "[spaces] ("
SDQL_testout(query_tree[item], indent + 2)
usr << "[spaces] )"
else
usr << "[spaces] [query_tree[item]]"
/proc/SDQL_from_objs(list/tree)
if("world" in tree)
return list(world)
var/list/out = list()
for(var/type in tree)
var/char = copytext(type, 1, 2)
if(char == "/")
out += SDQL_get_all(type, world)
else if(char == "'" || char == "\"")
out += locate(copytext(type, 2, length(type)))
return out
/proc/SDQL_get_all(type, location)
var/list/out = list()
if(type == "*")
for(var/datum/d in location)
out += d
return out
type = text2path(type)
if(ispath(type, /mob))
for(var/mob/d in location)
if(istype(d, type))
out += d
CHECK_TICK
else if(ispath(type, /turf))
for(var/turf/d in location)
if(istype(d, type))
out += d
CHECK_TICK
else if(ispath(type, /obj))
for(var/obj/d in location)
if(istype(d, type))
out += d
CHECK_TICK
else if(ispath(type, /area))
for(var/area/d in location)
if(istype(d, type))
out += d
CHECK_TICK
else if(ispath(type, /atom))
for(var/atom/d in location)
if(istype(d, type))
out += d
CHECK_TICK
else
for(var/datum/d in location)
if(istype(d, type))
out += d
CHECK_TICK
return out
/proc/SDQL_expression(datum/object, list/expression, start = 1)
var/result = 0
var/val
for(var/i = start, i <= expression.len, i++)
var/op = ""
if(i > start)
op = expression[i]
i++
var/list/ret = SDQL_value(object, expression, i)
val = ret["val"]
i = ret["i"]
if(op != "")
switch(op)
if("+")
result += val
if("-")
result -= val
if("*")
result *= val
if("/")
result /= val
if("&")
result &= val
if("|")
result |= val
if("^")
result ^= val
if("=", "==")
result = (result == val)
if("!=", "<>")
result = (result != val)
if("<")
result = (result < val)
if("<=")
result = (result <= val)
if(">")
result = (result > val)
if(">=")
result = (result >= val)
if("and", "&&")
result = (result && val)
if("or", "||")
result = (result || val)
else
usr << "<span class='danger'>SDQL2: Unknown op [op]</span>"
result = null
else
result = val
return result
/proc/SDQL_value(datum/object, list/expression, start = 1)
var/i = start
var/val = null
if(i > expression.len)
return list("val" = null, "i" = i)
if(istype(expression[i], /list))
val = SDQL_expression(object, expression[i])
else if(expression[i] == "!")
var/list/ret = SDQL_value(object, expression, i + 1)
val = !ret["val"]
i = ret["i"]
else if(expression[i] == "~")
var/list/ret = SDQL_value(object, expression, i + 1)
val = ~ret["val"]
i = ret["i"]
else if(expression[i] == "-")
var/list/ret = SDQL_value(object, expression, i + 1)
val = -ret["val"]
i = ret["i"]
else if(expression[i] == "null")
val = null
else if(isnum(expression[i]))
val = expression[i]
else if(copytext(expression[i], 1, 2) in list("'", "\""))
val = copytext(expression[i], 2, length(expression[i]))
else
val = SDQL_var(object, expression, i)
i = expression.len
return list("val" = val, "i" = i)
/proc/SDQL_var(datum/object, list/expression, start = 1)
if(expression[start] in object.vars)
if(start < expression.len && expression[start + 1] == ".")
return SDQL_var(object.vars[expression[start]], expression[start + 2])
else
return object.vars[expression[start]]
else
return null
/proc/SDQL2_tokenize(query_text)
var/list/whitespace = list(" ", "\n", "\t")
var/list/single = list("(", ")", ",", "+", "-", ".", ";")
var/list/multi = list(
"=" = list("", "="),
"<" = list("", "=", ">"),
">" = list("", "="),
"!" = list("", "="))
var/word = ""
var/list/query_list = list()
var/len = length(query_text)
for(var/i = 1, i <= len, i++)
var/char = copytext(query_text, i, i + 1)
if(char in whitespace)
if(word != "")
query_list += word
word = ""
else if(char in single)
if(word != "")
query_list += word
word = ""
query_list += char
else if(char in multi)
if(word != "")
query_list += word
word = ""
var/char2 = copytext(query_text, i + 1, i + 2)
if(char2 in multi[char])
query_list += "[char][char2]"
i++
else
query_list += char
else if(char == "'")
if(word != "")
usr << "\red SDQL2: You have an error in your SDQL syntax, unexpected ' in query: \"<font color=gray>[query_text]</font>\" following \"<font color=gray>[word]</font>\". Please check your syntax, and try again."
return null
word = "'"
for(i++, i <= len, i++)
char = copytext(query_text, i, i + 1)
if(char == "'")
if(copytext(query_text, i + 1, i + 2) == "'")
word += "'"
i++
else
break
else
word += char
if(i > len)
usr << "\red SDQL2: You have an error in your SDQL syntax, unmatched ' in query: \"<font color=gray>[query_text]</font>\". Please check your syntax, and try again."
return null
query_list += "[word]'"
word = ""
else if(char == "\"")
if(word != "")
usr << "\red SDQL2: You have an error in your SDQL syntax, unexpected \" in query: \"<font color=gray>[query_text]</font>\" following \"<font color=gray>[word]</font>\". Please check your syntax, and try again."
return null
word = "\""
for(i++, i <= len, i++)
char = copytext(query_text, i, i + 1)
if(char == "\"")
if(copytext(query_text, i + 1, i + 2) == "'")
word += "\""
i++
else
break
else
word += char
if(i > len)
usr << "\red SDQL2: You have an error in your SDQL syntax, unmatched \" in query: \"<font color=gray>[query_text]</font>\". Please check your syntax, and try again."
return null
query_list += "[word]\""
word = ""
else
word += char
if(word != "")
query_list += word
return query_list
@@ -0,0 +1,543 @@
//I'm pretty sure that this is a recursive [s]descent[/s] ascent parser.
//Spec
//////////
//
// query : select_query | delete_query | update_query | call_query | explain
// explain : 'EXPLAIN' query
//
// select_query : 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
// delete_query : 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
// update_query : 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression]
// call_query : 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]]
//
// select_list : select_item [',' select_list]
// select_item : '*' | select_function | object_type
// select_function : count_function
// count_function : 'COUNT' '(' '*' ')' | 'COUNT' '(' object_types ')'
//
// from_list : from_item [',' from_list]
// from_item : 'world' | object_type
//
// call_function : <function name> ['(' [arguments] ')']
// arguments : expression [',' arguments]
//
// object_type : <type path> | string
//
// assignments : assignment, [',' assignments]
// assignment : <variable name> '=' expression
// variable : <variable name> | <variable name> '.' variable
//
// bool_expression : expression comparitor expression [bool_operator bool_expression]
// expression : ( unary_expression | '(' expression ')' | value ) [binary_operator expression]
// unary_expression : unary_operator ( unary_expression | value | '(' expression ')' )
// comparitor : '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
// value : variable | string | number | 'null'
// unary_operator : '!' | '-' | '~'
// binary_operator : comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^'
// bool_operator : 'AND' | '&&' | 'OR' | '||'
//
// string : ''' <some text> ''' | '"' <some text > '"'
// number : <some digits>
//
//////////
/datum/SDQL_parser
var/query_type
var/error = 0
var/list/query
var/list/tree
var/list/select_functions = list("count")
var/list/boolean_operators = list("and", "or", "&&", "||")
var/list/unary_operators = list("!", "-", "~")
var/list/binary_operators = list("+", "-", "/", "*", "&", "|", "^")
var/list/comparitors = list("=", "==", "!=", "<>", "<", "<=", ">", ">=")
/datum/SDQL_parser/New(query_list)
query = query_list
/datum/SDQL_parser/proc/parse_error(error_message)
error = 1
usr << "<span class='danger'>SQDL2 Parsing Error: [error_message]</span>"
return query.len + 1
/datum/SDQL_parser/proc/parse()
tree = list()
query(1, tree)
if(error)
return list()
else
return tree
/datum/SDQL_parser/proc/token(i)
if(i <= query.len)
return query[i]
else
return null
/datum/SDQL_parser/proc/tokens(i, num)
if(i + num <= query.len)
return query.Copy(i, i + num)
else
return null
/datum/SDQL_parser/proc/tokenl(i)
return lowertext(token(i))
/datum/SDQL_parser/proc
//query: select_query | delete_query | update_query
query(i, list/node)
query_type = tokenl(i)
switch(query_type)
if("select")
select_query(i, node)
if("delete")
delete_query(i, node)
if("update")
update_query(i, node)
if("call")
call_query(i, node)
if("explain")
node += "explain"
node["explain"] = list()
query(i + 1, node["explain"])
// select_query: 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
select_query(i, list/node)
var/list/select = list()
i = select_list(i + 1, select)
node += "select"
node["select"] = select
var/list/from = list()
if(tokenl(i) in list("from", "in"))
i = from_list(i + 1, from)
else
from += "world"
node += "from"
node["from"] = from
if(tokenl(i) == "where")
var/list/where = list()
i = bool_expression(i + 1, where)
node += "where"
node["where"] = where
return i
//delete_query: 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
delete_query(i, list/node)
var/list/select = list()
i = select_list(i + 1, select)
node += "delete"
node["delete"] = select
var/list/from = list()
if(tokenl(i) in list("from", "in"))
i = from_list(i + 1, from)
else
from += "world"
node += "from"
node["from"] = from
if(tokenl(i) == "where")
var/list/where = list()
i = bool_expression(i + 1, where)
node += "where"
node["where"] = where
return i
//update_query: 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression]
update_query(i, list/node)
var/list/select = list()
i = select_list(i + 1, select)
node += "update"
node["update"] = select
var/list/from = list()
if(tokenl(i) in list("from", "in"))
i = from_list(i + 1, from)
else
from += "world"
node += "from"
node["from"] = from
if(tokenl(i) != "set")
i = parse_error("UPDATE has misplaced SET")
var/list/set_assignments = list()
i = assignments(i + 1, set_assignments)
node += "set"
node["set"] = set_assignments
if(tokenl(i) == "where")
var/list/where = list()
i = bool_expression(i + 1, where)
node += "where"
node["where"] = where
return i
//call_query: 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]]
call_query(i, list/node)
var/list/func = list()
var/list/arguments = list()
i = call_function(i + 1, func, arguments)
node += "call"
node["call"] = func
node["args"] = arguments
if(tokenl(i) != "on")
return i
var/list/select = list()
i = select_list(i + 1, select)
node += "on"
node["on"] = select
var/list/from = list()
if(tokenl(i) in list("from", "in"))
i = from_list(i + 1, from)
else
from += "world"
node += "from"
node["from"] = from
if(tokenl(i) == "where")
var/list/where = list()
i = bool_expression(i + 1, where)
node += "where"
node["where"] = where
return i
//select_list: select_item [',' select_list]
select_list(i, list/node)
i = select_item(i, node)
if(token(i) == ",")
i = select_list(i + 1, node)
return i
//from_list: from_item [',' from_list]
from_list(i, list/node)
i = from_item(i, node)
if(token(i) == ",")
i = from_list(i + 1, node)
return i
//assignments: assignment, [',' assignments]
assignments(i, list/node)
i = assignment(i, node)
if(token(i) == ",")
i = assignments(i + 1, node)
return i
//select_item: '*' | select_function | object_type
select_item(i, list/node)
if(token(i) == "*")
node += "*"
i++
else if(tokenl(i) in select_functions)
i = select_function(i, node)
else
i = object_type(i, node)
return i
//from_item: 'world' | object_type
from_item(i, list/node)
if(token(i) == "world")
node += "world"
i++
else
i = object_type(i, node)
return i
//bool_expression: expression [bool_operator bool_expression]
bool_expression(i, list/node)
var/list/bool = list()
i = expression(i, bool)
node[++node.len] = bool
if(tokenl(i) in boolean_operators)
i = bool_operator(i, node)
i = bool_expression(i, node)
return i
//assignment: <variable name> '=' expression
assignment(i, list/node)
node += token(i)
if(token(i + 1) == "=")
var/varname = token(i)
node[varname] = list()
i = expression(i + 2, node[varname])
else
parse_error("Assignment expected, but no = found")
return i
//variable: <variable name> | <variable name> '.' variable
variable(i, list/node)
var/list/L = list(token(i))
node[++node.len] = L
if(token(i + 1) == ".")
L += "."
i = variable(i + 2, L)
else
i++
return i
//object_type: <type path> | string
object_type(i, list/node)
if(copytext(token(i), 1, 2) == "/")
node += token(i)
else
i = string(i, node)
return i + 1
//comparitor: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
comparitor(i, list/node)
if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">="))
node += token(i)
else
parse_error("Unknown comparitor [token(i)]")
return i + 1
//bool_operator: 'AND' | '&&' | 'OR' | '||'
bool_operator(i, list/node)
if(tokenl(i) in list("and", "or", "&&", "||"))
node += token(i)
else
parse_error("Unknown comparitor [token(i)]")
return i + 1
//string: ''' <some text> ''' | '"' <some text > '"'
string(i, list/node)
if(copytext(token(i), 1, 2) in list("'", "\""))
node += copytext(token(i),2,-1)
else
parse_error("Expected string but found '[token(i)]'")
return i + 1
//call_function: <function name> ['(' [arguments] ')']
call_function(i, list/node, list/arguments)
if(length(tokenl(i)))
node += token(i++)
if(token(i) != "(")
parse_error("Expected ( but found '[token(i)]'")
else if(token(i + 1) != ")")
do
i = expression(i + 1, arguments)
if(token(i) == ",")
continue
while(token(i) && token(i) != ")")
else
i++
else
parse_error("Expected a function but found nothing")
return i + 1
//select_function: count_function
select_function(i, list/node)
parse_error("Sorry, function calls aren't available yet")
return i
//expression: ( unary_expression | '(' expression ')' | value ) [binary_operator expression]
expression(i, list/node)
if(token(i) in unary_operators)
i = unary_expression(i, node)
else if(token(i) == "(")
var/list/expr = list()
i = expression(i + 1, expr)
if(token(i) != ")")
parse_error("Missing ) at end of expression.")
else
i++
node[++node.len] = expr
else
i = value(i, node)
if(token(i) in binary_operators)
i = binary_operator(i, node)
i = expression(i, node)
else if(token(i) in comparitors)
i = binary_operator(i, node)
var/list/rhs = list()
i = expression(i, rhs)
node[++node.len] = rhs
return i
//unary_expression: unary_operator ( unary_expression | value | '(' expression ')' )
unary_expression(i, list/node)
if(token(i) in unary_operators)
var/list/unary_exp = list()
unary_exp += token(i)
i++
if(token(i) in unary_operators)
i = unary_expression(i, unary_exp)
else if(token(i) == "(")
var/list/expr = list()
i = expression(i + 1, expr)
if(token(i) != ")")
parse_error("Missing ) at end of expression.")
else
i++
unary_exp[++unary_exp.len] = expr
else
i = value(i, unary_exp)
node[++node.len] = unary_exp
else
parse_error("Expected unary operator but found '[token(i)]'")
return i
//binary_operator: comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^'
binary_operator(i, list/node)
if(token(i) in (binary_operators + comparitors))
node += token(i)
else
parse_error("Unknown binary operator [token(i)]")
return i + 1
//value: variable | string | number | 'null'
value(i, list/node)
if(token(i) == "null")
node += "null"
i++
else if(isnum(text2num(token(i))))
node += text2num(token(i))
i++
else if(copytext(token(i), 1, 2) in list("'", "\""))
i = string(i, node)
else
i = variable(i, node)
return i
/*EXPLAIN SELECT * WHERE 42 = 6 * 9 OR val = - 5 == 7*/
+163
View File
@@ -0,0 +1,163 @@
/proc/keywords_lookup(msg)
//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE!
var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as", "i")
//explode the input msg into a list
var/list/msglist = splittext(msg, " ")
//generate keywords lookup
var/list/surnames = list()
var/list/forenames = list()
var/list/ckeys = list()
for(var/mob/M in mob_list)
var/list/indexing = list(M.real_name, M.name)
if(M.mind)
indexing += M.mind.name
for(var/string in indexing)
var/list/L = splittext(string, " ")
var/surname_found = 0
//surnames
for(var/i=L.len, i>=1, i--)
var/word = ckey(L[i])
if(word)
surnames[word] = M
surname_found = i
break
//forenames
for(var/i=1, i<surname_found, i++)
var/word = ckey(L[i])
if(word)
forenames[word] = M
//ckeys
ckeys[M.ckey] = M
var/ai_found = 0
msg = ""
var/list/mobs_found = list()
for(var/original_word in msglist)
var/word = ckey(original_word)
if(word)
if(!(word in adminhelp_ignored_words))
if(word == "ai")
ai_found = 1
else
var/mob/found = ckeys[word]
if(!found)
found = surnames[word]
if(!found)
found = forenames[word]
if(found)
if(!(found in mobs_found))
mobs_found += found
if(!ai_found && isAI(found))
ai_found = 1
var/is_antag = 0
if(found.mind && found.mind.special_role)
is_antag = 1
msg += "[original_word]<font size='1' color='[is_antag ? "red" : "black"]'>(<A HREF='?_src_=holder;adminmoreinfo=\ref[found]'>?</A>|<A HREF='?_src_=holder;adminplayerobservefollow=\ref[found]'>F</A>)</font> "
continue
msg += "[original_word] "
return msg
/client/var/adminhelptimerid = 0
/client/proc/giveadminhelpverb()
src.verbs |= /client/verb/adminhelp
adminhelptimerid = 0
/client/verb/adminhelp(msg as text)
set category = "Admin"
set name = "Adminhelp"
if(say_disabled) //This is here to try to identify lag problems
usr << "<span class='danger'>Speech is currently admin-disabled.</span>"
return
//handle muting and automuting
if(prefs.muted & MUTE_ADMINHELP)
src << "<span class='danger'>Error: Admin-PM: You cannot send adminhelps (Muted).</span>"
return
if(src.handle_spam_prevention(msg,MUTE_ADMINHELP))
return
//clean the input msg
if(!msg)
return
msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
if(!msg) return
var/original_msg = msg
//remove our adminhelp verb temporarily to prevent spamming of admins.
src.verbs -= /client/verb/adminhelp
adminhelptimerid = addtimer(src, "giveadminhelpverb", 1200, FALSE) //2 minute cooldown of admin helps
msg = keywords_lookup(msg)
if(!mob)
return //this doesn't happen
var/ref_mob = "\ref[mob]"
var/ref_client = "\ref[src]"
msg = "<span class='adminnotice'><b><font color=red>HELP: </font><A HREF='?priv_msg=[ckey];ahelp_reply=1'>[key_name(src)]</A> (<A HREF='?_src_=holder;adminmoreinfo=[ref_mob]'>?</A>) (<A HREF='?_src_=holder;adminplayeropts=[ref_mob]'>PP</A>) (<A HREF='?_src_=vars;Vars=[ref_mob]'>VV</A>) (<A HREF='?_src_=holder;subtlemessage=[ref_mob]'>SM</A>) (<A HREF='?_src_=holder;adminplayerobservefollow=[ref_mob]'>FLW</A>) (<A HREF='?_src_=holder;traitor=[ref_mob]'>TP</A>) (<A HREF='?_src_=holder;rejectadminhelp=[ref_client]'>REJT</A>):</b> [msg]</span>"
//send this msg to all admins
for(var/client/X in admins)
if(X.prefs.toggles & SOUND_ADMINHELP)
X << 'sound/effects/adminhelp.ogg'
X << msg
//show it to the person adminhelping too
src << "<span class='adminnotice'>PM to-<b>Admins</b>: [original_msg]</span>"
//send it to irc if nobody is on and tell us how many were on
var/admin_number_present = send2irc_adminless_only(ckey,original_msg)
log_admin("HELP: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins who have +BAN.")
feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/proc/get_admin_counts(requiredflags = R_BAN)
. = list("total" = 0, "noflags" = 0, "afk" = 0, "stealth" = 0, "present" = 0)
for(var/client/X in admins)
.["total"]++
if(requiredflags != 0 && !check_rights_for(X, requiredflags))
.["noflags"]++
else if(X.is_afk())
.["afk"]++
else if(X.holder.fakekey)
.["stealth"]++
else
.["present"]++
/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN)
var/list/adm = get_admin_counts(requiredflags)
. = adm["present"]
if(. <= 0)
var/final = ""
if(!adm["afk"] && !adm["stealth"] && !adm["noflags"])
final = "[msg] - No admins online"
else
final = "[msg] - All admins AFK ([adm["afk"]]/[adm["total"]]), stealthminned ([adm["stealth"]]/[adm["total"]]), or lack[rights2text(requiredflags, " ")] ([adm["noflags"]]/[adm["total"]])"
send2irc(source,final)
send2otherserver(source,final)
/proc/send2irc(msg,msg2)
if(config.useircbot)
shell("python nudge.py [msg] [msg2]")
return
/proc/send2otherserver(source,msg,type = "Ahelp")
if(global.cross_allowed)
var/list/message = list()
message["message_sender"] = source
message["message"] = msg
message["source"] = "([config.cross_name])"
message["key"] = global.comms_key
message["crossmessage"] = type
world.Export("[global.cross_address]?[list2params(message)]")
+152
View File
@@ -0,0 +1,152 @@
/client/proc/jumptoarea(area/A in sortedAreas)
set name = "Jump to Area"
set desc = "Area to jump to"
set category = "Admin"
if(!src.holder)
src << "Only administrators may use this command."
return
if(!A)
return
var/list/turfs = list()
for(var/area/Ar in A.related)
for(var/turf/T in Ar)
if(T.density)
continue
turfs.Add(T)
var/turf/T = safepick(turfs)
if(!T)
src << "Nowhere to jump to!"
return
usr.forceMove(T)
log_admin("[key_name(usr)] jumped to [A]")
message_admins("[key_name_admin(usr)] jumped to [A]")
feedback_add_details("admin_verb","JA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/jumptoturf(turf/T in world)
set name = "Jump to Turf"
set category = "Admin"
if(!src.holder)
src << "Only administrators may use this command."
return
log_admin("[key_name(usr)] jumped to [T.x],[T.y],[T.z] in [T.loc]")
message_admins("[key_name_admin(usr)] jumped to [T.x],[T.y],[T.z] in [T.loc]")
usr.loc = T
feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/jumptomob(mob/M in mob_list)
set category = "Admin"
set name = "Jump to Mob"
if(!src.holder)
src << "Only administrators may use this command."
return
log_admin("[key_name(usr)] jumped to [key_name(M)]")
message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]")
if(src.mob)
var/mob/A = src.mob
var/turf/T = get_turf(M)
if(T && isturf(T))
feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
A.forceMove(M.loc)
else
A << "This mob is not located in the game world."
/client/proc/jumptocoord(tx as num, ty as num, tz as num)
set category = "Admin"
set name = "Jump to Coordinate"
if (!holder)
src << "Only administrators may use this command."
return
if(src.mob)
var/mob/A = src.mob
A.x = tx
A.y = ty
A.z = tz
feedback_add_details("admin_verb","JC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]")
/client/proc/jumptokey()
set category = "Admin"
set name = "Jump to Key"
if(!src.holder)
src << "Only administrators may use this command."
return
var/list/keys = list()
for(var/mob/M in player_list)
keys += M.client
var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys)
if(!selection)
src << "No keys found."
return
var/mob/M = selection:mob
log_admin("[key_name(usr)] jumped to [key_name(M)]")
message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]")
usr.forceMove(M.loc)
feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/Getmob(mob/M in mob_list)
set category = "Admin"
set name = "Get Mob"
set desc = "Mob to teleport"
if(!src.holder)
src << "Only administrators may use this command."
return
log_admin("[key_name(usr)] teleported [key_name(M)]")
message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]")
M.forceMove(get_turf(usr))
feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/Getkey()
set category = "Admin"
set name = "Get Key"
set desc = "Key to teleport"
if(!src.holder)
src << "Only administrators may use this command."
return
var/list/keys = list()
for(var/mob/M in player_list)
keys += M.client
var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys)
if(!selection)
return
var/mob/M = selection:mob
if(!M)
return
log_admin("[key_name(usr)] teleported [key_name(M)]")
message_admins("[key_name_admin(usr)] teleported [key_name(M)]")
if(M)
M.forceMove(get_turf(usr))
usr.loc = M.loc
feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/sendmob(mob/M in sortmobs())
set category = "Admin"
set name = "Send Mob"
if(!src.holder)
src << "Only administrators may use this command."
return
var/area/A = input(usr, "Pick an area.", "Pick an area") in sortedAreas|null
if(A && istype(A))
if(M.forceMove(safepick(get_area_turfs(A))))
log_admin("[key_name(usr)] teleported [key_name(M)] to [A]")
message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)] to [A]")
else
src << "Failed to move mob to a valid location."
feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+152
View File
@@ -0,0 +1,152 @@
//allows right clicking mobs to send an admin PM to their client, forwards the selected mob's client to cmd_admin_pm
/client/proc/cmd_admin_pm_context(mob/M in mob_list)
set category = null
set name = "Admin PM Mob"
if(!holder)
src << "<font color='red'>Error: Admin-PM-Context: Only administrators may use this command.</font>"
return
if( !ismob(M) || !M.client )
return
cmd_admin_pm(M.client,null)
feedback_add_details("admin_verb","APMM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//shows a list of clients we could send PMs to, then forwards our choice to cmd_admin_pm
/client/proc/cmd_admin_pm_panel()
set category = "Admin"
set name = "Admin PM"
if(!holder)
src << "<font color='red'>Error: Admin-PM-Panel: Only administrators may use this command.</font>"
return
var/list/client/targets[0]
for(var/client/T)
if(T.mob)
if(istype(T.mob, /mob/new_player))
targets["(New Player) - [T]"] = T
else if(istype(T.mob, /mob/dead/observer))
targets["[T.mob.name](Ghost) - [T]"] = T
else
targets["[T.mob.real_name](as [T.mob.name]) - [T]"] = T
else
targets["(No Mob) - [T]"] = T
var/list/sorted = sortList(targets)
var/target = input(src,"To whom shall we send a message?","Admin PM",null) in sorted|null
cmd_admin_pm(targets[target],null)
feedback_add_details("admin_verb","APM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_ahelp_reply(whom)
if(prefs.muted & MUTE_ADMINHELP)
src << "<font color='red'>Error: Admin-PM: You are unable to use admin PM-s (muted).</font>"
return
var/client/C
if(istext(whom))
if(cmptext(copytext(whom,1,2),"@"))
whom = findStealthKey(whom)
C = directory[whom]
else if(istype(whom,/client))
C = whom
if(!C)
if(holder)
src << "<font color='red'>Error: Admin-PM: Client not found.</font>"
return
message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.")
var/msg = input(src,"Message:", "Private message to [key_name(C, 0, 0)]") as text|null
if (!msg)
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.")
return
cmd_admin_pm(whom, msg)
//takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM.
//Fetching a message if needed. src is the sender and C is the target client
/client/proc/cmd_admin_pm(whom, msg)
if(prefs.muted & MUTE_ADMINHELP)
src << "<font color='red'>Error: Admin-PM: You are unable to use admin PM-s (muted).</font>"
return
var/client/C
if(istext(whom))
if(cmptext(copytext(whom,1,2),"@"))
whom = findStealthKey(whom)
C = directory[whom]
else if(istype(whom,/client))
C = whom
if(!C)
if(holder)
src << "<font color='red'>Error: Admin-PM: Client not found.</font>"
else
adminhelp(msg) //admin we are replying to left. adminhelp instead
return
//get message text, limit it's length.and clean/escape html
if(!msg)
msg = input(src,"Message:", "Private message to [key_name(C, 0, 0)]") as text|null
if(!msg)
return
if(!C)
if(holder)
src << "<font color='red'>Error: Admin-PM: Client not found.</font>"
else
adminhelp(msg) //admin we are replying to has vanished, adminhelp instead
return
if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
return
//clean the message if it's not sent by a high-rank admin
if(!check_rights(R_SERVER|R_DEBUG,0))
msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
if(!msg)
return
var/rawmsg = msg
if(holder)
msg = emoji_parse(msg)
var/keywordparsedmsg = keywords_lookup(msg)
if(C.holder)
if(holder) //both are admins
C << "<font color='red'>Admin PM from-<b>[key_name(src, C, 1)]</b>: [keywordparsedmsg]</font>"
src << "<font color='blue'>Admin PM to-<b>[key_name(C, src, 1)]</b>: [keywordparsedmsg]</font>"
else //recipient is an admin but sender is not
C << "<font color='red'>Reply PM from-<b>[key_name(src, C, 1)]</b>: [keywordparsedmsg]</font>"
src << "<font color='blue'>PM to-<b>Admins</b>: [msg]</font>"
//play the recieving admin the adminhelp sound (if they have them enabled)
if(C.prefs.toggles & SOUND_ADMINHELP)
C << 'sound/effects/adminhelp.ogg'
else
if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT
C << "<font color='red' size='4'><b>-- Administrator private message --</b></font>"
C << "<font color='red'>Admin PM from-<b>[key_name(src, C, 0)]</b>: [msg]</font>"
C << "<font color='red'><i>Click on the administrator's name to reply.</i></font>"
src << "<font color='blue'>Admin PM to-<b>[key_name(C, src, 1)]</b>: [msg]</font>"
//always play non-admin recipients the adminhelp sound
C << 'sound/effects/adminhelp.ogg'
//AdminPM popup for ApocStation and anybody else who wants to use it. Set it with POPUP_ADMIN_PM in config.txt ~Carn
if(config.popup_admin_pm)
spawn() //so we don't hold the caller proc up
var/sender = src
var/sendername = key
var/reply = input(C, msg,"Admin PM from-[sendername]", "") as text|null //show message and await a reply
if(C && reply)
if(sender)
C.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
else
adminhelp(reply) //sender has left, adminhelp instead
return
else //neither are admins
src << "<font color='red'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</font>"
return
log_admin("PM: [key_name(src)]->[key_name(C)]: [rawmsg]")
//we don't use message_admins here because the sender/receiver might get it too
for(var/client/X in admins)
if(X.key!=key && X.key!=C.key) //check client/X is an admin and isn't the sender or recipient
X << "<B><font color='blue'>PM: [key_name(src, X, 0)]-&gt;[key_name(C, X, 0)]:</B> \blue [keywordparsedmsg]</font>" //inform X
+22
View File
@@ -0,0 +1,22 @@
/client/proc/cmd_admin_say(msg as text)
set category = "Special Verbs"
set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite
set hidden = 1
if(!check_rights(0))
return
msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
if(!msg)
return
log_adminsay("[key_name(src)] : [msg]")
msg = keywords_lookup(msg)
if(check_rights(R_ADMIN,0))
msg = "<span class='admin'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]</EM> (<a href='?_src_=holder;adminplayerobservefollow=\ref[mob]'>FLW</A>): <span class='message'>[msg]</span></span>"
admins << msg
else
msg = "<span class='adminobserver'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]:</EM> <span class='message'>[msg]</span></span>"
admins << msg
feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+41
View File
@@ -0,0 +1,41 @@
/client/proc/atmosscan()
set category = "Mapping"
set name = "Check Plumbing"
if(!src.holder)
src << "Only administrators may use this command."
return
feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//all plumbing - yes, some things might get stated twice, doesn't matter.
for (var/obj/machinery/atmospherics/plumbing in machines)
if (plumbing.nodealert)
usr << "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])"
//Manifolds
for (var/obj/machinery/atmospherics/pipe/manifold/pipe in machines)
if (!pipe.NODE1 || !pipe.NODE2 || !pipe.NODE3)
usr << "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])"
//Pipes
for (var/obj/machinery/atmospherics/pipe/simple/pipe in machines)
if (!pipe.NODE1 || !pipe.NODE2)
usr << "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])"
/client/proc/powerdebug()
set category = "Mapping"
set name = "Check Power"
if(!src.holder)
src << "Only administrators may use this command."
return
feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
for (var/datum/powernet/PN in powernets)
if (!PN.nodes || !PN.nodes.len)
if(PN.cables && (PN.cables.len > 1))
var/obj/structure/cable/C = PN.cables[1]
usr << "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]"
if (!PN.cables || (PN.cables.len < 10))
if(PN.cables && (PN.cables.len > 1))
var/obj/structure/cable/C = PN.cables[1]
usr << "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]"
+37
View File
@@ -0,0 +1,37 @@
/client/proc/bluespace_artillery(mob/M in mob_list)
set name = "Bluespace Artillery"
set category = "Fun"
if(!holder || !check_rights(R_FUN))
return
var/mob/living/target = M
if(!isliving(target))
usr << "This can only be used on instances of type /mob/living"
return
if(alert(usr, "Are you sure you wish to hit [key_name(target)] with Blue Space Artillery?", "Confirm Firing?" , "Yes" , "No") != "Yes")
return
explosion(target.loc, 0, 0, 0, 0)
var/turf/open/floor/T = get_turf(target)
if(istype(T))
if(prob(80))
T.break_tile_to_plating()
else
T.break_tile()
target << "<span class='userdanger'>You're hit by bluespace artillery!</span>"
log_admin("[target.name] has been hit by Bluespace Artillery fired by [usr]")
message_admins("[target.name] has been hit by Bluespace Artillery fired by [usr]")
if(target.health <= 1)
target.gib(1, 1)
else
target.adjustBruteLoss(min(99,(target.health - 1)))
target.Stun(20)
target.Weaken(20)
target.stuttering = 20
+355
View File
@@ -0,0 +1,355 @@
#define BASIC_BUILDMODE 1
#define ADV_BUILDMODE 2
#define VAR_BUILDMODE 3
#define THROW_BUILDMODE 4
#define AREA_BUILDMODE 5
#define COPY_BUILDMODE 6
#define NUM_BUILDMODES 6
//Buildmode Shuttle
//Builmode Move
/obj/screen/buildmode
icon = 'icons/misc/buildmode.dmi'
var/datum/buildmode/bd
/obj/screen/buildmode/New(bd)
..()
src.bd = bd
/obj/screen/buildmode/mode
icon_state = "buildmode1"
name = "Toggle Mode"
screen_loc = "NORTH,WEST"
/obj/screen/buildmode/mode/Click(location, control, params)
var/list/pa = params2list(params)
if(pa.Find("left"))
bd.toggle_modes()
else if(pa.Find("right"))
bd.change_settings(usr)
update_icon()
return 1
/obj/screen/buildmode/mode/update_icon()
icon_state = "buildmode[bd.mode]"
return
/obj/screen/buildmode/help
icon_state = "buildhelp"
screen_loc = "NORTH,WEST+1"
name = "Buildmode Help"
/obj/screen/buildmode/help/Click()
bd.show_help(usr)
return 1
/obj/screen/buildmode/bdir
icon_state = "build"
screen_loc = "NORTH,WEST+2"
name = "Change Dir"
/obj/screen/buildmode/bdir/update_icon()
setDir(bd.build_dir)
return
/obj/screen/buildmode/quit
icon_state = "buildquit"
screen_loc = "NORTH,WEST+3"
name = "Quit Buildmode"
/obj/screen/buildmode/quit/Click()
bd.quit()
return 1
/obj/screen/buildmode/bdir/Click()
bd.change_dir()
update_icon()
return 1
/datum/buildmode
var/mode = BASIC_BUILDMODE
var/client/holder = null
var/list/obj/screen/buttons = list()
var/build_dir = SOUTH
var/atom/movable/throw_atom = null
var/turf/cornerA = null
var/turf/cornerB = null
var/generator_path = null
var/varholder = "name"
var/valueholder = "derp"
var/objholder = /obj/structure/closet
var/atom/movable/stored = null
/datum/buildmode/New(client/c)
create_buttons()
holder = c
holder.click_intercept = src
holder.show_popup_menus = 0
holder.screen += buttons
/datum/buildmode/proc/quit()
holder.screen -= buttons
holder.click_intercept = null
holder.show_popup_menus = 1
qdel(src)
return
/datum/buildmode/Destroy()
stored = null
for(var/button in buttons)
qdel(button)
/datum/buildmode/proc/create_buttons()
buttons += new /obj/screen/buildmode/mode(src)
buttons += new /obj/screen/buildmode/help(src)
buttons += new /obj/screen/buildmode/bdir(src)
buttons += new /obj/screen/buildmode/quit(src)
/datum/buildmode/proc/toggle_modes()
mode = (mode % NUM_BUILDMODES) +1
Reset()
return
/datum/buildmode/proc/show_help(mob/user)
switch(mode)
if(BASIC_BUILDMODE)
user << "\blue ***********************************************************"
user << "\blue Left Mouse Button = Construct / Upgrade"
user << "\blue Right Mouse Button = Deconstruct / Delete / Downgrade"
user << "\blue Left Mouse Button + ctrl = R-Window"
user << "\blue Left Mouse Button + alt = Airlock"
user << ""
user << "\blue Use the button in the upper left corner to"
user << "\blue change the direction of built objects."
user << "\blue ***********************************************************"
if(ADV_BUILDMODE)
user << "\blue ***********************************************************"
user << "\blue Right Mouse Button on buildmode button = Set object type"
user << "\blue Left Mouse Button on turf/obj = Place objects"
user << "\blue Right Mouse Button = Delete objects"
user << ""
user << "\blue Use the button in the upper left corner to"
user << "\blue change the direction of built objects."
user << "\blue ***********************************************************"
if(VAR_BUILDMODE)
user << "\blue ***********************************************************"
user << "\blue Right Mouse Button on buildmode button = Select var(type) & value"
user << "\blue Left Mouse Button on turf/obj/mob = Set var(type) & value"
user << "\blue Right Mouse Button on turf/obj/mob = Reset var's value"
user << "\blue ***********************************************************"
if(THROW_BUILDMODE)
user << "\blue ***********************************************************"
user << "\blue Left Mouse Button on turf/obj/mob = Select"
user << "\blue Right Mouse Button on turf/obj/mob = Throw"
user << "\blue ***********************************************************"
if(AREA_BUILDMODE)
user << "\blue ***********************************************************"
user << "\blue Left Mouse Button on turf/obj/mob = Select corner"
user << "\blue Right Mouse Button on buildmode button = Select generator"
user << "\blue ***********************************************************"
if(COPY_BUILDMODE)
user << "\blue ***********************************************************"
user << "\blue Left Mouse Button on obj/turf/mob = Spawn a Copy of selected target"
user << "\blue Right Mouse Button on obj/mob = Select target to copy"
user << "\blue ***********************************************************"
/datum/buildmode/proc/change_settings(mob/user)
switch(mode)
if(BASIC_BUILDMODE)
return 1
if(ADV_BUILDMODE)
var/target_path = input(user,"Enter typepath:" ,"Typepath","/obj/structure/closet")
objholder = text2path(target_path)
if(!ispath(objholder))
objholder = pick_closest_path(target_path)
if(!objholder)
objholder = /obj/structure/closet
alert("That path is not allowed.")
else
if(ispath(objholder,/mob) && !check_rights(R_DEBUG,0))
objholder = /obj/structure/closet
if(VAR_BUILDMODE)
var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "telekinesis", "xray", "virus", "viruses", "cuffed", "ka", "last_eaten", "urine")
varholder = input(user,"Enter variable name:" ,"Name", "name")
if(varholder in locked && !check_rights(R_DEBUG,0))
return 1
var/thetype = input(user,"Select variable type:" ,"Type") in list("text","number","mob-reference","obj-reference","turf-reference")
if(!thetype) return 1
switch(thetype)
if("text")
valueholder = input(user,"Enter variable value:" ,"Value", "value") as text
if("number")
valueholder = input(user,"Enter variable value:" ,"Value", 123) as num
if("mob-reference")
valueholder = input(user,"Enter variable value:" ,"Value") as mob in mob_list
if("obj-reference")
valueholder = input(user,"Enter variable value:" ,"Value") as obj in world
if("turf-reference")
valueholder = input(user,"Enter variable value:" ,"Value") as turf in world
if(AREA_BUILDMODE)
var/list/gen_paths = subtypesof(/datum/mapGenerator)
var/type = input(user,"Select Generator Type","Type") as null|anything in gen_paths
if(!type) return
generator_path = type
cornerA = null
cornerB = null
/datum/buildmode/proc/change_dir()
switch(build_dir)
if(NORTH)
build_dir = EAST
if(EAST)
build_dir = SOUTH
if(SOUTH)
build_dir = WEST
if(WEST)
build_dir = NORTHWEST
if(NORTHWEST)
build_dir = NORTH
return 1
/datum/buildmode/proc/Reset()//Reset temporary variables
cornerA = null
cornerB = null
/proc/togglebuildmode(mob/M in player_list)
set name = "Toggle Build Mode"
set category = "Special Verbs"
if(M.client)
if(istype(M.client.click_intercept,/datum/buildmode))
var/datum/buildmode/B = M.client.click_intercept
B.quit()
log_admin("[key_name(usr)] has left build mode.")
else
new/datum/buildmode(M.client)
message_admins("[key_name(usr)] has entered build mode.")
log_admin("[key_name(usr)] has entered build mode.")
/datum/buildmode/proc/InterceptClickOn(user,params,atom/object) //Click Intercept
var/list/pa = params2list(params)
var/right_click = pa.Find("right")
var/left_click = pa.Find("left")
var/alt_click = pa.Find("alt")
var/ctrl_click = pa.Find("ctrl")
. = 1
switch(mode)
if(BASIC_BUILDMODE)
if(istype(object,/turf) && left_click && !alt_click && !ctrl_click)
var/turf/T = object
if(istype(object,/turf/open/space))
T.ChangeTurf(/turf/open/floor/plasteel)
else if(istype(object,/turf/open/floor))
T.ChangeTurf(/turf/closed/wall)
else if(istype(object,/turf/closed/wall))
T.ChangeTurf(/turf/closed/wall/r_wall)
log_admin("Build Mode: [key_name(user)] built [T] at ([T.x],[T.y],[T.z])")
return
else if(right_click)
log_admin("Build Mode: [key_name(user)] deleted [object] at ([object.x],[object.y],[object.z])")
if(istype(object,/turf/closed/wall))
var/turf/T = object
T.ChangeTurf(/turf/open/floor/plasteel)
else if(istype(object,/turf/open/floor))
var/turf/T = object
T.ChangeTurf(/turf/open/space)
else if(istype(object,/turf/closed/wall/r_wall))
var/turf/T = object
T.ChangeTurf(/turf/closed/wall)
else if(istype(object,/obj))
qdel(object)
return
else if(istype(object,/turf) && alt_click && left_click)
log_admin("Build Mode: [key_name(user)] built an airlock at ([object.x],[object.y],[object.z])")
new/obj/machinery/door/airlock(get_turf(object))
else if(istype(object,/turf) && ctrl_click && left_click)
switch(build_dir)
if(NORTH)
var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object))
WIN.setDir(NORTH)
if(SOUTH)
var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object))
WIN.setDir(SOUTH)
if(EAST)
var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object))
WIN.setDir(EAST)
if(WEST)
var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object))
WIN.setDir(WEST)
if(NORTHWEST)
var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object))
WIN.setDir(NORTHWEST)
log_admin("Build Mode: [key_name(user)] built a window at ([object.x],[object.y],[object.z])")
if(ADV_BUILDMODE)
if(left_click)
if(ispath(objholder,/turf))
var/turf/T = get_turf(object)
log_admin("Build Mode: [key_name(user)] modified [T] ([T.x],[T.y],[T.z]) to [objholder]")
T.ChangeTurf(objholder)
else
var/obj/A = new objholder (get_turf(object))
A.setDir(build_dir)
log_admin("Build Mode: [key_name(user)] modified [A]'s ([A.x],[A.y],[A.z]) dir to [build_dir]")
else if(right_click)
if(isobj(object))
log_admin("Build Mode: [key_name(user)] deleted [object] at ([object.x],[object.y],[object.z])")
qdel(object)
if(VAR_BUILDMODE)
if(left_click) //I cant believe this shit actually compiles.
if(object.vars.Find(varholder))
log_admin("Build Mode: [key_name(user)] modified [object.name]'s [varholder] to [valueholder]")
object.vars[varholder] = valueholder
else
user << "<span class='warning'>[initial(object.name)] does not have a var called '[varholder]'</span>"
if(right_click)
if(object.vars.Find(varholder))
log_admin("Build Mode: [key_name(user)] modified [object.name]'s [varholder] to [valueholder]")
object.vars[varholder] = initial(object.vars[varholder])
else
user << "<span class='warning'>[initial(object.name)] does not have a var called '[varholder]'</span>"
if(THROW_BUILDMODE)
if(left_click)
if(isturf(object))
return
throw_atom = object
if(right_click)
if(throw_atom)
throw_atom.throw_at(object, 10, 1,user)
log_admin("Build Mode: [key_name(user)] threw [throw_atom] at [object] ([object.x],[object.y],[object.z])")
if(AREA_BUILDMODE)
if(!cornerA)
cornerA = get_turf(object)
return
if(cornerA && !cornerB)
cornerB = get_turf(object)
if(left_click) //rectangular
if(cornerA && cornerB)
if(!generator_path)
user << "<span class='warning'>Select generator type first.</span>"
var/datum/mapGenerator/G = new generator_path
G.defineRegion(cornerA,cornerB,1)
G.generate()
cornerA = null
cornerB = null
return
//Something wrong - Reset
cornerA = null
cornerB = null
if(COPY_BUILDMODE)
if(left_click)
var/turf/T = get_turf(object)
if(stored)
DuplicateObject(stored,perfectcopy=1,newloc=T)
else if(right_click)
if(ismovableatom(object)) // No copying turfs for now.
stored = object
+18
View File
@@ -0,0 +1,18 @@
/client/proc/cinematic(cinematic as anything in list("explosion",null))
set name = "cinematic"
set category = "Fun"
set desc = "Shows a cinematic." // Intended for testing but I thought it might be nice for events on the rare occasion Feel free to comment it out if it's not wanted.
set hidden = 1
if(!ticker)
return
switch(cinematic)
if("explosion")
var/parameter = input(src,"station_missed = ?","Enter Parameter",0) as num
var/override
switch(parameter)
if(1)
override = input(src,"mode = ?","Enter Parameter",null) as anything in list("nuclear emergency","gang war","fake","no override")
if(0)
override = input(src,"mode = ?","Enter Parameter",null) as anything in list("blob","nuclear emergency","AI malfunction","no override")
ticker.station_explosion_cinematic(parameter,override)
return
+32
View File
@@ -0,0 +1,32 @@
/client/proc/dsay(msg as text)
set category = "Special Verbs"
set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite
set hidden = 1
if(!src.holder)
src << "Only administrators may use this command."
return
if(!src.mob)
return
if(prefs.muted & MUTE_DEADCHAT)
src << "<span class='danger'>You cannot send DSAY messages (muted).</span>"
return
if (src.handle_spam_prevention(msg,MUTE_DEADCHAT))
return
msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
log_dsay("[key_name(src)] : [msg]")
if (!msg)
return
var/nicknames = file2list("config/admin_nicknames.txt")
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>ADMIN([src.holder.fakekey ? pick(nicknames) : src.key])</span> says, <span class='message'>\"[msg]\"</span></span>"
for (var/mob/M in player_list)
if (istype(M, /mob/new_player))
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)
feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+769
View File
@@ -0,0 +1,769 @@
/client/proc/Debug2()
set category = "Debug"
set name = "Debug-Game"
if(!check_rights(R_DEBUG))
return
if(Debug2)
Debug2 = 0
message_admins("[key_name(src)] toggled debugging off.")
log_admin("[key_name(src)] toggled debugging off.")
else
Debug2 = 1
message_admins("[key_name(src)] toggled debugging on.")
log_admin("[key_name(src)] toggled debugging on.")
feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/* 21st Sept 2010
Updated by Skie -- Still not perfect but better!
Stuff you can't do:
Call proc /mob/proc/Dizzy() for some player
Because if you select a player mob as owner it tries to do the proc for
/mob/living/carbon/human/ instead. And that gives a run-time error.
But you can call procs that are of type /mob/living/carbon/human/proc/ for that player.
*/
/client/proc/callproc()
set category = "Debug"
set name = "Advanced ProcCall"
set waitfor = 0
if(!check_rights(R_DEBUG)) return
var/target = null
var/targetselected = 0
var/returnval = null
var/class = null
switch(alert("Proc owned by something?",,"Yes","No"))
if("Yes")
targetselected = 1
if(src.holder && src.holder.marked_datum)
class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client","Marked datum ([holder.marked_datum.type])")
if(class == "Marked datum ([holder.marked_datum.type])")
class = "Marked datum"
else
class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client")
switch(class)
if("Obj")
target = input("Enter target:","Target",usr) as obj in world
if("Mob")
target = input("Enter target:","Target",usr) as mob in world
if("Area or Turf")
target = input("Enter target:","Target",usr.loc) as area|turf in world
if("Client")
var/list/keys = list()
for(var/client/C)
keys += C
target = input("Please, select a player!", "Selection", null, null) as null|anything in keys
if("Marked datum")
target = holder.marked_datum
else
return
if("No")
target = null
targetselected = 0
var/procname = input("Proc path, eg: /proc/fake_blood","Path:", null) as text|null
if(!procname)
return
if(targetselected && !hascall(target,procname))
usr << "<font color='red'>Error: callproc(): target has no such call [procname].</font>"
return
else
var/procpath = text2path(procname)
if (!procpath)
usr << "<font color='red'>Error: callproc(): proc [procname] does not exist. (Did you forget the /proc/ part?)</font>"
return
var/list/lst = get_callproc_args()
if(!lst)
return
if(targetselected)
if(!target)
usr << "<font color='red'>Error: callproc(): owner of proc no longer exists.</font>"
return
log_admin("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
message_admins("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
returnval = call(target,procname)(arglist(lst)) // Pass the lst as an argument list to the proc
else
//this currently has no hascall protection. wasn't able to get it working.
log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
returnval = call(procname)(arglist(lst)) // Pass the lst as an argument list to the proc
. = get_callproc_returnval(returnval, procname)
if(.)
usr << .
feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/callproc_datum(A as null|area|mob|obj|turf)
set category = "Debug"
set name = "Atom ProcCall"
set waitfor = 0
if(!check_rights(R_DEBUG))
return
var/procname = input("Proc name, eg: fake_blood","Proc:", null) as text|null
if(!procname)
return
if(!hascall(A,procname))
usr << "<span class='warning'>Error: callproc_datum(): target has no such call [procname].</span>"
return
var/list/lst = get_callproc_args()
if(!lst)
return
if(!A || !IsValidSrc(A))
usr << "<span class='warning'>Error: callproc_datum(): owner of proc no longer exists.</span>"
return
log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
message_admins("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
feedback_add_details("admin_verb","DPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
var/returnval = call(A,procname)(arglist(lst)) // Pass the lst as an argument list to the proc
. = get_callproc_returnval(returnval,procname)
if(.)
usr << .
/client/proc/get_callproc_args()
var/argnum = input("Number of arguments","Number:",0) as num|null
if(!argnum && (argnum!=0))
return
var/list/lst = list()
//TODO: make a list to store whether each argument was initialised as null.
//Reason: So we can abort the proccall if say, one of our arguments was a mob which no longer exists
//this will protect us from a fair few errors ~Carn
while(argnum--)
var/class = null
// Make a list with each index containing one variable, to be given to the proc
if(src.holder && src.holder.marked_datum)
class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","Marked datum ([holder.marked_datum.type])","CANCEL")
if(holder.marked_datum && class == "Marked datum ([holder.marked_datum.type])")
class = "Marked datum"
else
class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","CANCEL")
switch(class)
if("CANCEL")
return null
if("text")
lst += input("Enter new text:","Text",null) as text
if("num")
lst += input("Enter new number:","Num",0) as num
if("type")
lst += input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf)
if("reference")
lst += input("Select reference:","Reference",src) as mob|obj|turf|area in world
if("mob reference")
lst += input("Select reference:","Reference",usr) as mob in world
if("file")
lst += input("Pick file:","File") as file
if("icon")
lst += input("Pick icon:","Icon") as icon
if("client")
var/list/keys = list()
for(var/mob/M in world)
keys += M.client
lst += input("Please, select a player!", "Selection", null, null) as null|anything in keys
if("mob's area")
var/mob/temp = input("Select mob", "Selection", usr) as mob in world
lst += temp.loc
if("Marked datum")
lst += holder.marked_datum
return lst
/client/proc/get_callproc_returnval(returnval,procname)
. = ""
if(islist(returnval))
var/list/returnedlist = returnval
. = "<font color='blue'>"
if(returnedlist.len)
var/assoc_check = returnedlist[1]
if(istext(assoc_check) && (returnedlist[assoc_check] != null))
. += "[procname] returned an associative list:"
for(var/key in returnedlist)
. += "\n[key] = [returnedlist[key]]"
else
. += "[procname] returned a list:"
for(var/elem in returnedlist)
. += "\n[elem]"
else
. = "[procname] returned an empty list"
. += "</font>"
else
. = "<font color='blue'>[procname] returned: [returnval ? returnval : "null"]</font>"
/client/proc/Cell()
set category = "Debug"
set name = "Air Status in Location"
if(!mob)
return
var/turf/T = mob.loc
if (!( istype(T, /turf) ))
return
var/datum/gas_mixture/env = T.return_air()
var/list/env_gases = env.gases
var/t = ""
for(var/id in env_gases)
if(id in hardcoded_gases || env_gases[id][MOLES])
t+= "[env_gases[id][GAS_META][META_GAS_NAME]] : [env_gases[id][MOLES]]\n"
usr << t
feedback_add_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_robotize(mob/M in mob_list)
set category = "Fun"
set name = "Make Robot"
if(!ticker || !ticker.mode)
alert("Wait until the game starts")
return
if(istype(M, /mob/living/carbon/human))
log_admin("[key_name(src)] has robotized [M.key].")
var/mob/living/carbon/human/H = M
spawn(0)
H.Robotize()
else
alert("Invalid mob")
/client/proc/cmd_admin_blobize(mob/M in mob_list)
set category = "Fun"
set name = "Make Blob"
if(!ticker || !ticker.mode)
alert("Wait until the game starts")
return
if(istype(M, /mob/living/carbon/human))
log_admin("[key_name(src)] has blobized [M.key].")
var/mob/living/carbon/human/H = M
spawn(0)
var/mob/camera/blob/B = H.become_overmind()
B.place_blob_core(B.base_point_rate, -1) //place them wherever they are
else
alert("Invalid mob")
/client/proc/cmd_admin_animalize(mob/M in mob_list)
set category = "Fun"
set name = "Make Simple Animal"
if(!ticker || !ticker.mode)
alert("Wait until the game starts")
return
if(!M)
alert("That mob doesn't seem to exist, close the panel and try again.")
return
if(istype(M, /mob/new_player))
alert("The mob must not be a new_player.")
return
log_admin("[key_name(src)] has animalized [M.key].")
spawn(0)
M.Animalize()
/client/proc/makepAI(turf/T in mob_list)
set category = "Fun"
set name = "Make pAI"
set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI"
var/list/available = list()
for(var/mob/C in mob_list)
if(C.key)
available.Add(C)
var/mob/choice = input("Choose a player to play the pAI", "Spawn pAI") in available
if(!choice)
return 0
if(!istype(choice, /mob/dead/observer))
var/confirm = input("[choice.key] isn't ghosting right now. Are you sure you want to yank him out of them out of their body and place them in this pAI?", "Spawn pAI Confirmation", "No") in list("Yes", "No")
if(confirm != "Yes")
return 0
var/obj/item/device/paicard/card = new(T)
var/mob/living/silicon/pai/pai = new(card)
pai.name = input(choice, "Enter your pAI name:", "pAI Name", "Personal AI") as text
pai.real_name = pai.name
pai.key = choice.key
card.setPersonality(pai)
for(var/datum/paiCandidate/candidate in SSpai.candidates)
if(candidate.key == choice.key)
SSpai.candidates.Remove(candidate)
feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_alienize(mob/M in mob_list)
set category = "Fun"
set name = "Make Alien"
if(!ticker || !ticker.mode)
alert("Wait until the game starts")
return
if(ishuman(M))
log_admin("[key_name(src)] has alienized [M.key].")
spawn(0)
M:Alienize()
feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] made [key_name(M)] into an alien.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made [key_name(M)] into an alien.</span>")
else
alert("Invalid mob")
/client/proc/cmd_admin_slimeize(mob/M in mob_list)
set category = "Fun"
set name = "Make slime"
if(!ticker || !ticker.mode)
alert("Wait until the game starts")
return
if(ishuman(M))
log_admin("[key_name(src)] has slimeized [M.key].")
spawn(0)
M:slimeize()
feedback_add_details("admin_verb","MKMET") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] made [key_name(M)] into a slime.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made [key_name(M)] into a slime.</span>")
else
alert("Invalid mob")
var/list/TYPES_SHORTCUTS = list(
/obj/effect/decal/cleanable = "CLEANABLE",
/obj/item/device/radio/headset = "HEADSET",
/obj/item/clothing/head/helmet/space = "SPESSHELMET",
/obj/item/weapon/book/manual = "MANUAL",
/obj/item/weapon/reagent_containers/food/drinks = "DRINK", //longest paths comes first
/obj/item/weapon/reagent_containers/food = "FOOD",
/obj/item/weapon/reagent_containers = "REAGENT_CONTAINERS",
/obj/machinery/atmospherics = "ATMOS",
/obj/machinery/portable_atmospherics = "PORT_ATMOS",
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack = "MECHA_MISSILE_RACK",
/obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP",
/obj/item/organ = "ORGAN",
)
var/global/list/g_fancy_list_of_types = null
/proc/get_fancy_list_of_types()
if (isnull(g_fancy_list_of_types)) //init
var/list/temp = sortList(subtypesof(/atom) - typesof(/area) - /atom/movable)
g_fancy_list_of_types = new(temp.len)
for(var/type in temp)
var/typename = "[type]"
for (var/tn in TYPES_SHORTCUTS)
if (copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ )
typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/"))
break
g_fancy_list_of_types[typename] = type
return g_fancy_list_of_types
/proc/filter_fancy_list(list/L, filter as text)
var/list/matches = new
for(var/key in L)
var/value = L[key]
if(findtext("[key]", filter) || findtext("[value]", filter))
matches[key] = value
return matches
//TODO: merge the vievars version into this or something maybe mayhaps
/client/proc/cmd_debug_del_all(object as text)
set category = "Debug"
set name = "Del-All"
var/list/matches = get_fancy_list_of_types()
if (!isnull(object) && object!="")
matches = filter_fancy_list(matches, object)
if(matches.len==0)
return
var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in matches
if(hsbitem)
hsbitem = matches[hsbitem]
var/counter = 0
for(var/atom/O in world)
if(istype(O, hsbitem))
counter++
qdel(O)
CHECK_TICK
log_admin("[key_name(src)] has deleted all ([counter]) instances of [hsbitem].")
message_admins("[key_name_admin(src)] has deleted all ([counter]) instances of [hsbitem].", 0)
feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_debug_make_powernets()
set category = "Debug"
set name = "Make Powernets"
SSmachine.makepowernets()
log_admin("[key_name(src)] has remade the powernet. makepowernets() called.")
message_admins("[key_name_admin(src)] has remade the powernets. makepowernets() called.", 0)
feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_grantfullaccess(mob/M in mob_list)
set category = "Admin"
set name = "Grant Full Access"
if(!ticker || !ticker.mode)
alert("Wait until the game starts")
return
if (istype(M, /mob/living/carbon/human))
var/mob/living/carbon/human/H = M
var/obj/item/worn = H.wear_id
var/obj/item/weapon/card/id/id = null
if(worn)
id = worn.GetID()
if(id)
id.icon_state = "gold"
id.access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access()
else
id = new /obj/item/weapon/card/id/gold(H.loc)
id.access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access()
id.registered_name = H.real_name
id.assignment = "Captain"
id.update_label()
if(worn)
if(istype(worn,/obj/item/device/pda))
worn:id = id
id.loc = worn
else if(istype(worn,/obj/item/weapon/storage/wallet))
worn:front_id = id
id.loc = worn
worn.update_icon()
else
H.equip_to_slot(id,slot_wear_id)
else
alert("Invalid mob")
feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(src)] has granted [M.key] full access.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has granted [M.key] full access.</span>")
/client/proc/cmd_assume_direct_control(mob/M in mob_list)
set category = "Admin"
set name = "Assume direct control"
set desc = "Direct intervention"
if(M.ckey)
if(alert("This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.",,"Yes","No") != "Yes")
return
else
var/mob/dead/observer/ghost = new/mob/dead/observer(M,1)
ghost.ckey = M.ckey
message_admins("<span class='adminnotice'>[key_name_admin(usr)] assumed direct control of [M].</span>")
log_admin("[key_name(usr)] assumed direct control of [M].")
var/mob/adminmob = src.mob
M.ckey = src.ckey
if( isobserver(adminmob) )
qdel(adminmob)
feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_areatest()
set category = "Mapping"
set name = "Test areas"
var/list/areas_all = list()
var/list/areas_with_APC = list()
var/list/areas_with_air_alarm = list()
var/list/areas_with_RC = list()
var/list/areas_with_light = list()
var/list/areas_with_LS = list()
var/list/areas_with_intercom = list()
var/list/areas_with_camera = list()
for(var/area/A in world)
if(!(A.type in areas_all))
areas_all.Add(A.type)
for(var/obj/machinery/power/apc/APC in apcs_list)
var/area/A = get_area(APC)
if(!(A.type in areas_with_APC))
areas_with_APC.Add(A.type)
for(var/obj/machinery/airalarm/AA in machines)
var/area/A = get_area(AA)
if(!(A.type in areas_with_air_alarm))
areas_with_air_alarm.Add(A.type)
for(var/obj/machinery/requests_console/RC in machines)
var/area/A = get_area(RC)
if(!(A.type in areas_with_RC))
areas_with_RC.Add(A.type)
for(var/obj/machinery/light/L in machines)
var/area/A = get_area(L)
if(!(A.type in areas_with_light))
areas_with_light.Add(A.type)
for(var/obj/machinery/light_switch/LS in machines)
var/area/A = get_area(LS)
if(!(A.type in areas_with_LS))
areas_with_LS.Add(A.type)
for(var/obj/item/device/radio/intercom/I in machines)
var/area/A = get_area(I)
if(!(A.type in areas_with_intercom))
areas_with_intercom.Add(A.type)
for(var/obj/machinery/camera/C in machines)
var/area/A = get_area(C)
if(!(A.type in areas_with_camera))
areas_with_camera.Add(A.type)
var/list/areas_without_APC = areas_all - areas_with_APC
var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm
var/list/areas_without_RC = areas_all - areas_with_RC
var/list/areas_without_light = areas_all - areas_with_light
var/list/areas_without_LS = areas_all - areas_with_LS
var/list/areas_without_intercom = areas_all - areas_with_intercom
var/list/areas_without_camera = areas_all - areas_with_camera
world << "<b>AREAS WITHOUT AN APC:</b>"
for(var/areatype in areas_without_APC)
world << "* [areatype]"
world << "<b>AREAS WITHOUT AN AIR ALARM:</b>"
for(var/areatype in areas_without_air_alarm)
world << "* [areatype]"
world << "<b>AREAS WITHOUT A REQUEST CONSOLE:</b>"
for(var/areatype in areas_without_RC)
world << "* [areatype]"
world << "<b>AREAS WITHOUT ANY LIGHTS:</b>"
for(var/areatype in areas_without_light)
world << "* [areatype]"
world << "<b>AREAS WITHOUT A LIGHT SWITCH:</b>"
for(var/areatype in areas_without_LS)
world << "* [areatype]"
world << "<b>AREAS WITHOUT ANY INTERCOMS:</b>"
for(var/areatype in areas_without_intercom)
world << "* [areatype]"
world << "<b>AREAS WITHOUT ANY CAMERAS:</b>"
for(var/areatype in areas_without_camera)
world << "* [areatype]"
/client/proc/cmd_admin_dress(mob/living/carbon/human/M in mob_list)
set category = "Fun"
set name = "Select equipment"
if(!ishuman(M))
alert("Invalid mob")
return
//log_admin("[key_name(src)] has alienized [M.key].")
var/list/outfits = list("Naked","Custom","As Job...")
var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job)
for(var/path in paths)
var/datum/outfit/O = path //not much to initalize here but whatever
outfits[initial(O.name)] = path
var/dresscode = input("Select dress for [M]", "Robust quick dress shop") as null|anything in outfits
if (isnull(dresscode))
return
var/datum/job/jobdatum
if (dresscode == "As Job...")
var/jobname = input("Select job", "Robust quick dress shop") as null|anything in get_all_jobs()
if(isnull(jobname))
return
jobdatum = SSjob.GetJob(jobname)
var/datum/outfit/custom = null
if (dresscode == "Custom")
var/list/custom_names = list()
for(var/datum/outfit/D in custom_outfits)
custom_names[D.name] = D
var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in custom_names
custom = custom_names[selected_name]
if(isnull(custom))
return
feedback_add_details("admin_verb","SEQ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
for (var/obj/item/I in M)
if (istype(I, /obj/item/weapon/implant))
continue
qdel(I)
switch(dresscode)
if ("Naked")
//do nothing
if ("Custom")
//use custom one
M.equipOutfit(custom)
if ("As Job...")
if(jobdatum)
dresscode = jobdatum.title
M.job = jobdatum.title
jobdatum.equip(M)
else
M.equipOutfit(outfits[dresscode])
M.regenerate_icons()
log_admin("[key_name(usr)] changed the equipment of [key_name(M)] to [dresscode].")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] changed the equipment of [key_name_admin(M)] to [dresscode]..</span>")
return
/client/proc/startSinglo()
set category = "Debug"
set name = "Start Singularity"
set desc = "Sets up the singularity and all machines to get power flowing through the station"
if(alert("Are you sure? This will start up the engine. Should only be used during debug!",,"Yes","No") != "Yes")
return
for(var/obj/machinery/power/emitter/E in machines)
if(E.anchored)
E.active = 1
for(var/obj/machinery/field/generator/F in machines)
if(F.active == 0)
F.active = 1
F.state = 2
F.power = 250
F.anchored = 1
F.warming_up = 3
F.start_fields()
F.update_icon()
spawn(30)
for(var/obj/machinery/the_singularitygen/G in machines)
if(G.anchored)
var/obj/singularity/S = new /obj/singularity(get_turf(G), 50)
// qdel(G)
S.energy = 1750
S.current_size = 7
S.icon = 'icons/effects/224x224.dmi'
S.icon_state = "singularity_s7"
S.pixel_x = -96
S.pixel_y = -96
S.grav_pull = 0
//S.consume_range = 3
S.dissipate = 0
//S.dissipate_delay = 10
//S.dissipate_track = 0
//S.dissipate_strength = 10
for(var/obj/machinery/power/rad_collector/Rad in machines)
if(Rad.anchored)
if(!Rad.loaded_tank)
var/obj/item/weapon/tank/internals/plasma/Plasma = new/obj/item/weapon/tank/internals/plasma(Rad)
Plasma.air_contents.assert_gas("plasma")
Plasma.air_contents.gases["plasma"][MOLES] = 70
Rad.drainratio = 0
Rad.loaded_tank = Plasma
Plasma.loc = Rad
if(!Rad.active)
Rad.toggle_power()
for(var/obj/machinery/power/smes/SMES in machines)
if(SMES.anchored)
SMES.input_attempt = 1
/client/proc/cmd_debug_mob_lists()
set category = "Debug"
set name = "Debug Mob Lists"
set desc = "For when you just gotta know"
switch(input("Which list?") in list("Players","Admins","Mobs","Living Mobs","Dead Mobs","Clients","Joined Clients"))
if("Players")
usr << jointext(player_list,",")
if("Admins")
usr << jointext(admins,",")
if("Mobs")
usr << jointext(mob_list,",")
if("Living Mobs")
usr << jointext(living_mob_list,",")
if("Dead Mobs")
usr << jointext(dead_mob_list,",")
if("Clients")
usr << jointext(clients,",")
if("Joined Clients")
usr << jointext(joined_player_list,",")
/client/proc/cmd_display_del_log()
set category = "Debug"
set name = "Display del() Log"
set desc = "Displays a list of things that have failed to GC this round"
var/dat = "<B>List of things that failed to GC this round</B><BR><BR>"
for(var/path in SSgarbage.didntgc)
dat += "[path] - [SSgarbage.didntgc[path]] times<BR>"
dat += "<B>List of paths that did not return a qdel hint in Destroy()</B><BR><BR>"
for(var/path in SSgarbage.noqdelhint)
dat += "[path]<BR>"
usr << browse(dat, "window=dellog")
/client/proc/debug_huds(i as num)
set category = "Debug"
set name = "Debug HUDs"
set desc = "Debug the data or antag HUDs"
if(!holder)
return
debug_variables(huds[i])
/client/proc/jump_to_ruin()
set category = "Debug"
set name = "Jump to Ruin"
set desc = "Displays a list of all placed ruins to teleport to."
if(!holder)
return
var/list/names = list()
for(var/i in ruin_landmarks)
var/obj/effect/landmark/ruin/ruin_landmark = i
var/datum/map_template/ruin/template = ruin_landmark.ruin_template
var/count = 1
var/name = template.name
var/original_name = name
while(name in names)
count++
name = "[original_name] ([count])"
names[name] = ruin_landmark
var/ruinname = input("Select ruin", "Jump to Ruin") as null|anything in names
var/obj/effect/landmark/ruin/landmark = names[ruinname]
if(istype(landmark))
var/datum/map_template/ruin/template = landmark.ruin_template
usr.forceMove(get_turf(landmark))
usr << "<span class='name'>[template.name]</span>"
usr << "<span class='italics'>[template.description]</span>"
+102
View File
@@ -0,0 +1,102 @@
/client/proc/air_status(turf/target)
set category = "Debug"
set name = "Display Air Status"
if(!isturf(target))
return
var/datum/gas_mixture/GM = target.return_air()
var/list/GM_gases
var/burning = 0
if(istype(target, /turf/open))
var/turf/open/T = target
if(T.active_hotspot)
burning = 1
usr << "<span class='adminnotice'>@[target.x],[target.y]: [GM.temperature] Kelvin, [GM.return_pressure()] kPa [(burning)?("\red BURNING"):(null)]</span>"
for(var/id in GM_gases)
usr << "[GM_gases[id][GAS_META][META_GAS_NAME]]: [GM_gases[id][MOLES]]"
feedback_add_details("admin_verb","DAST") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/fix_next_move()
set category = "Debug"
set name = "Unfreeze Everyone"
var/largest_move_time = 0
var/largest_click_time = 0
var/mob/largest_move_mob = null
var/mob/largest_click_mob = null
for(var/mob/M in world)
if(!M.client)
continue
if(M.next_move >= largest_move_time)
largest_move_mob = M
if(M.next_move > world.time)
largest_move_time = M.next_move - world.time
else
largest_move_time = 1
if(M.next_click >= largest_click_time)
largest_click_mob = M
if(M.next_click > world.time)
largest_click_time = M.next_click - world.time
else
largest_click_time = 0
log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] lastDblClick = [M.next_click] world.time = [world.time]")
M.next_move = 1
M.next_click = 0
message_admins("[key_name_admin(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [largest_move_time/10] seconds!")
message_admins("[key_name_admin(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [largest_click_time/10] seconds!")
message_admins("world.time = [world.time]")
feedback_add_details("admin_verb","UFE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/radio_report()
set category = "Debug"
set name = "Radio report"
var/filters = list(
"1" = "RADIO_TO_AIRALARM",
"2" = "RADIO_FROM_AIRALARM",
"3" = "RADIO_CHAT",
"4" = "RADIO_ATMOSIA",
"5" = "RADIO_NAVBEACONS",
"6" = "RADIO_AIRLOCK",
"7" = "RADIO_SECBOT",
"8" = "RADIO_MULEBOT",
"_default" = "NO_FILTER"
)
var/output = "<b>Radio Report</b><hr>"
for (var/fq in SSradio.frequencies)
output += "<b>Freq: [fq]</b><br>"
var/list/datum/radio_frequency/fqs = SSradio.frequencies[fq]
if (!fqs)
output += "&nbsp;&nbsp;<b>ERROR</b><br>"
continue
for (var/filter in fqs.devices)
var/list/f = fqs.devices[filter]
if (!f)
output += "&nbsp;&nbsp;[filters[filter]]: ERROR<br>"
continue
output += "&nbsp;&nbsp;[filters[filter]]: [f.len]<br>"
for (var/device in f)
if (isobj(device))
output += "&nbsp;&nbsp;&nbsp;&nbsp;[device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)])<br>"
else
output += "&nbsp;&nbsp;&nbsp;&nbsp;[device]<br>"
usr << browse(output,"window=radioreport")
feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/reload_admins()
set name = "Reload Admins"
set category = "Admin"
if(!src.holder)
return
var/confirm = alert(src, "Are you sure you want to reload all admins?", "Confirm", "Yes", "No")
if(confirm !="Yes")
return
load_admins()
feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
message_admins("[key_name_admin(usr)] manually reloaded admins")
+24
View File
@@ -0,0 +1,24 @@
//replaces the old Ticklag verb, fps is easier to understand
/client/proc/fps()
set category = "Debug"
set name = "Set fps"
set desc = "Sets game speed in frames-per-second. Can potentially break the game"
if(!check_rights(R_DEBUG))
return
var/fps = round(input("Sets game frames-per-second. Can potentially break the game","FPS", config.fps) as num|null)
if(fps <= 0)
src << "<span class='danger'>Error: ticklag(): Invalid world.ticklag value. No changes made.</span>"
return
if(fps > config.fps)
if(alert(src, "You are setting fps to a high value:\n\t[fps] frames-per-second\n\tconfig.fps = [config.fps]","Warning!","Confirm","ABORT-ABORT-ABORT") != "Confirm")
return
var/msg = "[key_name(src)] has modified world.fps to [fps]"
log_admin(msg, 0)
message_admins(msg, 0)
feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
world.fps = fps
+108
View File
@@ -0,0 +1,108 @@
/*
HOW DO I LOG RUNTIMES?
Firstly, start dreamdeamon if it isn't already running. Then select "world>Log Session" (or press the F3 key)
navigate the popup window to the data/logs/runtimes/ folder from where your tgstation .dmb is located.
(you may have to make this folder yourself)
OPTIONAL: you can select the little checkbox down the bottom to make dreamdeamon save the log everytime you
start a world. Just remember to repeat these steps with a new name when you update to a new revision!
Save it with the name of the revision your server uses (e.g. r3459.txt).
Game Masters will now be able to grant access any runtime logs you have archived this way!
This will allow us to gather information on bugs across multiple servers and make maintaining the TG
codebase for the entire /TG/station commuity a TONNE easier :3 Thanks for your help!
*/
//This proc allows Game Masters to grant a client access to the .getruntimelog verb
//Permissions expire at the end of each round.
//Runtimes can be used to meta or spot game-crashing exploits so it's advised to only grant coders that
//you trust access. Also, it may be wise to ensure that they are not going to play in the current round.
/client/proc/giveruntimelog()
set name = ".giveruntimelog"
set desc = "Give somebody access to any session logfiles saved to the /log/runtime/ folder."
set category = null
if(!src.holder)
src << "<font color='red'>Only Admins may use this command.</font>"
return
var/client/target = input(src,"Choose somebody to grant access to the server's runtime logs (permissions expire at the end of each round):","Grant Permissions",null) as null|anything in clients
if(!istype(target,/client))
src << "<font color='red'>Error: giveruntimelog(): Client not found.</font>"
return
target.verbs |= /client/proc/getruntimelog
target << "<font color='red'>You have been granted access to runtime logs. Please use them responsibly or risk being banned.</font>"
return
//This proc allows download of runtime logs saved within the data/logs/ folder by dreamdeamon.
//It works similarly to show-server-log.
/client/proc/getruntimelog()
set name = ".getruntimelog"
set desc = "Retrieve any session logfiles saved by dreamdeamon."
set category = null
var/path = browse_files("data/logs/runtimes/")
if(!path)
return
if(file_spam_check())
return
message_admins("[key_name_admin(src)] accessed file: [path]")
src << ftp( file(path) )
src << "Attempting to send file, this may take a fair few minutes if the file is very large."
return
//This proc allows download of past server logs saved within the data/logs/ folder.
//It works similarly to show-server-log.
/client/proc/getserverlog()
set name = ".getserverlog"
set desc = "Fetch logfiles from data/logs"
set category = null
var/path = browse_files("data/logs/")
if(!path)
return
if(file_spam_check())
return
message_admins("[key_name_admin(src)] accessed file: [path]")
src << ftp( file(path) )
src << "Attempting to send file, this may take a fair few minutes if the file is very large."
return
//Other log stuff put here for the sake of organisation
//Shows today's server log
/datum/admins/proc/view_txt_log()
set category = "Admin"
set name = "Show Server Log"
set desc = "Shows today's server log."
if(fexists("[diary]"))
src << ftp(diary)
else
src << "<font color='red'>Server log not found, try using .getserverlog.</font>"
return
feedback_add_details("admin_verb","VTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
//Shows today's attack log
/datum/admins/proc/view_atk_log()
set category = "Admin"
set name = "Show Server Attack Log"
set desc = "Shows today's server attack log."
if(fexists("[diaryofmeanpeople]"))
src << ftp(diaryofmeanpeople)
else
src << "<font color='red'>Server attack log not found, try using .getserverlog.</font>"
return
feedback_add_details("admin_verb","SSAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
@@ -0,0 +1,10 @@
/proc/machine_upgrade(obj/machinery/M in world)
set name = "Tweak Component Ratings"
set category = "Debug"
var/new_rating = input("Enter new rating:","Num") as num
if(new_rating && M.component_parts)
for(var/obj/item/weapon/stock_parts/P in M.component_parts)
P.rating = new_rating
M.RefreshParts()
feedback_add_details("admin_verb","MU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -0,0 +1,56 @@
/client/proc/manipulate_organs(mob/living/carbon/C in world)
set name = "Manipulate Organs"
set category = "Debug"
var/operation = input("Select organ operation.", "Organ Manipulation", "cancel") in list("add organ", "add implant", "drop organ/implant", "remove organ/implant", "cancel")
var/list/organs = list()
switch(operation)
if("add organ")
for(var/path in subtypesof(/obj/item/organ))
var/dat = replacetext("[path]", "/obj/item/organ/", ":")
organs[dat] = path
var/obj/item/organ/organ = input("Select organ type:", "Organ Manipulation", null) in organs
organ = organs[organ]
organ = new organ
organ.Insert(C)
if("add implant")
for(var/path in subtypesof(/obj/item/weapon/implant))
var/dat = replacetext("[path]", "/obj/item/weapon/implant/", ":")
organs[dat] = path
var/obj/item/weapon/implant/organ = input("Select implant type:", "Organ Manipulation", null) in organs
organ = organs[organ]
organ = new organ
organ.implant(C)
if("drop organ/implant", "remove organ/implant")
for(var/obj/item/organ/I in C.internal_organs)
organs["[I.name] ([I.type])"] = I
for(var/obj/item/weapon/implant/I in C)
organs["[I.name] ([I.type])"] = I
var/obj/item/organ = input("Select organ/implant:", "Organ Manipulation", null) in organs
organ = organs[organ]
if(!organ) return
var/obj/item/organ/O
var/obj/item/weapon/implant/I
if(isorgan(organ))
O = organ
O.Remove(C)
else
I = organ
I.removed(C)
organ.loc = get_turf(C)
if(operation == "remove organ/implant")
qdel(organ)
else if(I) // Put the implant in case.
var/obj/item/weapon/implantcase/case = new(get_turf(C))
case.imp = I
I.loc = case
case.update_icon()
@@ -0,0 +1,44 @@
/client/proc/map_template_load()
set category = "Debug"
set name = "Map template - Place"
var/datum/map_template/template
var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in map_templates
if(!map)
return
template = map_templates[map]
var/turf/T = get_turf(mob)
if(!T)
return
var/list/preview = list()
for(var/S in template.get_affected_turfs(T,centered = TRUE))
preview += image('icons/turf/overlays.dmi',S,"greenOverlay")
usr.client.images += preview
if(alert(usr,"Confirm location.","Template Confirm","Yes","No") == "Yes")
if(template.load(T, centered = TRUE))
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has placed a map template ([template.name]) at <A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[T.x];Y=[T.y];Z=[T.z]'>(JMP)</a></span>")
else
usr << "Failed to place map"
usr.client.images -= preview
/client/proc/map_template_upload()
set category = "Debug"
set name = "Map Template - Upload"
var/map = input(usr, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file
if(!map)
return
if(copytext("[map]",-4) != ".dmm")
usr << "Bad map file: [map]"
return
var/datum/map_template/M = new(map=map, rename="[map]")
if(M.preload_size(map))
usr << "Map template '[map]' ready to place ([M.width]x[M.height])"
map_templates[M.name] = M
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has uploaded a map template ([map])</span>")
else
usr << "Map template '[map]' failed to load properly"
+255
View File
@@ -0,0 +1,255 @@
//- Are all the floors with or without air, as they should be? (regular or airless)
//- Does the area have an APC?
//- Does the area have an Air Alarm?
//- Does the area have a Request Console?
//- Does the area have lights?
//- Does the area have a light switch?
//- Does the area have enough intercoms?
//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug)
//- Is the area connected to the scrubbers air loop?
//- Is the area connected to the vent air loop? (vent pumps)
//- Is everything wired properly?
//- Does the area have a fire alarm and firedoors?
//- Do all pod doors work properly?
//- Are accesses set properly on doors, pod buttons, etc.
//- Are all items placed properly? (not below vents, scrubbers, tables)
//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room?
//- Check for any misplaced or stacked piece of pipe (air and disposal)
//- Check for any misplaced or stacked piece of wire
//- Identify how hard it is to break into the area and where the weak points are
//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels.
var/intercom_range_display_status = 0
var/list/admin_verbs_debug_mapping = list(
/client/proc/do_not_use_these, //-errorage
/client/proc/camera_view, //-errorage
/client/proc/sec_camera_report, //-errorage
/client/proc/intercom_view, //-errorage
/client/proc/air_status, //Air things
/client/proc/Cell, //More air things
/client/proc/atmosscan, //check plumbing
/client/proc/powerdebug, //check power
/client/proc/count_objects_on_z_level,
/client/proc/count_objects_all,
/client/proc/cmd_assume_direct_control, //-errorage
/client/proc/startSinglo,
/client/proc/fps, //allows you to set the ticklag.
/client/proc/cmd_admin_grantfullaccess,
/client/proc/cmd_admin_areatest,
/client/proc/cmd_admin_rejuvenate,
/datum/admins/proc/show_traitor_panel,
/client/proc/disable_communication,
/client/proc/print_pointers,
/client/proc/cmd_show_at_list,
/client/proc/cmd_show_at_list,
/client/proc/manipulate_organs
)
/obj/effect/debugging/marker
icon = 'icons/turf/areas.dmi'
icon_state = "yellow"
/obj/effect/debugging/marker/Move()
return 0
/client/proc/do_not_use_these()
set category = "Mapping"
set name = "-None of these are for ingame use!!"
..()
/client/proc/camera_view()
set category = "Mapping"
set name = "Camera Range Display"
var/on = 0
for(var/turf/T in world)
if(T.maptext)
on = 1
T.maptext = null
if(!on)
var/list/seen = list()
for(var/obj/machinery/camera/C in cameranet.cameras)
for(var/turf/T in C.can_see())
seen[T]++
for(var/turf/T in seen)
T.maptext = "[seen[T]]"
feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/sec_camera_report()
set category = "Mapping"
set name = "Camera Report"
if(!Master)
alert(usr,"Master_controller not found.","Sec Camera Report")
return 0
var/list/obj/machinery/camera/CL = list()
for(var/obj/machinery/camera/C in cameranet.cameras)
CL += C
var/output = {"<B>CAMERA ANNOMALITIES REPORT</B><HR>
<B>The following annomalities have been detected. The ones in red need immediate attention: Some of those in black may be intentional.</B><BR><ul>"}
for(var/obj/machinery/camera/C1 in CL)
for(var/obj/machinery/camera/C2 in CL)
if(C1 != C2)
if(C1.c_tag == C2.c_tag)
output += "<li><font color='red'>c_tag match for sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) and \[[C2.x], [C2.y], [C2.z]\] ([C2.loc.loc]) - c_tag is [C1.c_tag]</font></li>"
if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y)
output += "<li><font color='red'>FULLY overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]</font></li>"
if(C1.loc == C2.loc)
output += "<li>overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]</font></li>"
var/turf/T = get_step(C1,turn(C1.dir,180))
if(!T || !isturf(T) || !T.density )
if(!(locate(/obj/structure/grille,T)))
var/window_check = 0
for(var/obj/structure/window/W in T)
if (W.dir == turn(C1.dir,180) || W.dir in list(5,6,9,10) )
window_check = 1
break
if(!window_check)
output += "<li><font color='red'>Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]</color></li>"
output += "</ul>"
usr << browse(output,"window=airreport;size=1000x500")
feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/intercom_view()
set category = "Mapping"
set name = "Intercom Range Display"
if(intercom_range_display_status)
intercom_range_display_status = 0
else
intercom_range_display_status = 1
for(var/obj/effect/debugging/marker/M in world)
qdel(M)
if(intercom_range_display_status)
for(var/obj/item/device/radio/intercom/I in world)
for(var/turf/T in orange(7,I))
var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T)
if (!(F in view(7,I.loc)))
qdel(F)
feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_show_at_list()
set category = "Mapping"
set name = "Show roundstart AT list"
set desc = "Displays a list of active turfs coordinates at roundstart"
var/dat = {"<b>Coordinate list of Active Turfs at Roundstart</b>
<br>Real-time Active Turfs list you can see in Air Subsystem at active_turfs var<br>"}
for(var/i=1; i<=active_turfs_startlist.len; i++)
dat += active_turfs_startlist[i]
dat += "<br>"
usr << browse(dat, "window=at_list")
feedback_add_details("admin_verb","mATL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/enable_debug_verbs()
set category = "Debug"
set name = "Debug verbs - Enable"
if(!check_rights(R_DEBUG))
return
verbs -= /client/proc/enable_debug_verbs
verbs.Add(/client/proc/disable_debug_verbs, admin_verbs_debug_mapping)
feedback_add_details("admin_verb","mDVE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/disable_debug_verbs()
set category = "Debug"
set name = "Debug verbs - Disable"
verbs.Remove(/client/proc/disable_debug_verbs, admin_verbs_debug_mapping)
verbs += /client/proc/enable_debug_verbs
feedback_add_details("admin_verb", "mDVD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/count_objects_on_z_level()
set category = "Mapping"
set name = "Count Objects On Level"
var/level = input("Which z-level?","Level?") as text
if(!level) return
var/num_level = text2num(level)
if(!num_level) return
if(!isnum(num_level)) return
var/type_text = input("Which type path?","Path?") as text
if(!type_text) return
var/type_path = text2path(type_text)
if(!type_path) return
var/count = 0
var/list/atom/atom_list = list()
for(var/atom/A in world)
if(istype(A,type_path))
var/atom/B = A
while(!(isturf(B.loc)))
if(B && B.loc)
B = B.loc
else
break
if(B)
if(B.z == num_level)
count++
atom_list += A
/*
var/atom/temp_atom
for(var/i = 0; i <= (atom_list.len/10); i++)
var/line = ""
for(var/j = 1; j <= 10; j++)
if(i*10+j <= atom_list.len)
temp_atom = atom_list[i*10+j]
line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; "
world << line*/
world << "There are [count] objects of type [type_path] on z-level [num_level]"
feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/count_objects_all()
set category = "Mapping"
set name = "Count Objects All"
var/type_text = input("Which type path?","") as text
if(!type_text) return
var/type_path = text2path(type_text)
if(!type_path) return
var/count = 0
for(var/atom/A in world)
if(istype(A,type_path))
count++
/*
var/atom/temp_atom
for(var/i = 0; i <= (atom_list.len/10); i++)
var/line = ""
for(var/j = 1; j <= 10; j++)
if(i*10+j <= atom_list.len)
temp_atom = atom_list[i*10+j]
line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; "
world << line*/
world << "There are [count] objects of type [type_path] in the game world"
feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//This proc is intended to detect lag problems relating to communication procs
var/global/say_disabled = 0
/client/proc/disable_communication()
set category = "Mapping"
set name = "Disable all communication verbs"
say_disabled = !say_disabled
if(say_disabled)
message_admins("[src.ckey] used 'Disable all communication verbs', killing all communication methods.")
else
message_admins("[src.ckey] used 'Disable all communication verbs', restoring all communication methods.")
+44
View File
@@ -0,0 +1,44 @@
/client/proc/forcerandomrotate()
set category = "Server"
set name = "Trigger Random Map Rotation"
var/rotate = alert("Force a random map rotation to trigger?", "Rotate map?", "Yes", "Cancel")
if (rotate != "Yes")
return
message_admins("[key_name_admin(usr)] is forcing a random map rotation.")
log_admin("[key_name(usr)] is forcing a random map rotation.")
ticker.maprotatechecked = 1
maprotate()
/client/proc/adminchangemap()
set category = "Server"
set name = "Change Map"
var/list/maprotatechoices = list()
for (var/map in config.maplist)
var/datum/votablemap/VM = config.maplist[map]
var/mapname = VM.friendlyname
if (VM == config.defaultmap)
mapname += " (Default)"
if (VM.minusers > 0 || VM.maxusers > 0)
mapname += " \["
if (VM.minusers > 0)
mapname += "[VM.minusers]"
else
mapname += "0"
mapname += "-"
if (VM.maxusers > 0)
mapname += "[VM.maxusers]"
else
mapname += "inf"
mapname += "\]"
maprotatechoices[mapname] = VM
var/chosenmap = input("Choose a map to change to", "Change Map") as null|anything in maprotatechoices
if (!chosenmap)
return
ticker.maprotatechecked = 1
var/datum/votablemap/VM = maprotatechoices[chosenmap]
message_admins("[key_name_admin(usr)] is changing the map to [VM.name]([VM.friendlyname])")
log_admin("[key_name(usr)] is changing the map to [VM.name]([VM.friendlyname])")
if (changemap(VM) == 0)
message_admins("[key_name_admin(usr)] has changed the map to [VM.name]([VM.friendlyname])")
+538
View File
@@ -0,0 +1,538 @@
/client/proc/cmd_mass_modify_object_variables(atom/A, var_name)
set category = "Debug"
set name = "Mass Edit Variables"
set desc="(target) Edit all instances of a target item's variables"
var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL itms)
if(!check_rights(R_VAREDIT))
return
if(A && A.type)
if(typesof(A.type))
switch(input("Strict object type detection?") as null|anything in list("Strictly this type","This type and subtypes", "Cancel"))
if("Strictly this type")
method = 0
if("This type and subtypes")
method = 1
if("Cancel")
return
if(null)
return
src.massmodify_variables(A, var_name, method)
feedback_add_details("admin_verb","MEV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/massmodify_variables(atom/O, var_name = "", method = 0)
if(!check_rights(R_VAREDIT))
return
for(var/p in forbidden_varedit_object_types)
if( istype(O,p) )
usr << "<span class='danger'>It is forbidden to edit this object's variables.</span>"
return
var/list/names = list()
for (var/V in O.vars)
names += V
names = sortList(names)
var/variable = ""
if(!var_name)
variable = input("Which var?","Var") as null|anything in names
else
variable = var_name
if(!variable)
return
var/default
var/var_value = O.vars[variable]
var/dir
if(variable in VVckey_edit)
usr << "It's forbidden to mass-modify ckeys. I'll crash everyone's client you dummy."
return
if(variable in VVlocked)
if(!check_rights(R_DEBUG))
return
if(variable in VVicon_edit_lock)
if(!check_rights(R_FUN|R_DEBUG))
return
if(isnull(var_value))
usr << "Unable to determine variable type."
else if(isnum(var_value))
usr << "Variable appears to be <b>NUM</b>."
default = "num"
setDir(1)
else if(istext(var_value))
usr << "Variable appears to be <b>TEXT</b>."
default = "text"
else if(isloc(var_value))
usr << "Variable appears to be <b>REFERENCE</b>."
default = "reference"
else if(isicon(var_value))
usr << "Variable appears to be <b>ICON</b>."
var_value = "\icon[var_value]"
default = "icon"
else if(istype(var_value,/atom) || istype(var_value,/datum))
usr << "Variable appears to be <b>TYPE</b>."
default = "type"
else if(istype(var_value,/list))
usr << "Variable appears to be <b>LIST</b>."
default = "list"
else if(istype(var_value,/client))
usr << "Variable appears to be <b>CLIENT</b>."
default = "cancel"
else
usr << "Variable appears to be <b>FILE</b>."
default = "file"
usr << "Variable contains: [var_value]"
if(dir)
switch(var_value)
if(1)
setDir("NORTH")
if(2)
setDir("SOUTH")
if(4)
setDir("EAST")
if(8)
setDir("WEST")
if(5)
setDir("NORTHEAST")
if(6)
setDir("SOUTHEAST")
if(9)
setDir("NORTHWEST")
if(10)
setDir("SOUTHWEST")
else
setDir(null)
if(dir)
usr << "If a direction, direction is: [dir]"
var/class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
"num","type","icon","file","edit referenced object","restore to default")
if(!class)
return
var/original_name
if (!istype(O, /atom))
original_name = "\ref[O] ([O])"
else
original_name = O:name
switch(class)
if("restore to default")
O.vars[variable] = initial(O.vars[variable])
if(method)
if(istype(O, /mob))
for(var/mob/M in mob_list)
if ( istype(M , O.type) )
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if ( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if ( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
CHECK_TICK
else
if(istype(O, /mob))
for(var/mob/M in mob_list)
if (M.type == O.type)
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if (A.type == O.type)
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if (A.type == O.type)
A.vars[variable] = O.vars[variable]
CHECK_TICK
if("edit referenced object")
return .(O.vars[variable])
if("text")
var/new_value = input("Enter new text:","Text",O.vars[variable]) as message|null
if(new_value == null) return
var/process_vars = 0
var/unique = 0
if(findtext(new_value,"\["))
process_vars = alert(usr,"\[] detected in string, process as variables?","Process Variables?","Yes","No")
if(process_vars == "Yes")
process_vars = 1
unique = alert(usr,"Process vars unique to each instance, or same for all?","Variable Association","Unique","Same")
if(unique == "Unique")
unique = 1
else
unique = 0
else
process_vars = 0
var/pre_processing = new_value
var/list/varsvars = list()
if(process_vars)
varsvars = string2listofvars(new_value, O)
if(varsvars.len)
for(var/V in varsvars)
new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]")
O.vars[variable] = new_value
//Convert the string vars for anything that's not O
if(method)
if(istype(O, /mob))
for(var/mob/M in mob_list)
if ( istype(M , O.type) )
new_value = pre_processing //reset new_value, ready to convert it uniquely for the next iteration
if(process_vars)
if(unique)
for(var/V in varsvars)
new_value = replacetext(new_value,"\[[V]]","[M.vars[V]]")
else
new_value = O.vars[variable] //We already processed the non-unique form for O, reuse it
M.vars[variable] = new_value
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if ( istype(A , O.type) )
new_value = pre_processing
if(process_vars)
if(unique)
for(var/V in varsvars)
new_value = replacetext(new_value,"\[[V]]","[A.vars[V]]")
else
new_value = O.vars[variable]
A.vars[variable] = new_value
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if ( istype(A , O.type) )
new_value = pre_processing
if(process_vars)
if(unique)
for(var/V in varsvars)
new_value = replacetext(new_value,"\[[V]]","[A.vars[V]]")
else
new_value = O.vars[variable]
A.vars[variable] = new_value
CHECK_TICK
else
if(istype(O, /mob))
for(var/mob/M in mob_list)
if (M.type == O.type)
new_value = pre_processing
if(process_vars)
if(unique)
for(var/V in varsvars)
new_value = replacetext(new_value,"\[[V]]","[M.vars[V]]")
else
new_value = O.vars[variable]
M.vars[variable] = new_value
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if (A.type == O.type)
new_value = pre_processing
if(process_vars)
if(unique)
for(var/V in varsvars)
new_value = replacetext(new_value,"\[[V]]","[A.vars[V]]")
else
new_value = O.vars[variable]
A.vars[variable] = new_value
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if (A.type == O.type)
new_value = pre_processing
if(process_vars)
if(unique)
for(var/V in varsvars)
new_value = replacetext(new_value,"\[[V]]","[A.vars[V]]")
else
new_value = O.vars[variable]
A.vars[variable] = new_value
CHECK_TICK
if("num")
var/new_value = input("Enter new number:","Num",\
O.vars[variable]) as num|null
if(new_value == null) return
if(variable=="luminosity")
O.SetLuminosity(new_value)
else
O.vars[variable] = new_value
if(method)
if(istype(O, /mob))
for(var/mob/M in mob_list)
if ( istype(M , O.type) )
if(variable=="luminosity")
M.SetLuminosity(new_value)
else
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if ( istype(A , O.type) )
if(variable=="luminosity")
A.SetLuminosity(new_value)
else
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if ( istype(A , O.type) )
if(variable=="luminosity")
A.SetLuminosity(new_value)
else
A.vars[variable] = O.vars[variable]
CHECK_TICK
else
if(istype(O, /mob))
for(var/mob/M in mob_list)
if (M.type == O.type)
if(variable=="luminosity")
M.SetLuminosity(new_value)
else
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if (A.type == O.type)
if(variable=="luminosity")
A.SetLuminosity(new_value)
else
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if (A.type == O.type)
if(variable=="luminosity")
A.SetLuminosity(new_value)
else
A.vars[variable] = O.vars[variable]
CHECK_TICK
if("type")
var/new_value
new_value = input("Enter type:","Type",O.vars[variable]) as null|anything in typesof(/obj,/mob,/area,/turf)
if(new_value == null) return
O.vars[variable] = new_value
if(method)
if(istype(O, /mob))
for(var/mob/M in mob_list)
if ( istype(M , O.type) )
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if ( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if ( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
CHECK_TICK
else
if(istype(O, /mob))
for(var/mob/M in mob_list)
if (M.type == O.type)
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if (A.type == O.type)
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if (A.type == O.type)
A.vars[variable] = O.vars[variable]
CHECK_TICK
if("file")
var/new_value = input("Pick file:","File",O.vars[variable]) as null|file
if(new_value == null) return
O.vars[variable] = new_value
if(method)
if(istype(O, /mob))
for(var/mob/M in mob_list)
if ( istype(M , O.type) )
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O.type, /obj))
for(var/obj/A in world)
if ( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O.type, /turf))
for(var/turf/A in world)
if ( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
CHECK_TICK
else
if(istype(O, /mob))
for(var/mob/M in mob_list)
if (M.type == O.type)
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if (A.type == O.type)
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if (A.type == O.type)
A.vars[variable] = O.vars[variable]
CHECK_TICK
if("icon")
var/new_value = input("Pick icon:","Icon",O.vars[variable]) as null|icon
if(new_value == null) return
O.vars[variable] = new_value
if(method)
if(istype(O, /mob))
for(var/mob/M in mob_list)
if ( istype(M , O.type) )
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if ( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if ( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
CHECK_TICK
else
if(istype(O, /mob))
for(var/mob/M in mob_list)
if (M.type == O.type)
M.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if (A.type == O.type)
A.vars[variable] = O.vars[variable]
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if (A.type == O.type)
A.vars[variable] = O.vars[variable]
CHECK_TICK
if(method)
if(istype(O,/mob))
for(var/mob/M in mob_list)
if(istype(M,O.type))
M.on_varedit(variable)
CHECK_TICK
else if(istype(O,/obj))
for(var/obj/A in world)
if(istype(A,O.type))
A.on_varedit(variable)
CHECK_TICK
else if(istype(O,/turf))
for(var/turf/A in block(locate(1,1,1),locate(world.maxx,world.maxy,world.maxz)))
if(istype(A,O.type))
A.on_varedit(variable)
CHECK_TICK
else
if(istype(O, /mob))
for(var/mob/M in mob_list)
if(M.type == O.type)
M.on_varedit(variable)
CHECK_TICK
else if(istype(O, /obj))
for(var/obj/A in world)
if(A.type == O.type)
A.on_varedit(variable)
CHECK_TICK
else if(istype(O, /turf))
for(var/turf/A in world)
if(A.type == O.type)
A.on_varedit(variable)
CHECK_TICK
world.log << "### MassVarEdit by [src]: [O.type] [variable]=[html_encode("[O.vars[variable]]")]"
log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]]")
message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]]")
+688
View File
@@ -0,0 +1,688 @@
var/list/forbidden_varedit_object_types = list(
/datum/admins, //Admins editing their own admin-power object? Yup, sounds like a good idea.
/obj/machinery/blackbox_recorder, //Prevents people messing with feedback gathering
/datum/feedback_variable, //Prevents people messing with feedback gathering
/datum/admin_rank //editing my own rank? it's more likely than you think
)
var/list/VVlocked = list("vars", "var_edited", "client", "virus", "viruses", "cuffed", "last_eaten", "unlock_content", "step_x", "step_y", "force_ending")
var/list/VVicon_edit_lock = list("icon", "icon_state", "overlays", "underlays", "resize")
var/list/VVckey_edit = list("key", "ckey")
/*
/client/proc/cmd_modify_object_variables(obj/O as obj|mob|turf|area in world)
set category = "Debug"
set name = "Edit Variables"
set desc="(target) Edit a target item's variables"
src.modify_variables(O)
feedback_add_details("admin_verb","EDITV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
*/
/client/proc/cmd_modify_ticker_variables()
set category = "Debug"
set name = "Edit Ticker Variables"
if (ticker == null)
src << "Game hasn't started yet."
else
src.modify_variables(ticker)
feedback_add_details("admin_verb","ETV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/mod_list_add_ass(atom/O) //haha
var/class = "text"
if(src.holder && src.holder.marked_datum)
class = input("What kind of variable?","Variable Type") as null|anything in list("text",
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "new atom", "new datum", "marked datum ([holder.marked_datum.type])")
else
class = input("What kind of variable?","Variable Type") as null|anything in list("text",
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "new atom", "new datum")
if(!class)
return
if(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])")
class = "marked datum"
var/var_value = null
switch(class)
if("text")
var_value = input("Enter new text:","Text") as null|message
if("num")
var_value = input("Enter new number:","Num") as null|num
if("type")
var_value = input("Enter type:","Type") as null|anything in typesof(/obj,/mob,/area,/turf)
if("reference")
var_value = input("Select reference:","Reference") as null|mob|obj|turf|area in world
if("mob reference")
var_value = input("Select reference:","Reference") as null|mob in world
if("file")
var_value = input("Pick file:","File") as null|file
if("icon")
var_value = input("Pick icon:","Icon") as null|icon
if("marked datum")
var_value = holder.marked_datum
if("new atom")
var/type = input("Enter type:","Type") as null|anything in typesof(/obj,/mob,/area,/turf)
var_value = new type()
if("new datum")
var/type = input("Enter type:","Type") as null|anything in (typesof(/datum)-typesof(/obj,/mob,/area,/turf))
var_value = new type()
if(!var_value) return
if(istext(var_value))
if(findtext(var_value,"\["))
var/process_vars = alert(usr,"\[] detected in string, process as variables?","Process Variables?","Yes","No")
if(process_vars == "Yes")
var/list/varsvars = string2listofvars(var_value, O)
for(var/V in varsvars)
var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]")
return var_value
/client/proc/mod_list_add(list/L, atom/O, original_name, objectvar)
var/class = "text"
if(src.holder && src.holder.marked_datum)
class = input("What kind of variable?","Variable Type") as null|anything in list("text",
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "new atom", "new datum","marked datum ([holder.marked_datum.type])")
else
class = input("What kind of variable?","Variable Type") as null|anything in list("text",
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "new atom", "new datum")
if(!class)
return
if(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])")
class = "marked datum"
var/var_value = null
switch(class)
if("text")
var_value = input("Enter new text:","Text") as message
if("num")
var_value = input("Enter new number:","Num") as num
if("type")
var_value = input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf)
if("reference")
var_value = input("Select reference:","Reference") as mob|obj|turf|area in world
if("mob reference")
var_value = input("Select reference:","Reference") as mob in world
if("file")
var_value = input("Pick file:","File") as file
if("icon")
var_value = input("Pick icon:","Icon") as icon
if("marked datum")
var_value = holder.marked_datum
if("new atom")
var/type = input("Enter type:","Type") as null|anything in typesof(/obj,/mob,/area,/turf)
var_value = new type()
if("new datum")
var/type = input("Enter type:","Type") as null|anything in (typesof(/datum)-typesof(/obj,/mob,/area,/turf))
var_value = new type()
if(!var_value) return
if(istext(var_value))
if(findtext(var_value,"\["))
var/process_vars = alert(usr,"\[] detected in string, process as variables?","Process Variables?","Yes","No")
if(process_vars == "Yes")
var/list/varsvars = string2listofvars(var_value, O)
for(var/V in varsvars)
var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]")
L += var_value
switch(alert("Would you like to associate a var with the list entry?",,"Yes","No"))
if("Yes")
L[var_value] = mod_list_add_ass(O) //haha
O.on_varedit(objectvar)
world.log << "### ListVarEdit by [src]: [O.type] [objectvar]: ADDED=[var_value]"
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
/client/proc/mod_list(list/L, atom/O, original_name, objectvar)
if(!check_rights(R_VAREDIT))
return
if(!istype(L,/list))
src << "Not a List."
if(L.len > 1000)
var/confirm = alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", "Continue", "Abort")
if(confirm != "Continue")
return
var/assoc = 0
if(L.len > 0)
var/a = L[1]
if(istext(a) && L[a] != null)
assoc = 1 //This is pretty weak test but i can't think of anything else
usr << "List appears to be associative."
var/list/names = null
if(!assoc)
names = sortList(L)
var/variable
var/assoc_key
if(assoc)
variable = input("Which var?","Var") as null|anything in L + "(ADD VAR)"
else
variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)"
if(variable == "(ADD VAR)")
mod_list_add(L, O, original_name, objectvar)
return
if(assoc)
assoc_key = variable
variable = L[assoc_key]
if(!assoc && !variable || assoc && !assoc_key)
return
var/default
var/dir
if(variable in VVlocked)
if(!check_rights(R_DEBUG))
return
if(variable in VVckey_edit)
if(!check_rights(R_SPAWN|R_DEBUG))
return
if(variable in VVicon_edit_lock)
if(!check_rights(R_FUN|R_DEBUG))
return
if(isnull(variable))
usr << "Unable to determine variable type."
else if(isnum(variable))
usr << "Variable appears to be <b>NUM</b>."
default = "num"
setDir(1)
else if(istext(variable))
usr << "Variable appears to be <b>TEXT</b>."
default = "text"
else if(isloc(variable))
usr << "Variable appears to be <b>REFERENCE</b>."
default = "reference"
else if(isicon(variable))
usr << "Variable appears to be <b>ICON</b>."
variable = "\icon[variable]"
default = "icon"
else if(istype(variable,/atom) || istype(variable,/datum))
usr << "Variable appears to be <b>TYPE</b>."
default = "type"
else if(istype(variable,/list))
usr << "Variable appears to be <b>LIST</b>."
default = "list"
else if(istype(variable,/client))
usr << "Variable appears to be <b>CLIENT</b>."
default = "cancel"
else
usr << "Variable appears to be <b>FILE</b>."
default = "file"
usr << "Variable contains: [variable]"
if(dir)
switch(variable)
if(1)
setDir("NORTH")
if(2)
setDir("SOUTH")
if(4)
setDir("EAST")
if(8)
setDir("WEST")
if(5)
setDir("NORTHEAST")
if(6)
setDir("SOUTHEAST")
if(9)
setDir("NORTHWEST")
if(10)
setDir("SOUTHWEST")
else
setDir(null)
if(dir)
usr << "If a direction, direction is: [dir]"
var/class = "text"
if(src.holder && src.holder.marked_datum)
class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "new atom", "new datum","marked datum ([holder.marked_datum.type])", "DELETE FROM LIST")
else
class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "new atom", "new datum", "DELETE FROM LIST")
if(!class)
return
if(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])")
class = "marked datum"
var/original_var
if(assoc)
original_var = L[assoc_key]
else
original_var = L[L.Find(variable)]
var/new_var
switch(class) //Spits a runtime error if you try to modify an entry in the contents list. Dunno how to fix it, yet.
if("list")
mod_list(variable, O, original_name, objectvar)
if("restore to default")
new_var = initial(variable)
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("edit referenced object")
modify_variables(variable)
if("DELETE FROM LIST")
world.log << "### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[variable]")]"
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[variable]")
message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[variable]")
L -= variable
O.on_varedit(objectvar)
return
if("text")
new_var = input("Enter new text:","Text") as message
if(findtext(new_var,"\["))
var/process_vars = alert(usr,"\[] detected in string, process as variables?","Process Variables?","Yes","No")
if(process_vars == "Yes")
var/list/varsvars = string2listofvars(new_var, O)
for(var/V in varsvars)
new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]")
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("num")
new_var = input("Enter new number:","Num") as num
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("type")
new_var = input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf)
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("reference")
new_var = input("Select reference:","Reference") as mob|obj|turf|area in world
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("mob reference")
new_var = input("Select reference:","Reference") as mob in world
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("file")
new_var = input("Pick file:","File") as file
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("icon")
new_var = input("Pick icon:","Icon") as icon
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("marked datum")
new_var = holder.marked_datum
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("new atom")
var/type = input("Enter type:","Type") as null|anything in typesof(/obj,/mob,/area,/turf)
new_var = new type()
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
if("new datum")
var/type = input("Enter type:","Type") as null|anything in (typesof(/datum)-typesof(/obj,/mob,/area,/turf))
new_var = new type()
if(assoc)
L[assoc_key] = new_var
else
L[L.Find(variable)] = new_var
O.on_varedit(objectvar)
world.log << "### ListVarEdit by [src]: [O.type] [objectvar]: [original_var]=[new_var]"
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]")
message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]")
/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0)
if(!check_rights(R_VAREDIT))
return
if(is_type_in_list(O, forbidden_varedit_object_types))
usr << "<span class='danger'>It is forbidden to edit this object's variables.</span>"
return
if(istype(O, /client) && (param_var_name == "ckey" || param_var_name == "key"))
usr << "<span class='danger'>You cannot edit ckeys on client objects.</span>"
return
var/class
var/variable
var/var_value
if(param_var_name)
if(!param_var_name in O.vars)
src << "A variable with this name ([param_var_name]) doesn't exist in this atom ([O])"
return
if(param_var_name in VVlocked)
if(!check_rights(R_DEBUG))
return
if(param_var_name in VVckey_edit)
if(!check_rights(R_SPAWN|R_DEBUG))
return
if(param_var_name in VVicon_edit_lock)
if(!check_rights(R_FUN|R_DEBUG))
return
variable = param_var_name
var_value = O.vars[variable]
if(autodetect_class)
if(isnull(var_value))
usr << "Unable to determine variable type."
class = null
autodetect_class = null
else if(isnum(var_value))
usr << "Variable appears to be <b>NUM</b>."
class = "num"
setDir(1)
else if(istext(var_value))
usr << "Variable appears to be <b>TEXT</b>."
class = "text"
else if(isloc(var_value))
usr << "Variable appears to be <b>REFERENCE</b>."
class = "reference"
else if(isicon(var_value))
usr << "Variable appears to be <b>ICON</b>."
var_value = "\icon[var_value]"
class = "icon"
else if(istype(var_value,/atom) || istype(var_value,/datum))
usr << "Variable appears to be <b>TYPE</b>."
class = "type"
else if(istype(var_value,/list))
usr << "Variable appears to be <b>LIST</b>."
class = "list"
else if(istype(var_value,/client))
usr << "Variable appears to be <b>CLIENT</b>."
class = "cancel"
else
usr << "Variable appears to be <b>FILE</b>."
class = "file"
else
var/list/names = list()
for (var/V in O.vars)
names += V
names = sortList(names)
variable = input("Which var?","Var") as null|anything in names
if(!variable)
return
var_value = O.vars[variable]
if(variable in VVlocked)
if(!check_rights(R_DEBUG))
return
if(variable in VVckey_edit)
if(!check_rights(R_SPAWN|R_DEBUG))
return
if(variable in VVicon_edit_lock)
if(!check_rights(R_FUN|R_DEBUG))
return
if(!autodetect_class)
var/dir
var/default
if(isnull(var_value))
usr << "Unable to determine variable type."
else if(isnum(var_value))
usr << "Variable appears to be <b>NUM</b>."
default = "num"
setDir(1)
else if(istext(var_value))
usr << "Variable appears to be <b>TEXT</b>."
default = "text"
else if(isloc(var_value))
usr << "Variable appears to be <b>REFERENCE</b>."
default = "reference"
else if(isicon(var_value))
usr << "Variable appears to be <b>ICON</b>."
var_value = "\icon[var_value]"
default = "icon"
else if(istype(var_value,/atom) || istype(var_value,/datum))
usr << "Variable appears to be <b>TYPE</b>."
default = "type"
else if(istype(var_value,/list))
usr << "Variable appears to be <b>LIST</b>."
default = "list"
else if(istype(var_value,/client))
usr << "Variable appears to be <b>CLIENT</b>."
default = "cancel"
else
usr << "Variable appears to be <b>FILE</b>."
default = "file"
usr << "Variable contains: [var_value]"
if(dir)
switch(var_value)
if(1)
setDir("NORTH")
if(2)
setDir("SOUTH")
if(4)
setDir("EAST")
if(8)
setDir("WEST")
if(5)
setDir("NORTHEAST")
if(6)
setDir("SOUTHEAST")
if(9)
setDir("NORTHWEST")
if(10)
setDir("SOUTHWEST")
else
setDir(null)
if(dir)
usr << "If a direction, direction is: [dir]"
if(src.holder && src.holder.marked_datum)
class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "new atom", "new datum", "marked datum ([holder.marked_datum.type])")
else
class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "new atom", "new datum")
if(!class)
return
var/original_name
if (!istype(O, /atom))
original_name = "\ref[O] ([O])"
else
original_name = O:name
if(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])")
class = "marked datum"
switch(class)
if("list")
mod_list(O.vars[variable], O, original_name, variable)
return
if("restore to default")
O.vars[variable] = initial(O.vars[variable])
if("edit referenced object")
return .(O.vars[variable])
if("text")
var/var_new = input("Enter new text:","Text",O.vars[variable]) as null|message
if(var_new==null) return
if(findtext(var_new,"\["))
var/process_vars = alert(usr,"\[] detected in string, process as variables?","Process Variables?","Yes","No")
if(process_vars == "Yes")
var/list/varsvars = string2listofvars(var_new, O)
for(var/V in varsvars)
var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]")
O.vars[variable] = var_new
if("num")
if(variable=="luminosity")
var/var_new = input("Enter new number:","Num",O.vars[variable]) as null|num
if(var_new == null) return
O.SetLuminosity(var_new)
else if(variable=="stat")
var/var_new = input("Enter new number:","Num",O.vars[variable]) as null|num
if(var_new == null) return
if((O.vars[variable] == 2) && (var_new < 2))//Bringing the dead back to life
dead_mob_list -= O
living_mob_list += O
if((O.vars[variable] < 2) && (var_new == 2))//Kill he
living_mob_list -= O
dead_mob_list += O
O.vars[variable] = var_new
else
var/var_new = input("Enter new number:","Num",O.vars[variable]) as null|num
if(var_new==null) return
O.vars[variable] = var_new
if("type")
var/target_path = input("Enter type:", "Type", O.vars[variable]) as null|text
if(!target_path)
return
var/var_new = text2path(target_path)
if(!ispath(var_new))
var_new = pick_closest_path(target_path)
if(!var_new)
return
O.vars[variable] = var_new
if("reference")
var/var_new = input("Select reference:","Reference",O.vars[variable]) as null|mob|obj|turf|area in world
if(var_new==null) return
O.vars[variable] = var_new
if("mob reference")
var/var_new = input("Select reference:","Reference",O.vars[variable]) as null|mob in world
if(var_new==null) return
O.vars[variable] = var_new
if("file")
var/var_new = input("Pick file:","File",O.vars[variable]) as null|file
if(var_new==null) return
O.vars[variable] = var_new
if("icon")
var/var_new = input("Pick icon:","Icon",O.vars[variable]) as null|icon
if(var_new==null) return
O.vars[variable] = var_new
if("marked datum")
O.vars[variable] = holder.marked_datum
if("new atom")
var/type = input("Enter type:","Type") as null|anything in typesof(/obj,/mob,/area,/turf)
var/var_new = new type()
if(var_new==null) return
O.vars[variable] = var_new
if("new datum")
var/type = input("Enter type:","Type") as null|anything in (typesof(/datum)-typesof(/obj,/mob,/area,/turf))
var/var_new = new type()
if(var_new==null) return
O.vars[variable] = var_new
O.on_varedit(variable)
world.log << "### VarEdit by [src]: [O.type] [variable]=[html_encode("[O.vars[variable]]")]"
log_admin("[key_name(src)] modified [original_name]'s [variable] to [O.vars[variable]]")
message_admins("[key_name_admin(src)] modified [original_name]'s [variable] to [O.vars[variable]]")
+556
View File
@@ -0,0 +1,556 @@
/client/proc/one_click_antag()
set name = "Create Antagonist"
set desc = "Auto-create an antagonist of your choice"
set category = "Admin"
if(holder)
holder.one_click_antag()
return
/datum/admins/proc/one_click_antag()
var/dat = {"
<a href='?src=\ref[src];makeAntag=1'>Make Traitors</a><br>
<a href='?src=\ref[src];makeAntag=2'>Make Changelings</a><br>
<a href='?src=\ref[src];makeAntag=3'>Make Revs</a><br>
<a href='?src=\ref[src];makeAntag=4'>Make Cult</a><br>
<a href='?src=\ref[src];makeAntag=15'>Make Clockwork Cult</a><br>
<a href='?src=\ref[src];makeAntag=11'>Make Blob</a><br>
<a href='?src=\ref[src];makeAntag=12'>Make Gangsters</a><br>
<a href='?src=\ref[src];makeAntag=6'>Make Wizard (Requires Ghosts)</a><br>
<a href='?src=\ref[src];makeAntag=7'>Make Nuke Team (Requires Ghosts)</a><br>
<a href='?src=\ref[src];makeAntag=13'>Make Centcom Response Team (Requires Ghosts)</a><br>
<a href='?src=\ref[src];makeAntag=14'>Make Abductor Team (Requires Ghosts)</a><br>
<a href='?src=\ref[src];makeAntag=15'>Make Revenant (Requires Ghost)</a><br>
"}
var/datum/browser/popup = new(usr, "oneclickantag", "Quick-Create Antagonist", 400, 400)
popup.set_content(dat)
popup.open()
/datum/admins/proc/makeTraitors()
var/datum/game_mode/traitor/temp = new
if(config.protect_roles_from_antagonist)
temp.restricted_jobs += temp.protected_jobs
if(config.protect_assistant_from_antagonist)
temp.restricted_jobs += "Assistant"
var/list/mob/living/carbon/human/candidates = list()
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in player_list)
if(ROLE_TRAITOR in applicant.client.prefs.be_special)
if(!applicant.stat)
if(applicant.mind)
if (!applicant.mind.special_role)
if(!jobban_isbanned(applicant, ROLE_TRAITOR) && !jobban_isbanned(applicant, "Syndicate"))
if(temp.age_check(applicant.client))
if(!(applicant.job in temp.restricted_jobs))
candidates += applicant
if(candidates.len)
var/numTraitors = min(candidates.len, 3)
for(var/i = 0, i<numTraitors, i++)
H = pick(candidates)
H.mind.make_Traitor()
candidates.Remove(H)
return 1
return 0
/datum/admins/proc/makeChanglings()
var/datum/game_mode/changeling/temp = new
if(config.protect_roles_from_antagonist)
temp.restricted_jobs += temp.protected_jobs
if(config.protect_assistant_from_antagonist)
temp.restricted_jobs += "Assistant"
var/list/mob/living/carbon/human/candidates = list()
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in player_list)
if(ROLE_CHANGELING in applicant.client.prefs.be_special)
var/turf/T = get_turf(applicant)
if(applicant.stat == CONSCIOUS && applicant.mind && !applicant.mind.special_role && T.z == ZLEVEL_STATION)
if(!jobban_isbanned(applicant, ROLE_CHANGELING) && !jobban_isbanned(applicant, "Syndicate"))
if(temp.age_check(applicant.client))
if(!(applicant.job in temp.restricted_jobs))
candidates += applicant
if(candidates.len)
var/numChanglings = min(candidates.len, 3)
for(var/i = 0, i<numChanglings, i++)
H = pick(candidates)
H.mind.make_Changling()
candidates.Remove(H)
return 1
return 0
/datum/admins/proc/makeRevs()
var/datum/game_mode/revolution/temp = new
if(config.protect_roles_from_antagonist)
temp.restricted_jobs += temp.protected_jobs
if(config.protect_assistant_from_antagonist)
temp.restricted_jobs += "Assistant"
var/list/mob/living/carbon/human/candidates = list()
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in player_list)
if(ROLE_REV in applicant.client.prefs.be_special)
var/turf/T = get_turf(applicant)
if(applicant.stat == CONSCIOUS && applicant.mind && !applicant.mind.special_role && T.z == ZLEVEL_STATION)
if(!jobban_isbanned(applicant, ROLE_REV) && !jobban_isbanned(applicant, "Syndicate"))
if(temp.age_check(applicant.client))
if(!(applicant.job in temp.restricted_jobs))
candidates += applicant
if(candidates.len)
var/numRevs = min(candidates.len, 3)
for(var/i = 0, i<numRevs, i++)
H = pick(candidates)
H.mind.make_Rev()
candidates.Remove(H)
return 1
return 0
/datum/admins/proc/makeWizard()
var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard", null)
var/mob/dead/observer/selected = popleft(candidates)
var/mob/living/carbon/human/new_character = makeBody(selected)
new_character.mind.make_Wizard()
return TRUE
/datum/admins/proc/makeCult()
var/datum/game_mode/cult/temp = new
if(config.protect_roles_from_antagonist)
temp.restricted_jobs += temp.protected_jobs
if(config.protect_assistant_from_antagonist)
temp.restricted_jobs += "Assistant"
var/list/mob/living/carbon/human/candidates = list()
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in player_list)
if(ROLE_CULTIST in applicant.client.prefs.be_special)
var/turf/T = get_turf(applicant)
if(applicant.stat == CONSCIOUS && applicant.mind && !applicant.mind.special_role && T.z == ZLEVEL_STATION)
if(!jobban_isbanned(applicant, ROLE_CULTIST) && !jobban_isbanned(applicant, "Syndicate"))
if(temp.age_check(applicant.client))
if(!(applicant.job in temp.restricted_jobs))
candidates += applicant
if(candidates.len)
var/numCultists = min(candidates.len, 4)
for(var/i = 0, i<numCultists, i++)
H = pick(candidates)
H.mind.make_Cultist()
candidates.Remove(H)
return 1
return 0
/datum/admins/proc/makeClockCult()
var/datum/game_mode/clockwork_cult/temp = new
if(config.protect_roles_from_antagonist)
temp.restricted_jobs += temp.protected_jobs
if(config.protect_assistant_from_antagonist)
temp.restricted_jobs += "Assistant"
var/list/mob/living/carbon/human/candidates = list()
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in player_list)
if(ROLE_SERVANT_OF_RATVAR in applicant.client.prefs.be_special)
var/turf/T = get_turf(applicant)
if(applicant.stat == CONSCIOUS && applicant.mind && !applicant.mind.special_role && T.z == ZLEVEL_STATION)
if(!jobban_isbanned(applicant, ROLE_SERVANT_OF_RATVAR) && !jobban_isbanned(applicant, "Syndicate"))
if(temp.age_check(applicant.client))
if(!(applicant.job in temp.restricted_jobs))
candidates += applicant
if(candidates.len)
var/numCultists = min(candidates.len, 4)
for(var/i = 0, i<numCultists, i++)
H = pick(candidates)
H << "<span class='heavy_brass'>The world before you suddenly glows a brilliant yellow. You hear the whooshing steam and clanking cogs of a billion billion machines, and all at once \
you see the truth. Ratvar, the Clockwork Justiciar, lies derelict and forgotten in an unseen realm, and he has selected you as one of his harbringers. You are now a servant of \
Ratvar, and you will bring him back.</span>"
add_servant_of_ratvar(H, TRUE)
ticker.mode.equip_servant(H)
candidates.Remove(H)
return 1
return 0
/datum/admins/proc/makeNukeTeam()
var/datum/game_mode/nuclear/temp = new
var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for a nuke team being sent in?", "operative", temp)
var/list/mob/dead/observer/chosen = list()
var/mob/dead/observer/theghost = null
if(candidates.len)
var/numagents = 5
var/agentcount = 0
for(var/i = 0, i<numagents,i++)
shuffle(candidates) //More shuffles means more randoms
for(var/mob/j in candidates)
if(!j || !j.client)
candidates.Remove(j)
continue
theghost = j
candidates.Remove(theghost)
chosen += theghost
agentcount++
break
//Making sure we have atleast 3 Nuke agents, because less than that is kinda bad
if(agentcount < 3)
return 0
var/nuke_code = "[rand(10000, 99999)]"
var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in nuke_list
if(nuke)
nuke.r_code = nuke_code
//Let's find the spawn locations
var/list/turf/synd_spawn = list()
for(var/obj/effect/landmark/A in landmarks_list)
if(A.name == "Syndicate-Spawn")
synd_spawn += get_turf(A)
continue
var/leader_chosen
var/spawnpos = 1 //Decides where they'll spawn. 1=leader.
for(var/mob/c in chosen)
if(spawnpos > synd_spawn.len)
spawnpos = 2 //Ran out of spawns. Let's loop back to the first non-leader position
var/mob/living/carbon/human/new_character=makeBody(c)
if(!leader_chosen)
leader_chosen = 1
new_character.mind.make_Nuke(synd_spawn[spawnpos],nuke_code,1)
else
new_character.mind.make_Nuke(synd_spawn[spawnpos],nuke_code)
spawnpos++
return 1
else
return 0
/datum/admins/proc/makeAliens()
var/datum/round_event/ghost_role/alien_infestation/E = new(FALSE)
E.spawncount = 3
// TODO The fact we have to do this rather than just have events start
// when we ask them to, is bad.
E.processing = TRUE
return TRUE
/datum/admins/proc/makeSpaceNinja()
new /datum/round_event/ghost_role/ninja()
return 1
// DEATH SQUADS
/datum/admins/proc/makeDeathsquad()
var/mission = input("Assign a mission to the deathsquad", "Assign Mission", "Leave no witnesses.")
var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for an elite Nanotrasen Strike Team?", "deathsquad", null)
var/squadSpawned = 0
if(candidates.len >= 2) //Minimum 2 to be considered a squad
//Pick the lucky players
var/numagents = min(5,candidates.len) //How many commandos to spawn
var/list/spawnpoints = emergencyresponseteamspawn
while(numagents && candidates.len)
if (numagents > spawnpoints.len)
numagents--
continue // This guy's unlucky, not enough spawn points, we skip him.
var/spawnloc = spawnpoints[numagents]
var/mob/dead/observer/chosen_candidate = pick(candidates)
candidates -= chosen_candidate
if(!chosen_candidate.key)
continue
//Spawn and equip the commando
var/mob/living/carbon/human/Commando = new(spawnloc)
chosen_candidate.client.prefs.copy_to(Commando)
if(numagents == 1) //If Squad Leader
Commando.real_name = "Officer [pick(commando_names)]"
Commando.equipOutfit(/datum/outfit/death_commando/officer)
else
Commando.real_name = "Trooper [pick(commando_names)]"
Commando.equipOutfit(/datum/outfit/death_commando)
Commando.dna.update_dna_identity()
Commando.key = chosen_candidate.key
Commando.mind.assigned_role = "Death Commando"
for(var/obj/machinery/door/poddoor/ert/door in airlocks)
spawn(0)
door.open()
//Assign antag status and the mission
ticker.mode.traitors += Commando.mind
Commando.mind.special_role = "deathsquad"
var/datum/objective/missionobj = new
missionobj.owner = Commando.mind
missionobj.explanation_text = mission
missionobj.completed = 1
Commando.mind.objectives += missionobj
//Greet the commando
Commando << "<B><font size=3 color=red>You are the [numagents==1?"Deathsquad Officer":"Death Commando"].</font></B>"
var/missiondesc = "Your squad is being sent on a mission to [station_name()] by Nanotrasen's Security Division."
if(numagents == 1) //If Squad Leader
missiondesc += " Lead your squad to ensure the completion of the mission. Board the shuttle when your team is ready."
else
missiondesc += " Follow orders given to you by your squad leader."
missiondesc += "<BR><B>Your Mission</B>: [mission]"
Commando << missiondesc
if(config.enforce_human_authority)
Commando.set_species(/datum/species/human)
//Logging and cleanup
if(numagents == 1)
message_admins("The deathsquad has spawned with the mission: [mission].")
log_game("[key_name(Commando)] has been selected as a Death Commando")
numagents--
squadSpawned++
if (squadSpawned)
return 1
else
return 0
return
/datum/admins/proc/makeGangsters()
var/datum/game_mode/gang/temp = new
if(config.protect_roles_from_antagonist)
temp.restricted_jobs += temp.protected_jobs
if(config.protect_assistant_from_antagonist)
temp.restricted_jobs += "Assistant"
var/list/mob/living/carbon/human/candidates = list()
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in player_list)
if(ROLE_GANG in applicant.client.prefs.be_special)
var/turf/T = get_turf(applicant)
if(applicant.stat == CONSCIOUS && applicant.mind && !applicant.mind.special_role && T.z == ZLEVEL_STATION)
if(!jobban_isbanned(applicant, ROLE_GANG) && !jobban_isbanned(applicant, "Syndicate"))
if(temp.age_check(applicant.client))
if(!(applicant.job in temp.restricted_jobs))
candidates += applicant
if(candidates.len >= 2)
for(var/needs_assigned=2,needs_assigned>0,needs_assigned--)
H = pick(candidates)
if(gang_colors_pool.len)
var/datum/gang/newgang = new()
ticker.mode.gangs += newgang
H.mind.make_Gang(newgang)
candidates.Remove(H)
else if(needs_assigned == 2)
return 0
return 1
return 0
/datum/admins/proc/makeOfficial()
var/mission = input("Assign a task for the official", "Assign Task", "Conduct a routine preformance review of [station_name()] and its Captain.")
var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered to be a Centcom Official?", "deathsquad")
if(candidates.len)
var/mob/dead/observer/chosen_candidate = pick(candidates)
//Create the official
var/mob/living/carbon/human/newmob = new (pick(emergencyresponseteamspawn))
chosen_candidate.client.prefs.copy_to(newmob)
newmob.real_name = newmob.dna.species.random_name(newmob.gender,1)
newmob.dna.update_dna_identity()
newmob.key = chosen_candidate.key
newmob.mind.assigned_role = "Centcom Official"
newmob.equipOutfit(/datum/outfit/centcom_official)
//Assign antag status and the mission
ticker.mode.traitors += newmob.mind
newmob.mind.special_role = "official"
var/datum/objective/missionobj = new
missionobj.owner = newmob.mind
missionobj.explanation_text = mission
missionobj.completed = 1
newmob.mind.objectives += missionobj
if(config.enforce_human_authority)
newmob.set_species(/datum/species/human)
//Greet the official
newmob << "<B><font size=3 color=red>You are a Centcom Official.</font></B>"
newmob << "<BR>Central Command is sending you to [station_name()] with the task: [mission]"
//Logging and cleanup
message_admins("Centcom Official [key_name_admin(newmob)] has spawned with the task: [mission]")
log_game("[key_name(newmob)] has been selected as a Centcom Official")
return 1
return 0
// CENTCOM RESPONSE TEAM
/datum/admins/proc/makeEmergencyresponseteam()
var/alert = input("Which team should we send?", "Select Response Level") as null|anything in list("Green: Centcom Official", "Blue: Light ERT (No Armoury Access)", "Amber: Full ERT (Armoury Access)", "Red: Elite ERT (Armoury Access + Pulse Weapons)", "Delta: Deathsquad")
if(!alert)
return
switch(alert)
if("Delta: Deathsquad")
return makeDeathsquad()
if("Red: Elite ERT (Armoury Access + Pulse Weapons)")
alert = "Red"
if("Amber: Full ERT (Armoury Access)")
alert = "Amber"
if("Blue: Light ERT (No Armoury Access)")
alert = "Blue"
if("Green: Centcom Official")
return makeOfficial()
var/teamsize = min(7,input("Maximum size of team? (7 max)", "Select Team Size",4) as null|num)
var/mission = input("Assign a mission to the Emergency Response Team", "Assign Mission", "Assist the station.")
var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for a Code [alert] Nanotrasen Emergency Response Team?", "deathsquad", null)
var/teamSpawned = 0
if(candidates.len > 0)
//Pick the (un)lucky players
var/numagents = min(teamsize,candidates.len) //How many officers to spawn
var/redalert //If the ert gets super weapons
if (alert == "Red")
numagents = min(teamsize,candidates.len)
redalert = 1
var/list/spawnpoints = emergencyresponseteamspawn
while(numagents && candidates.len)
if (numagents > spawnpoints.len)
numagents--
continue // This guy's unlucky, not enough spawn points, we skip him.
var/spawnloc = spawnpoints[numagents]
var/mob/dead/observer/chosen_candidate = pick(candidates)
candidates -= chosen_candidate
if(!chosen_candidate.key)
continue
//Spawn and equip the officer
var/mob/living/carbon/human/ERTOperative = new(spawnloc)
var/list/lastname = last_names
chosen_candidate.client.prefs.copy_to(ERTOperative)
var/ertname = pick(lastname)
switch(numagents)
if(1)
ERTOperative.real_name = "Commander [ertname]"
ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/commander/alert : /datum/outfit/ert/commander)
if(2)
ERTOperative.real_name = "Security Officer [ertname]"
ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/security/alert : /datum/outfit/ert/security)
if(3)
ERTOperative.real_name = "Medical Officer [ertname]"
ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/medic/alert : /datum/outfit/ert/medic)
if(4)
ERTOperative.real_name = "Engineer [ertname]"
ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/engineer/alert : /datum/outfit/ert/engineer)
if(5)
ERTOperative.real_name = "Security Officer [ertname]"
ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/security/alert : /datum/outfit/ert/security)
if(6)
ERTOperative.real_name = "Medical Officer [ertname]"
ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/medic/alert : /datum/outfit/ert/medic)
if(7)
ERTOperative.real_name = "Engineer [ertname]"
ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/engineer/alert : /datum/outfit/ert/engineer)
ERTOperative.dna.update_dna_identity()
ERTOperative.key = chosen_candidate.key
ERTOperative.mind.assigned_role = "ERT"
//Open the Armory doors
if(alert != "Blue")
for(var/obj/machinery/door/poddoor/ert/door in airlocks)
spawn(0)
door.open()
//Assign antag status and the mission
ticker.mode.traitors += ERTOperative.mind
ERTOperative.mind.special_role = "ERT"
var/datum/objective/missionobj = new
missionobj.owner = ERTOperative.mind
missionobj.explanation_text = mission
missionobj.completed = 1
ERTOperative.mind.objectives += missionobj
//Greet the commando
ERTOperative << "<B><font size=3 color=red>You are [numagents==1?"the Emergency Response Team Commander":"an Emergency Response Officer"].</font></B>"
var/missiondesc = "Your squad is being sent on a Code [alert] mission to [station_name()] by Nanotrasen's Security Division."
if(numagents == 1) //If Squad Leader
missiondesc += " Lead your squad to ensure the completion of the mission. Avoid civilian casualites when possible. Board the shuttle when your team is ready."
else
missiondesc += " Follow orders given to you by your commander. Avoid civilian casualites when possible."
missiondesc += "<BR><B>Your Mission</B>: [mission]"
ERTOperative << missiondesc
if(config.enforce_human_authority)
ERTOperative.set_species(/datum/species/human)
//Logging and cleanup
if(numagents == 1)
message_admins("A Code [alert] emergency response team has spawned with the mission: [mission]")
log_game("[key_name(ERTOperative)] has been selected as an Emergency Response Officer")
numagents--
teamSpawned++
if (teamSpawned)
return 1
else
return 0
return
//Abductors
/datum/admins/proc/makeAbductorTeam()
new /datum/round_event/ghost_role/abductor
return 1
/datum/admins/proc/makeRevenant()
new /datum/round_event/ghost_role/revenant
return 1
+93
View File
@@ -0,0 +1,93 @@
/client/proc/only_one()
if(!ticker || !ticker.mode)
alert("The game hasn't started yet!")
return
for(var/mob/living/carbon/human/H in player_list)
if(H.stat == 2 || !(H.client)) continue
if(is_special_character(H)) continue
ticker.mode.traitors += H.mind
H.mind.special_role = "traitor"
var/datum/objective/steal/steal_objective = new
steal_objective.owner = H.mind
steal_objective.set_target(new /datum/objective_item/steal/nukedisc)
H.mind.objectives += steal_objective
var/datum/objective/hijack/hijack_objective = new
hijack_objective.owner = H.mind
H.mind.objectives += hijack_objective
H << "<B>You are the traitor.</B>"
var/obj_count = 1
for(var/datum/objective/OBJ in H.mind.objectives)
H << "<B>Objective #[obj_count]</B>: [OBJ.explanation_text]"
obj_count++
for (var/obj/item/I in H)
if (istype(I, /obj/item/weapon/implant))
continue
qdel(I)
H.equip_to_slot_or_del(new /obj/item/clothing/under/kilt(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/device/radio/headset/heads/captain(H), slot_ears)
H.equip_to_slot_or_del(new /obj/item/clothing/head/beret(H), slot_head)
H.equip_to_slot_or_del(new /obj/item/weapon/claymore(H), slot_l_hand)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/weapon/pinpointer(H.loc), slot_l_store)
var/obj/item/weapon/card/id/W = new(H)
W.icon_state = "centcom"
W.access = get_all_accesses()
W.access += get_all_centcom_access()
W.assignment = "Highlander"
W.registered_name = H.real_name
W.update_label(H.real_name)
H.equip_to_slot_or_del(W, slot_wear_id)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] used THERE CAN BE ONLY ONE!</span>")
log_admin("[key_name(usr)] used there can be only one.")
/proc/only_me()
if(!ticker || !ticker.mode)
alert("The game hasn't started yet!")
return
for(var/mob/living/carbon/human/H in player_list)
if(H.stat == 2 || !(H.client)) continue
if(is_special_character(H)) continue
ticker.mode.traitors += H.mind
H.mind.special_role = "[H.real_name] Prime"
var/datum/objective/hijackclone/hijack_objective = new /datum/objective/hijackclone
hijack_objective.owner = H.mind
H.mind.objectives += hijack_objective
H << "<B>You are the multiverse summoner. Activate your blade to summon copies of yourself from another universe to fight by your side.</B>"
var/obj_count = 1
for(var/datum/objective/OBJ in H.mind.objectives)
H << "<B>Objective #[obj_count]</B>: [OBJ.explanation_text]"
obj_count++
var/obj/item/slot_item_ID = H.get_item_by_slot(slot_wear_id)
qdel(slot_item_ID)
var/obj/item/slot_item_hand = H.get_item_by_slot(slot_r_hand)
H.unEquip(slot_item_hand)
var /obj/item/weapon/multisword/multi = new(H)
H.equip_to_slot_or_del(multi, slot_r_hand)
var/obj/item/weapon/card/id/W = new(H)
W.icon_state = "centcom"
W.access = get_all_accesses()
W.access += get_all_centcom_access()
W.assignment = "Multiverse Summoner"
W.registered_name = H.real_name
W.update_label(H.real_name)
H.equip_to_slot_or_del(W, slot_wear_id)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] used THERE CAN BE ONLY ME!</span>")
log_admin("[key_name(usr)] used there can be only me.")
+15
View File
@@ -0,0 +1,15 @@
/client/proc/panicbunker()
set category = "Server"
set name = "Toggle Panic Bunker"
if (!config.sql_enabled)
usr << "<span class='adminnotice'>The Database is not enabled!</span>"
return
config.panic_bunker = (!config.panic_bunker)
log_admin("[key_name(usr)] has toggled the Panic Bunker, it is now [(config.panic_bunker?"on":"off")]")
message_admins("[key_name_admin(usr)] has toggled the Panic Bunker, it is now [(config.panic_bunker?"enabled":"disabled")].")
if (config.panic_bunker && (!dbcon || !dbcon.IsConnected()))
message_admins("The Database is not connected! Panic bunker will not work until the connection is reestablished.")
feedback_add_details("admin_verb","PANIC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+73
View File
@@ -0,0 +1,73 @@
#define SOUND_CHANNEL_ADMIN 777
var/sound/admin_sound
/client/proc/play_sound(S as sound)
set category = "Fun"
set name = "Play Global Sound"
if(!check_rights(R_SOUNDS))
return
log_admin("[key_name(src)] played sound [S]")
message_admins("[key_name_admin(src)] played sound [S]")
var/freq = 1
if(SSevent.holidays && SSevent.holidays[APRIL_FOOLS])
freq = pick(0.5, 0.7, 0.8, 0.85, 0.9, 0.95, 1.1, 1.2, 1.4, 1.6, 2.0, 2.5)
src << "You feel the Honkmother messing with your song..."
var/sound/admin_sound = new()
admin_sound.file = S
admin_sound.priority = 250
admin_sound.channel = SOUND_CHANNEL_ADMIN
admin_sound.frequency = freq
admin_sound.wait = 1
admin_sound.repeat = 0
admin_sound.status = SOUND_STREAM
for(var/mob/M in player_list)
if(M.client.prefs.toggles & SOUND_MIDI)
M << admin_sound
feedback_add_details("admin_verb","PGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/play_local_sound(S as sound)
set category = "Fun"
set name = "Play Local Sound"
if(!check_rights(R_SOUNDS))
return
log_admin("[key_name(src)] played a local sound [S]")
message_admins("[key_name_admin(src)] played a local sound [S]")
playsound(get_turf(src.mob), S, 50, 0, 0)
feedback_add_details("admin_verb","PLS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/set_round_end_sound(S as sound)
set category = "Fun"
set name = "Set Round End Sound"
if(!check_rights(R_SOUNDS))
return
if(ticker)
ticker.round_end_sound = fcopy_rsc(S)
else
return
log_admin("[key_name(src)] set the round end sound to [S]")
message_admins("[key_name_admin(src)] set the round end sound to [S]")
feedback_add_details("admin_verb","SRES") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/stop_sounds()
set category = "Debug"
set name = "Stop All Playing Sounds"
if(!src.holder)
return
log_admin("[key_name(src)] stopped all currently playing sounds.")
message_admins("[key_name_admin(src)] stopped all currently playing sounds.")
for(var/mob/M in player_list)
if(M.client)
M << sound(null)
feedback_add_details("admin_verb","SS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
#undef SOUND_CHANNEL_ADMIN
+53
View File
@@ -0,0 +1,53 @@
/proc/possess(obj/O in world)
set name = "Possess Obj"
set category = "Object"
if(istype(O,/obj/singularity))
if(config.forbid_singulo_possession)
usr << "It is forbidden to possess singularities."
return
var/turf/T = get_turf(O)
if(T)
log_admin("[key_name(usr)] has possessed [O] ([O.type]) at ([T.x], [T.y], [T.z])")
message_admins("[key_name(usr)] has possessed [O] ([O.type]) at ([T.x], [T.y], [T.z])")
else
log_admin("[key_name(usr)] has possessed [O] ([O.type]) at an unknown location")
message_admins("[key_name(usr)] has possessed [O] ([O.type]) at an unknown location")
if(!usr.control_object) //If you're not already possessing something...
usr.name_archive = usr.real_name
usr.loc = O
usr.real_name = O.name
usr.name = O.name
usr.client.eye = O
usr.control_object = O
feedback_add_details("admin_verb","PO") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/proc/release(obj/O in world)
set name = "Release Obj"
set category = "Object"
//usr.loc = get_turf(usr)
if(usr.control_object && usr.name_archive) //if you have a name archived and if you are actually relassing an object
usr.real_name = usr.name_archive
usr.name = usr.real_name
if(ishuman(usr))
var/mob/living/carbon/human/H = usr
H.name = H.get_visible_name()
// usr.regenerate_icons() //So the name is updated properly
usr.loc = O.loc
usr.client.eye = usr
usr.control_object = null
feedback_add_details("admin_verb","RO") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/proc/givetestverbs(mob/M in mob_list)
set desc = "Give this guy possess/release verbs"
set category = "Debug"
set name = "Give Possessing Verbs"
M.verbs += /proc/possess
M.verbs += /proc/release
feedback_add_details("admin_verb","GPV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+61
View File
@@ -0,0 +1,61 @@
/mob/verb/pray(msg as text)
set category = "IC"
set name = "Pray"
if(say_disabled) //This is here to try to identify lag problems
usr << "<span class='danger'>Speech is currently admin-disabled.</span>"
return
msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
if(!msg)
return
log_prayer("[src.key]/([src.name]): [msg]")
if(usr.client)
if(usr.client.prefs.muted & MUTE_PRAY)
usr << "<span class='danger'>You cannot pray (muted).</span>"
return
if(src.client.handle_spam_prevention(msg,MUTE_PRAY))
return
var/image/cross = image('icons/obj/storage.dmi',"bible")
if(usr.job == "Chaplain")
cross = image('icons/obj/storage.dmi',"kingyellow")
msg = "<span class='adminnotice'>\icon[cross] <b><font color=blue>CHAPLAIN PRAYER: </font>[key_name_admin(src)] (<A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>) (<A HREF='?_src_=holder;adminplayeropts=\ref[src]'>PP</A>) (<A HREF='?_src_=vars;Vars=\ref[src]'>VV</A>) (<A HREF='?_src_=holder;subtlemessage=\ref[src]'>SM</A>) (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[src]'>FLW</A>) (<A HREF='?_src_=holder;traitor=\ref[src]'>TP</A>) (<A HREF='?_src_=holder;adminspawncookie=\ref[src]'>SC</a>):</b> [msg]</span>"
else if(iscultist(usr))
cross = image('icons/obj/storage.dmi',"tome")
msg = "<span class='adminnotice'>\icon[cross] <b><font color=red>CULTIST PRAYER: </font>[key_name_admin(src)] (<A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>) (<A HREF='?_src_=holder;adminplayeropts=\ref[src]'>PP</A>) (<A HREF='?_src_=vars;Vars=\ref[src]'>VV</A>) (<A HREF='?_src_=holder;subtlemessage=\ref[src]'>SM</A>) (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[src]'>FLW</A>) (<A HREF='?_src_=holder;traitor=\ref[src]'>TP</A>) (<A HREF='?_src_=holder;adminspawncookie=\ref[src]'>SC</a>):</b> [msg]</span>"
else
cross = image('icons/obj/storage.dmi',"bible")
msg = "<span class='adminnotice'>\icon[cross] <b><font color=purple>PRAYER: </font>[key_name_admin(src)] (<A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>) (<A HREF='?_src_=holder;adminplayeropts=\ref[src]'>PP</A>) (<A HREF='?_src_=vars;Vars=\ref[src]'>VV</A>) (<A HREF='?_src_=holder;subtlemessage=\ref[src]'>SM</A>) (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[src]'>FLW</A>) (<A HREF='?_src_=holder;traitor=\ref[src]'>TP</A>) (<A HREF='?_src_=holder;adminspawncookie=\ref[src]'>SC</a>):</b> [msg]</span>"
for(var/client/C in admins)
if(C.prefs.chat_toggles & CHAT_PRAYER)
C << msg
if(C.prefs.toggles & SOUND_PRAYERS)
if(usr.job == "Chaplain")
C << 'sound/effects/pray.ogg'
usr << "Your prayers have been received by the gods."
feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//log_admin("HELP: [key_name(src)]: [msg]")
/proc/Centcomm_announce(text , mob/Sender)
var/msg = copytext(sanitize(text), 1, MAX_MESSAGE_LEN)
msg = "<span class='adminnotice'><b><font color=orange>CENTCOM:</font>[key_name_admin(Sender)] (<A HREF='?_src_=holder;adminmoreinfo=\ref[Sender]'>?</A>) (<A HREF='?_src_=holder;adminplayeropts=\ref[Sender]'>PP</A>) (<A HREF='?_src_=vars;Vars=\ref[Sender]'>VV</A>) (<A HREF='?_src_=holder;subtlemessage=\ref[Sender]'>SM</A>) (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[Sender]'>FLW</A>) (<A HREF='?_src_=holder;traitor=\ref[Sender]'>TP</A>) (<A HREF='?_src_=holder;BlueSpaceArtillery=\ref[Sender]'>BSA</A>) (<A HREF='?_src_=holder;CentcommReply=\ref[Sender]'>RPLY</A>):</b> [msg]</span>"
admins << msg
for(var/obj/machinery/computer/communications/C in machines)
C.overrideCooldown()
/proc/Syndicate_announce(text , mob/Sender)
var/msg = copytext(sanitize(text), 1, MAX_MESSAGE_LEN)
msg = "<span class='adminnotice'><b><font color=crimson>SYNDICATE:</font>[key_name_admin(Sender)] (<A HREF='?_src_=holder;adminmoreinfo=\ref[Sender]'>?</A>) (<A HREF='?_src_=holder;adminplayeropts=\ref[Sender]'>PP</A>) (<A HREF='?_src_=vars;Vars=\ref[Sender]'>VV</A>) (<A HREF='?_src_=holder;subtlemessage=\ref[Sender]'>SM</A>) (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[Sender]'>FLW</A>) (<A HREF='?_src_=holder;traitor=\ref[Sender]'>TP</A>) (<A HREF='?_src_=holder;BlueSpaceArtillery=\ref[Sender]'>BSA</A>) (<A HREF='?_src_=holder;SyndicateReply=\ref[Sender]'>RPLY</A>):</b> [msg]</span>"
admins << msg
for(var/obj/machinery/computer/communications/C in machines)
C.overrideCooldown()
/proc/Nuke_request(text , mob/Sender)
var/msg = copytext(sanitize(text), 1, MAX_MESSAGE_LEN)
msg = "<span class='adminnotice'><b><font color=orange>NUKE CODE REQUEST:</font>[key_name_admin(Sender)] (<A HREF='?_src_=holder;adminmoreinfo=\ref[Sender]'>?</A>) (<A HREF='?_src_=holder;adminplayeropts=\ref[Sender]'>PP</A>) (<A HREF='?_src_=vars;Vars=\ref[Sender]'>VV</A>) (<A HREF='?_src_=holder;subtlemessage=\ref[Sender]'>SM</A>) (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[Sender]'>FLW</A>) (<A HREF='?_src_=holder;traitor=\ref[Sender]'>TP</A>) (<A HREF='?_src_=holder;BlueSpaceArtillery=\ref[Sender]'>BSA</A>) (<A HREF='?_src_=holder;CentcommReply=\ref[Sender]'>RPLY</A>):</b> [msg]</span>"
admins << msg
admins << "<span class='adminnotice'><b>At this current time, the nuke must have the code manually set via varedit.</b></span>"
for(var/obj/machinery/computer/communications/C in machines)
C.overrideCooldown()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
/client/proc/reestablish_db_connection()
set category = "Special Verbs"
set name = "Reestablish DB Connection"
if (!config.sql_enabled)
usr << "<span class='adminnotice'>The Database is not enabled!</span>"
return
if (dbcon && dbcon.IsConnected())
if (!check_rights(R_DEBUG,0))
alert("The database is already connected! (Only those with +debug can force a reconnection)", "The database is already connected!")
return
var/reconnect = alert("The database is already connected! If you *KNOW* that this is incorrect, you can force a reconnection", "The database is already connected!", "Force Reconnect", "Cancel")
if (reconnect != "Force Reconnect")
return
dbcon.Disconnect()
failed_db_connections = 0
log_admin("[key_name(usr)] has forced the database to disconnect")
message_admins("[key_name_admin(usr)] has <b>forced</b> the database to disconnect!")
feedback_add_details("admin_verb","FRDB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] is attempting to re-established the DB Connection")
message_admins("[key_name_admin(usr)] is attempting to re-established the DB Connection")
feedback_add_details("admin_verb","RDB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
failed_db_connections = 0
if (!establish_db_connection())
message_admins("Database connection failed: " + dbcon.ErrorMsg())
else
message_admins("Database connection re-established")
+20
View File
@@ -0,0 +1,20 @@
/client/proc/triple_ai()
set category = "Fun"
set name = "Create AI Triumvirate"
if(ticker.current_state > GAME_STATE_PREGAME)
usr << "This option is currently only usable during pregame. This may change at a later date."
return
var/datum/job/job = SSjob.GetJob("AI")
if(!job)
usr << "Unable to locate the AI job"
return
if(ticker.triai)
ticker.triai = 0
usr << "Only one AI will be spawned at round start."
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has toggled off triple AIs at round start.</span>")
else
ticker.triai = 1
usr << "There will be an AI Triumvirate at round start."
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has toggled on triple AIs at round start.</span>")
+117
View File
@@ -0,0 +1,117 @@
/client/proc/watchlist_add(target_ckey, browse = 0)
if(!target_ckey)
var/new_ckey = ckey(input(usr,"Who would you like to add to the watchlist?","Enter a ckey",null) as text)
if(!new_ckey)
return
new_ckey = sanitizeSQL(new_ckey)
var/DBQuery/query_watchfind = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[new_ckey]'")
if(!query_watchfind.Execute())
var/err = query_watchfind.ErrorMsg()
log_game("SQL ERROR obtaining ckey from player table. Error : \[[err]\]\n")
return
if(!query_watchfind.NextRow())
if(alert(usr, "[new_ckey] has not been seen before, are you sure you want to add them to the watchlist?", "Unknown ckey", "Yes", "No", "Cancel") != "Yes")
return
target_ckey = new_ckey
var/target_sql_ckey = sanitizeSQL(target_ckey)
if(check_watchlist(target_sql_ckey))
usr << "<span class='redtext'>[target_sql_ckey] is already on the watchlist.</span>"
return
var/reason = input(usr,"Please State Reason","Reason") as message
if(!reason)
return
reason = sanitizeSQL(reason)
var/timestamp = SQLtime()
var/adminckey = usr.ckey
if(!adminckey)
return
var/admin_sql_ckey = sanitizeSQL(adminckey)
var/DBQuery/query_watchadd = dbcon.NewQuery("INSERT INTO [format_table_name("watch")] (ckey, reason, adminckey, timestamp) VALUES ('[target_sql_ckey]', '[reason]', '[admin_sql_ckey]', '[timestamp]')")
if(!query_watchadd.Execute())
var/err = query_watchadd.ErrorMsg()
log_game("SQL ERROR during adding new watch entry. Error : \[[err]\]\n")
return
log_admin("[key_name(usr)] has added [target_ckey] to the watchlist - Reason: [reason]")
message_admins("[key_name_admin(usr)] has added [target_ckey] to the watchlist - Reason: [reason]", 1)
if(browse)
watchlist_show(target_sql_ckey)
/client/proc/watchlist_remove(target_ckey, browse = 0)
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/DBQuery/query_watchdel = dbcon.NewQuery("DELETE FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'")
if(!query_watchdel.Execute())
var/err = query_watchdel.ErrorMsg()
log_game("SQL ERROR during removing watch entry. Error : \[[err]\]\n")
return
log_admin("[key_name(usr)] has removed [target_ckey] from the watchlist")
message_admins("[key_name_admin(usr)] has removed [target_ckey] from the watchlist", 1)
if(browse)
watchlist_show()
/client/proc/watchlist_edit(target_ckey, browse = 0)
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/DBQuery/query_watchreason = dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'")
if(!query_watchreason.Execute())
var/err = query_watchreason.ErrorMsg()
log_game("SQL ERROR obtaining reason from watch table. Error : \[[err]\]\n")
return
if(query_watchreason.NextRow())
var/watch_reason = query_watchreason.item[1]
var/new_reason = input("Input new reason", "New Reason", "[watch_reason]") as message
new_reason = sanitizeSQL(new_reason)
if(!new_reason)
return
var/sql_ckey = sanitizeSQL(usr.ckey)
var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from<br>[watch_reason]<br>to<br>[new_reason]<hr>"
edit_text = sanitizeSQL(edit_text)
var/DBQuery/query_watchupdate = dbcon.NewQuery("UPDATE [format_table_name("watch")] SET reason = '[new_reason]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE ckey = '[target_sql_ckey]'")
if(!query_watchupdate.Execute())
var/err = query_watchupdate.ErrorMsg()
log_game("SQL ERROR editing watchlist reason. Error : \[[err]\]\n")
return
log_admin("[key_name(usr)] has edited [target_ckey]'s watchlist reason from [watch_reason] to [new_reason]")
message_admins("[key_name_admin(usr)] has edited [target_ckey]'s watchlist reason from<br>[watch_reason]<br>to<br>[new_reason]")
if(browse)
watchlist_show(target_sql_ckey)
/client/proc/watchlist_show(search)
var/output
output += "<form method='GET' name='search' action='?'>\
<input type='hidden' name='_src_' value='holder'>\
<input type='text' name='watchsearch' value='[search]'>\
<input type='submit' value='Search'></form>"
output += "<a href='?_src_=holder;watchshow=1'>\[Clear Search\]</a> <a href='?_src_=holder;watchaddbrowse=1'>\[Add Ckey\]</a>"
output += "<hr style='background:#000000; border:0; height:3px'>"
if(search)
search = "^[search]"
else
search = "^."
search = sanitizeSQL(search)
var/DBQuery/query_watchlist = dbcon.NewQuery("SELECT ckey, reason, adminckey, timestamp, last_editor FROM [format_table_name("watch")] WHERE ckey REGEXP '[search]' ORDER BY ckey")
if(!query_watchlist.Execute())
var/err = query_watchlist.ErrorMsg()
log_game("SQL ERROR obtaining ckey, reason, adminckey, timestamp, last_editor from watch table. Error : \[[err]\]\n")
return
while(query_watchlist.NextRow())
var/ckey = query_watchlist.item[1]
var/reason = query_watchlist.item[2]
var/adminckey = query_watchlist.item[3]
var/timestamp = query_watchlist.item[4]
var/last_editor = query_watchlist.item[5]
output += "<b>[ckey]</b> | Added by <b>[adminckey]</b> on <b>[timestamp]</b> <a href='?_src_=holder;watchremovebrowse=[ckey]'>\[Remove\]</a> <a href='?_src_=holder;watcheditbrowse=[ckey]'>\[Edit Reason\]</a>"
if(last_editor)
output += " <font size='2'>Last edit by [last_editor] <a href='?_src_=holder;watcheditlog=[ckey]'>(Click here to see edit log)</a></font>"
output += "<br>[reason]<hr style='background:#000000; border:0; height:1px'>"
usr << browse(output, "window=watchwin;size=900x500")
/client/proc/check_watchlist(target_ckey)
var/target_sql_ckey = sanitizeSQL(target_ckey)
var/DBQuery/query_watch = dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'")
if(!query_watch.Execute())
var/err = query_watch.ErrorMsg()
log_game("SQL ERROR obtaining reason from watch table. Error : \[[err]\]\n")
return
if(query_watch.NextRow())
return query_watch.item[1]
else
return 0
+131
View File
@@ -0,0 +1,131 @@
/obj/item/device/assembly
name = "assembly"
desc = "A small electronic device that should never exist."
icon = 'icons/obj/assemblies/new_assemblies.dmi'
icon_state = ""
flags = CONDUCT
w_class = 2
materials = list(MAT_METAL=100)
throwforce = 2
throw_speed = 3
throw_range = 7
origin_tech = "magnets=1;engineering=1"
var/secured = 1
var/list/attached_overlays = null
var/obj/item/device/assembly_holder/holder = null
var/cooldown = 0//To prevent spam
var/wire_type = WIRE_RECEIVE | WIRE_PULSE
var/attachable = 0 // can this be attached to wires
var/datum/wires/connected = null
var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate()
var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder
var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly
var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate()
var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message
/obj/item/device/assembly/proc/on_attach()
/obj/item/device/assembly/proc/on_detach()
/obj/item/device/assembly/proc/holder_movement() //Called when the holder is moved
return
/obj/item/device/assembly/proc/describe() // Called by grenades to describe the state of the trigger (time left, etc)
return "The trigger assembly looks broken!"
/obj/item/device/assembly/proc/is_secured(mob/user)
if(!secured)
user << "<span class='warning'>The [name] is unsecured!</span>"
return 0
return 1
//Called via spawn(10) to have it count down the cooldown var
/obj/item/device/assembly/proc/process_cooldown()
cooldown--
if(cooldown <= 0)
return 0
spawn(10)
process_cooldown()
return 1
//Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs
/obj/item/device/assembly/proc/pulsed(radio = 0)
if(wire_type & WIRE_RECEIVE)
spawn(0)
activate()
if(radio && (wire_type & WIRE_RADIO_RECEIVE))
spawn(0)
activate()
return 1
//Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
/obj/item/device/assembly/proc/pulse(radio = 0)
if(connected && wire_type)
connected.pulse_assembly(src)
return 1
if(holder && (wire_type & WIRE_PULSE))
holder.process_activation(src, 1, 0)
if(holder && (wire_type & WIRE_PULSE_SPECIAL))
holder.process_activation(src, 0, 1)
return 1
// What the device does when turned on
/obj/item/device/assembly/proc/activate()
if(!secured || (cooldown > 0))
return 0
cooldown = 2
spawn(10)
process_cooldown()
return 1
/obj/item/device/assembly/proc/toggle_secure()
secured = !secured
update_icon()
return secured
/obj/item/device/assembly/attackby(obj/item/weapon/W, mob/user, params)
if(isassembly(W))
var/obj/item/device/assembly/A = W
if((!A.secured) && (!secured))
holder = new/obj/item/device/assembly_holder(get_turf(src))
holder.assemble(src,A,user)
user << "<span class='notice'>You attach and secure \the [A] to \the [src]!</span>"
else
user << "<span class='warning'>Both devices must be in attachable mode to be attached together.</span>"
return
if(istype(W, /obj/item/weapon/screwdriver))
if(toggle_secure())
user << "<span class='notice'>\The [src] is ready!</span>"
else
user << "<span class='notice'>\The [src] can now be attached!</span>"
return
..()
/obj/item/device/assembly/examine(mob/user)
..()
if(secured)
user << "\The [src] is secured and ready to be used."
else
user << "\The [src] can be attached to other things."
/obj/item/device/assembly/attack_self(mob/user)
if(!user)
return 0
user.set_machine(src)
interact(user)
return 1
/obj/item/device/assembly/interact(mob/user)
return //HTML MENU FOR WIRES GOES HERE
+165
View File
@@ -0,0 +1,165 @@
/obj/item/device/onetankbomb
name = "bomb"
icon = 'icons/obj/tank.dmi'
item_state = "assembly"
throwforce = 5
w_class = 3
throw_speed = 2
throw_range = 4
flags = CONDUCT
var/status = 0 //0 - not readied //1 - bomb finished with welder
var/obj/item/device/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device
var/obj/item/weapon/tank/bombtank = null //the second part of the bomb is a plasma tank
origin_tech = "materials=1;engineering=1"
/obj/item/device/onetankbomb/examine(mob/user)
..()
user.examinate(bombtank)
/obj/item/device/onetankbomb/update_icon()
if(bombtank)
icon_state = bombtank.icon_state
if(bombassembly)
add_overlay(bombassembly.icon_state)
add_overlay(bombassembly.overlays)
add_overlay("bomb_assembly")
/obj/item/device/onetankbomb/attackby(obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/device/analyzer))
bombtank.attackby(W, user)
return
if(istype(W, /obj/item/weapon/wrench) && !status) //This is basically bomb assembly code inverted. apparently it works.
user << "<span class='notice'>You disassemble [src].</span>"
bombassembly.loc = user.loc
bombassembly.master = null
bombassembly = null
bombtank.loc = user.loc
bombtank.master = null
bombtank = null
qdel(src)
return
if((istype(W, /obj/item/weapon/weldingtool) && W:welding))
if(!status)
status = 1
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]")
user << "<span class='notice'>A pressure hole has been bored to [bombtank] valve. \The [bombtank] can now be ignited.</span>"
else
status = 0
bombers += "[key_name(user)] unwelded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]"
user << "<span class='notice'>The hole has been closed.</span>"
add_fingerprint(user)
..()
/obj/item/device/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly
bombassembly.attack_self(user, 1)
add_fingerprint(user)
return
/obj/item/device/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here.
visible_message("\icon[src] *beep* *beep*", "*beep* *beep*")
sleep(10)
if(!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()
/obj/item/device/onetankbomb/Crossed(atom/movable/AM as mob|obj) //for mousetraps
if(bombassembly)
bombassembly.Crossed(AM)
/obj/item/device/onetankbomb/on_found(mob/finder) //for mousetraps
if(bombassembly)
bombassembly.on_found(finder)
// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ----------
/obj/item/weapon/tank/proc/bomb_assemble(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb
var/obj/item/device/assembly_holder/S = W
var/mob/M = user
if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it
return
if(!M.drop_item()) //Remove the assembly from your hands
return
var/obj/item/device/onetankbomb/R = new /obj/item/device/onetankbomb(loc)
M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it
M.put_in_hands(R) //Equips the bomb if possible, or puts it on the floor.
R.bombassembly = S //Tell the bomb about its assembly part
S.master = R //Tell the assembly about its new owner
S.loc = R //Move the assembly out of the fucking way
R.bombtank = src //Same for tank
master = R
loc = R
R.update_icon()
return
/obj/item/weapon/tank/proc/ignite() //This happens when a bomb is told to explode
air_contents.assert_gases("plasma", "o2")
var/fuel_moles = air_contents.gases["plasma"][MOLES] + air_contents.gases["o2"][MOLES]/6
air_contents.garbage_collect()
var/strength = 1
var/turf/ground_zero = get_turf(loc)
loc = null
if(air_contents.temperature > (T0C + 400))
strength = (fuel_moles/15)
if(strength >=1)
explosion(ground_zero, round(strength,1), round(strength*2,1), round(strength*3,1), round(strength*4,1))
else if(strength >=0.5)
explosion(ground_zero, 0, 1, 2, 4)
else if(strength >=0.2)
explosion(ground_zero, -1, 0, 1, 2)
else
ground_zero.assume_air(air_contents)
ground_zero.hotspot_expose(1000, 125)
else if(air_contents.temperature > (T0C + 250))
strength = (fuel_moles/20)
if(strength >=1)
explosion(ground_zero, 0, round(strength,1), round(strength*2,1), round(strength*3,1))
else if (strength >=0.5)
explosion(ground_zero, -1, 0, 1, 2)
else
ground_zero.assume_air(air_contents)
ground_zero.hotspot_expose(1000, 125)
else if(air_contents.temperature > (T0C + 100))
strength = (fuel_moles/25)
if (strength >=1)
explosion(ground_zero, -1, 0, round(strength,1), round(strength*3,1))
else
ground_zero.assume_air(air_contents)
ground_zero.hotspot_expose(1000, 125)
else
ground_zero.assume_air(air_contents)
ground_zero.hotspot_expose(1000, 125)
air_update_turf()
if(master)
qdel(master)
qdel(src)
/obj/item/weapon/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out.
var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles())
var/turf/T = get_turf(src)
if(!T)
return
T.assume_air(removed)
air_update_turf()
+150
View File
@@ -0,0 +1,150 @@
/obj/item/device/assembly/control
name = "blast door controller"
desc = "A small electronic device able to control a blast door remotely."
icon_state = "control"
origin_tech = "magnets=1;programming=2"
attachable = 1
var/id = null
var/can_change_id = 0
/obj/item/device/assembly/control/examine(mob/user)
..()
if(id)
user << "<span class='notice'>Its channel ID is '[id]'.</span>"
/obj/item/device/assembly/control/activate()
cooldown = 1
var/openclose
for(var/obj/machinery/door/poddoor/M in machines)
if(M.id == src.id)
if(openclose == null)
openclose = M.density
spawn(0)
if(M)
if(openclose)
M.open()
else
M.close()
return
sleep(10)
cooldown = 0
/obj/item/device/assembly/control/airlock
name = "airlock controller"
desc = "A small electronic device able to control an airlock remotely."
id = "badmin" // Set it to null for MEGAFUN.
var/specialfunctions = OPEN
/*
Bitflag, 1= open (OPEN)
2= idscan (IDSCAN)
4= bolts (BOLTS)
8= shock (SHOCK)
16= door safties (SAFE)
*/
/obj/item/device/assembly/control/airlock/activate()
cooldown = 1
for(var/obj/machinery/door/airlock/D in airlocks)
if(D.id_tag == src.id)
if(specialfunctions & OPEN)
if(D.density)
D.open()
else
D.close()
if(specialfunctions & IDSCAN)
D.aiDisabledIdScanner = !D.aiDisabledIdScanner
if(specialfunctions & BOLTS)
if(!D.wires.is_cut(WIRE_BOLTS) && D.hasPower())
D.locked = !D.locked
D.update_icon()
if(specialfunctions & SHOCK)
if(D.secondsElectrified)
D.secondsElectrified = -1
D.shockedby += "\[[time_stamp()]\][usr](ckey:[usr.ckey])"
add_logs(usr, D, "electrified")
else
D.secondsElectrified = 0
if(specialfunctions & SAFE)
D.safe = !D.safe
sleep(10)
cooldown = 0
/obj/item/device/assembly/control/massdriver
name = "mass driver controller"
desc = "A small electronic device able to control a mass driver."
/obj/item/device/assembly/control/massdriver/activate()
cooldown = 1
for(var/obj/machinery/door/poddoor/M in machines)
if (M.id == src.id)
spawn( 0 )
M.open()
sleep(10)
for(var/obj/machinery/mass_driver/M in machines)
if(M.id == src.id)
M.drive()
sleep(60)
for(var/obj/machinery/door/poddoor/M in machines)
if (M.id == src.id)
spawn( 0 )
M.close()
sleep(10)
cooldown = 0
/obj/item/device/assembly/control/igniter
name = "ignition controller"
desc = "A remote controller for a mounted igniter."
/obj/item/device/assembly/control/igniter/activate()
cooldown = 1
for(var/obj/machinery/sparker/M in machines)
if (M.id == src.id)
spawn( 0 )
M.ignite()
for(var/obj/machinery/igniter/M in machines)
if(M.id == src.id)
M.use_power(50)
M.on = !M.on
M.icon_state = "igniter[M.on]"
sleep(30)
cooldown = 0
/obj/item/device/assembly/control/flasher
name = "flasher controller"
desc = "A remote controller for a mounted flasher."
/obj/item/device/assembly/control/flasher/activate()
cooldown = 1
for(var/obj/machinery/flasher/M in machines)
if(M.id == src.id)
spawn(0)
M.flash()
sleep(50)
cooldown = 0
/obj/item/device/assembly/control/crematorium
name = "crematorium controller"
desc = "An evil-looking remote controller for a crematorium."
/obj/item/device/assembly/control/crematorium/activate()
cooldown = 1
for (var/obj/structure/bodycontainer/crematorium/C in crematoriums)
if (C.id == id)
C.cremate(usr)
sleep(50)
cooldown = 0
+203
View File
@@ -0,0 +1,203 @@
/obj/item/device/assembly/flash
name = "flash"
desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production."
icon_state = "flash"
item_state = "flashtool"
throwforce = 0
w_class = 1
materials = list(MAT_METAL = 300, MAT_GLASS = 300)
origin_tech = "magnets=2;combat=1"
crit_fail = 0 //Is the flash burnt out?
var/times_used = 0 //Number of times it's been used.
var/last_used = 0 //last world.time it was used.
/obj/item/device/assembly/flash/update_icon(var/flash = 0)
cut_overlays()
attached_overlays = list()
if(crit_fail)
add_overlay("flashburnt")
attached_overlays += "flashburnt"
if(flash)
add_overlay("flash-f")
attached_overlays += "flash-f"
spawn(5)
update_icon()
if(holder)
holder.update_icon()
/obj/item/device/assembly/flash/proc/clown_check(mob/living/carbon/human/user)
if(user.disabilities & CLUMSY && prob(50))
flash_carbon(user, user, 15, 0)
return 0
return 1
/obj/item/device/assembly/flash/activate()
if(!try_use_flash())
return 0
var/turf/T = get_turf(src)
T.visible_message("<span class='disarm'>[src] emits a blinding light!</span>")
for(var/mob/living/carbon/M in viewers(3, T))
flash_carbon(M, null, 5, 0)
/obj/item/device/assembly/flash/proc/burn_out() //Made so you can override it if you want to have an invincible flash from R&D or something.
if(!crit_fail)
crit_fail = 1
update_icon()
var/turf/T = get_turf(src)
T.visible_message("The [src.name] burns out!")
/obj/item/device/assembly/flash/proc/flash_recharge(interval=10)
if(prob(times_used * 3)) //The more often it's used in a short span of time the more likely it will burn out
burn_out()
return 0
var/deciseconds_passed = world.time - last_used
for(var/seconds = deciseconds_passed/10, seconds>=interval, seconds-=interval) //get 1 charge every interval
times_used--
last_used = world.time
times_used = max(0, times_used) //sanity
return 1
/obj/item/device/assembly/flash/proc/try_use_flash(mob/user = null)
flash_recharge(10)
if(crit_fail)
return 0
playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
update_icon(1)
times_used++
if(user && !clown_check(user))
return 0
return 1
/obj/item/device/assembly/flash/proc/flash_carbon(mob/living/carbon/M, mob/user = null, power = 15, targeted = 1)
add_logs(user, M, "flashed", src)
if(user && targeted)
if(M.weakeyes)
M.Weaken(3) //quick weaken bypasses eye protection but has no eye flash
if(M.flash_eyes(1, 1))
M.confused += power
terrible_conversion_proc(M, user)
M.Stun(1)
visible_message("<span class='disarm'>[user] blinds [M] with the flash!</span>")
user << "<span class='danger'>You blind [M] with the flash!</span>"
M << "<span class='userdanger'>[user] blinds you with the flash!</span>"
if(M.weakeyes)
M.Stun(2)
M.visible_message("<span class='disarm'>[M] gasps and shields their eyes!</span>", "<span class='userdanger'>You gasp and shield your eyes!</span>")
else
visible_message("<span class='disarm'>[user] fails to blind [M] with the flash!</span>")
user << "<span class='warning'>You fail to blind [M] with the flash!</span>"
M << "<span class='danger'>[user] fails to blind you with the flash!</span>"
else
if(M.flash_eyes())
M.confused += power
/obj/item/device/assembly/flash/attack(mob/living/M, mob/user)
if(!try_use_flash(user))
return 0
if(iscarbon(M))
flash_carbon(M, user, 5, 1)
return 1
else if(issilicon(M))
var/mob/living/silicon/robot/R = M
add_logs(user, R, "flashed", src)
update_icon(1)
M.Weaken(3)
R.confused += 5
R.flash_eyes(affect_silicon = 1)
user.visible_message("<span class='disarm'>[user] overloads [R]'s sensors with the flash!</span>", "<span class='danger'>You overload [R]'s sensors with the flash!</span>")
return 1
user.visible_message("<span class='disarm'>[user] fails to blind [M] with the flash!</span>", "<span class='warning'>You fail to blind [M] with the flash!</span>")
/obj/item/device/assembly/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0)
if(holder)
return 0
if(!try_use_flash(user))
return 0
user.visible_message("<span class='disarm'>[user]'s flash emits a blinding light!</span>", "<span class='danger'>Your flash emits a blinding light!</span>")
for(var/mob/living/carbon/M in oviewers(3, null))
flash_carbon(M, user, 1, 0)
/obj/item/device/assembly/flash/emp_act(severity)
if(!try_use_flash() || !loc)
return 0
for(var/mob/living/carbon/M in viewers(3, loc))
flash_carbon(M, null, 10, 0)
burn_out()
..()
/obj/item/device/assembly/flash/proc/terrible_conversion_proc(mob/M, mob/user)
if(ishuman(M) && ishuman(user) && M.stat != DEAD)
if(user.mind && (user.mind in ticker.mode.head_revolutionaries))
if(M.client)
if(M.stat == CONSCIOUS)
M.mind_initialize() //give them a mind datum if they don't have one.
var/resisted
if(!isloyal(M))
if(user.mind in ticker.mode.head_revolutionaries)
if(ticker.mode.add_revolutionary(M.mind))
M.Stun(3)
times_used -- //Flashes less likely to burn out for headrevs when used for conversion
else
resisted = 1
else
resisted = 1
if(resisted)
user << "<span class='warning'>This mind seems resistant to the flash!</span>"
else
user << "<span class='warning'>They must be conscious before you can convert them!</span>"
else
user << "<span class='warning'>This mind is so vacant that it is not susceptible to influence!</span>"
/obj/item/device/assembly/flash/cyborg
origin_tech = null
/obj/item/device/assembly/flash/cyborg/attack(mob/living/M, mob/user)
..()
cyborg_flash_animation(user)
/obj/item/device/assembly/flash/cyborg/attack_self(mob/user)
..()
cyborg_flash_animation(user)
/obj/item/device/assembly/flash/cyborg/attackby(obj/item/weapon/W, mob/user, params)
return
/obj/item/device/assembly/flash/cyborg/proc/cyborg_flash_animation(mob/living/user)
var/atom/movable/overlay/animation = new(user.loc)
animation.layer = user.layer + 1
animation.icon_state = "blank"
animation.icon = 'icons/mob/mob.dmi'
animation.master = user
flick("blspell", animation)
sleep(5)
qdel(animation)
/obj/item/device/assembly/flash/memorizer
name = "memorizer"
desc = "If you see this, you're not likely to remember it any time soon."
icon = 'icons/obj/device.dmi'
icon_state = "memorizer"
item_state = "nullrod"
/obj/item/device/assembly/flash/handheld //this is now the regular pocket flashes
+107
View File
@@ -0,0 +1,107 @@
/obj/item/device/assembly/health
name = "health sensor"
desc = "Used for scanning and monitoring health."
icon_state = "health"
materials = list(MAT_METAL=800, MAT_GLASS=200)
origin_tech = "magnets=1;biotech=1"
attachable = 1
secured = 0
var/scanning = 0
var/health_scan
var/alarm_health = 0
/obj/item/device/assembly/health/activate()
if(!..())
return 0//Cooldown check
toggle_scan()
return 0
/obj/item/device/assembly/health/toggle_secure()
secured = !secured
if(secured && scanning)
START_PROCESSING(SSobj, src)
else
scanning = 0
STOP_PROCESSING(SSobj, src)
update_icon()
return secured
/obj/item/device/assembly/health/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype(W, /obj/item/device/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
else
return ..()
/obj/item/device/assembly/health/process()
if(!scanning || !secured)
return
var/atom/A = src
if(connected && connected.holder)
A = connected.holder
for(A, A && !ismob(A), A=A.loc);
// like get_turf(), but for mobs.
var/mob/living/M = A
if(M)
health_scan = M.health
if(health_scan <= alarm_health)
pulse()
audible_message("\icon[src] *beep* *beep*", "*beep* *beep*")
toggle_scan()
return
return
/obj/item/device/assembly/health/proc/toggle_scan()
if(!secured)
return 0
scanning = !scanning
if(scanning)
START_PROCESSING(SSobj, src)
else
STOP_PROCESSING(SSobj, src)
return
/obj/item/device/assembly/health/interact(mob/user as mob)//TODO: Change this to the wires thingy
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>"
if(scanning && health_scan)
dat += "<BR>Health: [health_scan]"
user << browse(dat, "window=hscan")
onclose(user, "hscan")
return
/obj/item/device/assembly/health/Topic(href, href_list)
..()
if(!ismob(usr))
return
var/mob/user = usr
if(!user.canUseTopic(src))
usr << browse(null, "window=hscan")
onclose(usr, "hscan")
return
if(href_list["scanning"])
toggle_scan()
if(href_list["close"])
usr << browse(null, "window=hscan")
return
attack_self(user)
return
+16
View File
@@ -0,0 +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
+126
View File
@@ -0,0 +1,126 @@
/obj/item/device/assembly_holder
name = "Assembly"
icon = 'icons/obj/assemblies/new_assemblies.dmi'
icon_state = "holder"
item_state = "assembly"
flags = CONDUCT
throwforce = 5
w_class = 2
throw_speed = 2
throw_range = 7
var/obj/item/device/assembly/a_left = null
var/obj/item/device/assembly/a_right = null
/obj/item/device/assembly_holder/IsAssemblyHolder()
return 1
/obj/item/device/assembly_holder/proc/assemble(obj/item/device/assembly/A, obj/item/device/assembly/A2, mob/user)
attach(A,user)
attach(A2,user)
name = "[A.name]-[A2.name] assembly"
update_icon()
feedback_add_details("assembly_made","[A.name]-[A2.name]")
/obj/item/device/assembly_holder/proc/attach(obj/item/device/assembly/A, mob/user)
if(!A.remove_item_from_storage(src))
if(user)
user.remove_from_mob(A)
A.loc = src
A.holder = src
A.toggle_secure()
if(!a_left)
a_left = A
else
a_right = A
/obj/item/device/assembly_holder/update_icon()
cut_overlays()
if(a_left)
add_overlay("[a_left.icon_state]_left")
for(var/O in a_left.attached_overlays)
add_overlay("[O]_l")
if(a_right)
var/list/images = list()
images += image(icon, icon_state = "[a_right.icon_state]_left")
for(var/O in a_right.attached_overlays)
images += image(icon, icon_state = "[O]_l")
var/matrix = matrix(-1, 0, 0, 0, 1, 0)
for(var/image/I in images)
I.transform = matrix
add_overlay(I)
if(master)
master.update_icon()
/obj/item/device/assembly_holder/Crossed(atom/movable/AM as mob|obj)
if(a_left)
a_left.Crossed(AM)
if(a_right)
a_right.Crossed(AM)
/obj/item/device/assembly_holder/on_found(mob/finder)
if(a_left)
a_left.on_found(finder)
if(a_right)
a_right.on_found(finder)
/obj/item/device/assembly_holder/Move()
..()
if(a_left && a_right)
a_left.holder_movement()
a_right.holder_movement()
return
/obj/item/device/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess
if(a_left && a_right)
a_left.holder_movement()
a_right.holder_movement()
..()
return
/obj/item/device/assembly_holder/attackby(obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/weapon/screwdriver))
var/turf/T = get_turf(src)
if(!T)
return 0
if(a_left)
a_left.holder = null
a_left.loc = T
if(a_right)
a_right.holder = null
a_right.loc = T
qdel(src)
else
..()
/obj/item/device/assembly_holder/attack_self(mob/user)
src.add_fingerprint(user)
if(!a_left || !a_right)
user << "<span class='danger'>Assembly part missing!</span>"
return
if(istype(a_left,a_right.type))//If they are the same type it causes issues due to window code
switch(alert("Which side would you like to use?",,"Left","Right"))
if("Left")
a_left.attack_self(user)
if("Right")
a_right.attack_self(user)
return
else
a_left.attack_self(user)
a_right.attack_self(user)
/obj/item/device/assembly_holder/proc/process_activation(obj/D, normal = 1, special = 1)
if(!D)
return 0
if((normal) && (a_right) && (a_left))
if(a_right != D)
a_right.pulsed(0)
if(a_left != D)
a_left.pulsed(0)
if(master)
master.receive_signal()
return 1
+34
View File
@@ -0,0 +1,34 @@
/obj/item/device/assembly/igniter
name = "igniter"
desc = "A small electronic device able to ignite combustable substances."
icon_state = "igniter"
materials = list(MAT_METAL=500, MAT_GLASS=50)
origin_tech = "magnets=1"
var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread
heat = 1000
/obj/item/device/assembly/igniter/New()
..()
sparks.set_up(2, 0, src)
sparks.attach(src)
/obj/item/device/assembly/igniter/Destroy()
qdel(sparks)
sparks = null
return ..()
/obj/item/device/assembly/igniter/activate()
if(!..())
return 0//Cooldown check
var/turf/location = get_turf(loc)
if(location)
location.hotspot_expose(1000,1000)
sparks.start()
return 1
/obj/item/device/assembly/igniter/attack_self(mob/user)
activate()
add_fingerprint(user)
return
+236
View File
@@ -0,0 +1,236 @@
/obj/item/device/assembly/infra
name = "infrared emitter"
desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted.\n<span class='notice'>Alt-click to rotate it clockwise.</span>"
icon_state = "infrared"
materials = list(MAT_METAL=1000, MAT_GLASS=500)
origin_tech = "magnets=2;materials=2"
var/on = 0
var/visible = 0
var/obj/effect/beam/i_beam/first = null
var/obj/effect/beam/i_beam/last = null
/obj/item/device/assembly/infra/New()
..()
START_PROCESSING(SSobj, src)
/obj/item/device/assembly/infra/Destroy()
if(first)
qdel(first)
return ..()
/obj/item/device/assembly/infra/describe()
return "The infrared trigger is [on?"on":"off"]."
/obj/item/device/assembly/infra/activate()
if(!..())
return 0//Cooldown check
on = !on
update_icon()
return 1
/obj/item/device/assembly/infra/toggle_secure()
secured = !secured
if(secured)
START_PROCESSING(SSobj, src)
else
on = 0
if(first)
qdel(first)
STOP_PROCESSING(SSobj, src)
update_icon()
return secured
/obj/item/device/assembly/infra/update_icon()
cut_overlays()
attached_overlays = list()
if(on)
add_overlay("infrared_on")
attached_overlays += "infrared_on"
if(holder)
holder.update_icon()
return
/obj/item/device/assembly/infra/process()
if(!on)
if(first)
qdel(first)
return
if(!secured)
return
if(first && last)
last.process()
return
var/turf/T = get_turf(src)
if(T)
var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(T)
I.master = src
I.density = 1
I.setDir(dir)
first = I
step(I, I.dir)
if(first)
I.density = 0
I.vis_spread(visible)
I.limit = 8
I.process()
/obj/item/device/assembly/infra/attack_hand()
qdel(first)
..()
return
/obj/item/device/assembly/infra/Move()
var/t = dir
..()
setDir(t)
qdel(first)
return
/obj/item/device/assembly/infra/holder_movement()
if(!holder)
return 0
// setDir(holder.dir)
qdel(first)
return 1
/obj/item/device/assembly/infra/proc/trigger_beam()
if((!secured)||(!on)||(cooldown > 0))
return 0
pulse(0)
audible_message("\icon[src] *beep* *beep*", null, 3)
cooldown = 2
addtimer(src, "process_cooldown", 10)
/obj/item/device/assembly/infra/interact(mob/user)//TODO: change this this to the wire control panel
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>"
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")
onclose(user, "infra")
return
/obj/item/device/assembly/infra/Topic(href, href_list)
..()
if(usr.incapacitated() || !in_range(loc, usr))
usr << browse(null, "window=infra")
onclose(usr, "infra")
return
if(href_list["state"])
on = !(on)
update_icon()
if(href_list["visible"])
visible = !(visible)
if(first)
first.vis_spread(visible)
if(href_list["close"])
usr << browse(null, "window=infra")
return
if(usr)
attack_self(usr)
/obj/item/device/assembly/infra/verb/rotate()//This could likely be better
set name = "Rotate Infrared Laser"
set category = "Object"
set src in usr
if(usr.incapacitated())
return
setDir(turn(dir, 90))
return
/obj/item/device/assembly/infra/AltClick(mob/user)
..()
if(user.incapacitated())
user << "<span class='warning'>You can't do that right now!</span>"
return
if(!in_range(src, user))
return
else
rotate()
/***************************IBeam*********************************/
/obj/effect/beam/i_beam
name = "infrared beam"
icon = 'icons/obj/projectiles.dmi'
icon_state = "ibeam"
var/obj/effect/beam/i_beam/next = null
var/obj/effect/beam/i_beam/previous = null
var/obj/item/device/assembly/infra/master = null
var/limit = null
var/visible = 0
var/left = null
anchored = 1
/obj/effect/beam/i_beam/proc/hit()
if(master)
master.trigger_beam()
qdel(src)
return
/obj/effect/beam/i_beam/proc/vis_spread(v)
visible = v
if(next)
next.vis_spread(v)
/obj/effect/beam/i_beam/process()
if((loc.density || !(master)))
qdel(src)
return
if(left > 0)
left--
if(left < 1)
if(!(visible))
invisibility = INVISIBILITY_ABSTRACT
else
invisibility = 0
else
invisibility = 0
if(!next && (limit > 0))
var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(loc)
I.master = master
I.density = 1
I.setDir(dir)
I.previous = src
next = I
step(I, I.dir)
if(next)
I.density = 0
I.vis_spread(visible)
I.limit = limit - 1
master.last = I
I.process()
/obj/effect/beam/i_beam/Bump()
qdel(src)
return
/obj/effect/beam/i_beam/Bumped()
hit()
/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj)
if(istype(AM, /obj/effect/beam))
return
hit()
/obj/effect/beam/i_beam/Destroy()
if(master.first == src)
master.first = null
if(next)
qdel(next)
next = null
if(previous)
previous.next = null
master.last = previous
return ..()
+141
View File
@@ -0,0 +1,141 @@
/obj/item/device/assembly/mousetrap
name = "mousetrap"
desc = "A handy little spring-loaded trap for catching pesty rodents."
icon_state = "mousetrap"
materials = list(MAT_METAL=100)
origin_tech = "combat=1;materials=2;engineering=1"
attachable = 1
var/armed = 0
/obj/item/device/assembly/mousetrap/examine(mob/user)
..()
if(armed)
user << "It looks like it's armed."
/obj/item/device/assembly/mousetrap/activate()
if(..())
armed = !armed
if(!armed)
if(ishuman(usr))
var/mob/living/carbon/human/user = usr
if((user.getBrainLoss() >= 60) || user.disabilities & CLUMSY && prob(50))
user << "<span class='warning'>Your hand slips, setting off the trigger!</span>"
pulse(0)
update_icon()
if(usr)
playsound(usr.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3)
/obj/item/device/assembly/mousetrap/describe()
return "The pressure switch is [armed?"primed":"safe"]."
/obj/item/device/assembly/mousetrap/update_icon()
if(armed)
icon_state = "mousetraparmed"
else
icon_state = "mousetrap"
if(holder)
holder.update_icon()
/obj/item/device/assembly/mousetrap/proc/triggered(mob/target, type = "feet")
if(!armed)
return
var/obj/item/bodypart/affecting = null
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(PIERCEIMMUNE in H.dna.species.specflags)
playsound(src.loc, 'sound/effects/snap.ogg', 50, 1)
armed = 0
update_icon()
pulse(0)
return 0
switch(type)
if("feet")
if(!H.shoes)
affecting = H.get_bodypart(pick("l_leg", "r_leg"))
H.Weaken(3)
if("l_hand", "r_hand")
if(!H.gloves)
affecting = H.get_bodypart(type)
H.Stun(3)
if(affecting)
if(affecting.take_damage(1, 0))
H.update_damage_overlays(0)
H.updatehealth()
else if(ismouse(target))
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)
armed = 0
update_icon()
pulse(0)
/obj/item/device/assembly/mousetrap/attack_self(mob/living/carbon/human/user)
if(!armed)
user << "<span class='notice'>You arm [src].</span>"
else
if(((user.getBrainLoss() >= 60) || user.disabilities & CLUMSY) && prob(50))
var/which_hand = "l_hand"
if(!user.hand)
which_hand = "r_hand"
triggered(user, which_hand)
user.visible_message("<span class='warning'>[user] accidentally sets off [src], breaking their fingers.</span>", \
"<span class='warning'>You accidentally trigger [src]!</span>")
return
user << "<span class='notice'>You disarm [src].</span>"
armed = !armed
update_icon()
playsound(user.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3)
/obj/item/device/assembly/mousetrap/attack_hand(mob/living/carbon/human/user)
if(armed)
if(((user.getBrainLoss() >= 60) || user.disabilities & CLUMSY) && prob(50))
var/which_hand = "l_hand"
if(!user.hand)
which_hand = "r_hand"
triggered(user, which_hand)
user.visible_message("<span class='warning'>[user] accidentally sets off [src], breaking their fingers.</span>", \
"<span class='warning'>You accidentally trigger [src]!</span>")
return
..()
/obj/item/device/assembly/mousetrap/Crossed(atom/movable/AM as mob|obj)
if(armed)
if(ishuman(AM))
var/mob/living/carbon/H = AM
if(H.m_intent == "run")
triggered(H)
H.visible_message("<span class='warning'>[H] accidentally steps on [src].</span>", \
"<span class='warning'>You accidentally step on [src]</span>")
else if(isanimal(AM))
var/mob/living/simple_animal/SA = AM
if(!SA.flying)
triggered(AM)
else if(AM.density) // For mousetrap grenades, set off by anything heavy
triggered(AM)
..()
/obj/item/device/assembly/mousetrap/on_found(mob/finder)
if(armed)
finder.visible_message("<span class='warning'>[finder] accidentally sets off [src], breaking their fingers.</span>", \
"<span class='warning'>You accidentally trigger [src]!</span>")
triggered(finder, finder.hand ? "l_hand" : "r_hand")
return 1 //end the search!
return 0
/obj/item/device/assembly/mousetrap/hitby(A as mob|obj)
if(!armed)
return ..()
visible_message("<span class='warning'>[src] is triggered by [A].</span>")
triggered(null)
/obj/item/device/assembly/mousetrap/armed
icon_state = "mousetraparmed"
armed = 1
+168
View File
@@ -0,0 +1,168 @@
/obj/item/device/assembly/prox_sensor
name = "proximity sensor"
desc = "Used for scanning and alerting when someone enters a certain proximity."
icon_state = "prox"
materials = list(MAT_METAL=800, MAT_GLASS=200)
origin_tech = "magnets=1;engineering=1"
attachable = 1
var/scanning = 0
var/timing = 0
var/time = 10
var/sensitivity = 1
var/atom/oldloc
var/list/turfs_around = list()
/obj/item/device/assembly/prox_sensor/proc/toggle_scan()
/obj/item/device/assembly/prox_sensor/proc/sense()
/obj/item/device/assembly/prox_sensor/New()
..()
START_PROCESSING(SSobj, src)
oldloc = loc
/obj/item/device/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/device/assembly/prox_sensor/on_attach(datum/wires/w)
handle_move(w.holder)
/obj/item/device/assembly/prox_sensor/on_detach(datum/wires/w)
handle_move(w.holder.loc)
/obj/item/device/assembly/prox_sensor/activate()
if(!..())
return 0//Cooldown check
timing = !timing
update_icon()
return 1
/obj/item/device/assembly/prox_sensor/toggle_secure()
secured = !secured
if(!secured)
scanning = 0
timing = 0
update_icon()
return secured
/obj/item/device/assembly/prox_sensor/HasProximity(atom/movable/AM as mob|obj)
if (istype(AM, /obj/effect/beam))
return
sense()
/obj/item/device/assembly/prox_sensor/sense()
if((!secured)||(cooldown > 0))
return 0
pulse(0)
audible_message("\icon[src] *beep* *beep*", null, 3)
cooldown = 2
addtimer(src, "process_cooldown", 10)
/obj/item/device/assembly/prox_sensor/process()
if(timing)
time--
if(time <= 0)
timing = 0
toggle_scan(1)
time = initial(time)
handle_move(get_turf(loc))
/obj/item/device/assembly/prox_sensor/dropped()
..()
if(scanning)
addtimer(src, "sense", 0)
/obj/item/device/assembly/prox_sensor/toggle_scan(scan)
if(!secured)
return 0
scanning = scan
if(scanning)
add_to_proximity_list(src, sensitivity)
else
remove_from_proximity_list(src, sensitivity)
oldloc = get_turf(loc)
update_icon()
/obj/item/device/assembly/prox_sensor/proc/sensitivity_change(value)
var/sense = min(max(sensitivity + value, 0), 5)
if(scanning)
shift_proximity(src, oldloc, sensitivity, loc, sense)
sensitivity = sense
/obj/item/device/assembly/prox_sensor/update_icon()
cut_overlays()
attached_overlays = list()
if(timing)
add_overlay("prox_timing")
attached_overlays += "prox_timing"
if(scanning)
add_overlay("prox_scanning")
attached_overlays += "prox_scanning"
if(holder)
holder.update_icon()
return
/obj/item/device/assembly/prox_sensor/proc/handle_move(atom/newloc)
if(scanning)
if(shift_proximity(src, oldloc, sensitivity, newloc, sensitivity))
sense()
oldloc = newloc
/obj/item/device/assembly/prox_sensor/Move(newloc)
..()
handle_move(newloc)
/obj/item/device/assembly/prox_sensor/interact(mob/user)//TODO: Change this to the wires thingy
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!)"
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>"
user << browse(dat, "window=prox")
onclose(user, "prox")
return
/obj/item/device/assembly/prox_sensor/Topic(href, href_list)
..()
if(usr.incapacitated() || !in_range(loc, usr))
usr << browse(null, "window=prox")
onclose(usr, "prox")
return
if(href_list["sense"])
sensitivity_change(((href_list["sense"] == "up") ? 1 : -1))
if(href_list["scanning"])
toggle_scan(text2num(href_list["scanning"]))
if(href_list["time"])
timing = text2num(href_list["time"])
update_icon()
if(href_list["tp"])
var/tp = text2num(href_list["tp"])
time += tp
time = min(max(round(time), 0), 600)
if(href_list["close"])
usr << browse(null, "window=prox")
return
if(usr)
attack_self(usr)
+42
View File
@@ -0,0 +1,42 @@
/obj/item/assembly/shock_kit
name = "electrohelmet assembly"
desc = "This appears to be made from both an electropack and a helmet."
icon = 'icons/obj/assemblies.dmi'
icon_state = "shock_kit"
var/obj/item/clothing/head/helmet/part1 = null
var/obj/item/device/electropack/part2 = null
w_class = 5
flags = CONDUCT
/obj/item/assembly/shock_kit/Destroy()
qdel(part1)
qdel(part2)
return ..()
/obj/item/assembly/shock_kit/attackby(obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/weapon/wrench))
var/turf/T = loc
if(ismob(T))
T = T.loc
part1.loc = T
part2.loc = T
part1.master = null
part2.master = null
part1 = null
part2 = null
qdel(src)
return
add_fingerprint(user)
return
/obj/item/assembly/shock_kit/attack_self(mob/user)
part1.attack_self(user)
part2.attack_self(user)
add_fingerprint(user)
return
/obj/item/assembly/shock_kit/receive_signal()
if(istype(loc, /obj/structure/chair/e_chair))
var/obj/structure/chair/e_chair/C = loc
C.shock()
return
+191
View File
@@ -0,0 +1,191 @@
/obj/item/device/assembly/signaler
name = "remote signaling device"
desc = "Used to remotely activate devices."
icon_state = "signaller"
item_state = "signaler"
materials = list(MAT_METAL=400, MAT_GLASS=120)
origin_tech = "magnets=1;bluespace=1"
wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE
attachable = 1
var/code = 30
var/frequency = 1457
var/delay = 0
var/datum/radio_frequency/radio_connection
/obj/item/device/assembly/signaler/New()
..()
spawn(40)
set_frequency(frequency)
/obj/item/device/assembly/signaler/Destroy()
if(SSradio)
SSradio.remove_object(src,frequency)
return ..()
/obj/item/device/assembly/signaler/activate()
if(cooldown > 0)
return 0
cooldown = 2
spawn(10)
process_cooldown()
signal()
return 1
/obj/item/device/assembly/signaler/update_icon()
if(holder)
holder.update_icon()
return
/obj/item/device/assembly/signaler/interact(mob/user, flag1)
if(is_secured(user))
var/t1 = "-------"
// if ((src.b_stat && !( flag1 )))
// t1 = text("-------<BR>\nGreen Wire: []<BR>\nRed Wire: []<BR>\nBlue Wire: []<BR>\n", (src.wires & 4 ? text("<A href='?src=\ref[];wires=4'>Cut Wire</A>", src) : text("<A href='?src=\ref[];wires=4'>Mend Wire</A>", src)), (src.wires & 2 ? text("<A href='?src=\ref[];wires=2'>Cut Wire</A>", src) : text("<A href='?src=\ref[];wires=2'>Mend Wire</A>", src)), (src.wires & 1 ? text("<A href='?src=\ref[];wires=1'>Cut Wire</A>", src) : text("<A href='?src=\ref[];wires=1'>Mend Wire</A>", src)))
// else
// t1 = "-------" Speaker: [src.listening ? "<A href='byond://?src=\ref[src];listen=0'>Engaged</A>" : "<A href='byond://?src=\ref[src];listen=1'>Disengaged</A>"]<BR>
var/dat = {"
<TT>
<A href='byond://?src=\ref[src];send=1'>Send Signal</A><BR>
<B>Frequency/Code</B> for signaler:<BR>
Frequency:
<A href='byond://?src=\ref[src];freq=-10'>-</A>
<A href='byond://?src=\ref[src];freq=-2'>-</A>
[format_frequency(src.frequency)]
<A href='byond://?src=\ref[src];freq=2'>+</A>
<A href='byond://?src=\ref[src];freq=10'>+</A><BR>
Code:
<A href='byond://?src=\ref[src];code=-5'>-</A>
<A href='byond://?src=\ref[src];code=-1'>-</A>
[src.code]
<A href='byond://?src=\ref[src];code=1'>+</A>
<A href='byond://?src=\ref[src];code=5'>+</A><BR>
[t1]
</TT>"}
user << browse(dat, "window=radio")
onclose(user, "radio")
return
/obj/item/device/assembly/signaler/Topic(href, href_list)
..()
if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr))
usr << browse(null, "window=radio")
onclose(usr, "radio")
return
if (href_list["freq"])
var/new_frequency = (frequency + text2num(href_list["freq"]))
if(new_frequency < 1200 || new_frequency > 1600)
new_frequency = sanitize_frequency(new_frequency)
set_frequency(new_frequency)
if(href_list["code"])
src.code += text2num(href_list["code"])
src.code = round(src.code)
src.code = min(100, src.code)
src.code = max(1, src.code)
if(href_list["send"])
spawn( 0 )
signal()
if(usr)
attack_self(usr)
return
/obj/item/device/assembly/signaler/proc/signal()
if(!radio_connection) return
var/datum/signal/signal = new
signal.source = src
signal.encryption = code
signal.data["message"] = "ACTIVATE"
radio_connection.post_signal(src, signal)
var/time = time2text(world.realtime,"hh:mm:ss")
var/turf/T = get_turf(src)
if(usr)
lastsignalers.Add("[time] <B>:</B> [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) <B>:</B> [format_frequency(frequency)]/[code]")
return
/*
for(var/obj/item/device/assembly/signaler/S in world)
if(!S)
continue
if(S == src)
continue
if((S.frequency == src.frequency) && (S.code == src.code))
spawn(0)
if(S)
S.pulse(0)
return 0*/
/obj/item/device/assembly/signaler/receive_signal(datum/signal/signal)
if(!signal)
return 0
if(signal.encryption != code)
return 0
if(!(src.wires & WIRE_RADIO_RECEIVE))
return 0
pulse(1)
audible_message("\icon[src] *beep* *beep*", null, 1)
return
/obj/item/device/assembly/signaler/proc/set_frequency(new_frequency)
if(!SSradio)
sleep(20)
if(!SSradio)
return
SSradio.remove_object(src, frequency)
frequency = new_frequency
radio_connection = SSradio.add_object(src, frequency, RADIO_CHAT)
return
// Embedded signaller used in grenade construction.
// It's necessary because the signaler doens't have an off state.
// Generated during grenade construction. -Sayu
/obj/item/device/assembly/signaler/reciever
var/on = 0
/obj/item/device/assembly/signaler/reciever/proc/toggle_safety()
on = !on
/obj/item/device/assembly/signaler/reciever/activate()
toggle_safety()
return 1
/obj/item/device/assembly/signaler/reciever/describe()
return "The radio receiver is [on?"on":"off"]."
/obj/item/device/assembly/signaler/reciever/receive_signal(datum/signal/signal)
if(!on) return
return ..(signal)
// Embedded signaller used in anomalies.
/obj/item/device/assembly/signaler/anomaly
name = "anomaly core"
desc = "The neutralized core of an anomaly. It'd probably be valuable for research."
icon_state = "anomaly core"
item_state = "electronic"
/obj/item/device/assembly/signaler/anomaly/receive_signal(datum/signal/signal)
if(!signal)
return 0
if(signal.encryption != code)
return 0
for(var/obj/effect/anomaly/A in get_turf(src))
A.anomalyNeutralize()
/obj/item/device/assembly/signaler/anomaly/attack_self()
return
+118
View File
@@ -0,0 +1,118 @@
/obj/item/device/assembly/timer
name = "timer"
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)
origin_tech = "magnets=1;engineering=1"
attachable = 1
var/timing = 0
var/time = 5
var/saved_time = 5
var/loop = 0
/obj/item/device/assembly/timer/New()
..()
START_PROCESSING(SSobj, src)
/obj/item/device/assembly/timer/describe()
if(timing)
return "The timer is counting down from [time]!"
return "The timer is set for [time] seconds."
/obj/item/device/assembly/timer/activate()
if(!..())
return 0//Cooldown check
timing = !timing
update_icon()
return 1
/obj/item/device/assembly/timer/toggle_secure()
secured = !secured
if(secured)
START_PROCESSING(SSobj, src)
else
timing = 0
STOP_PROCESSING(SSobj, src)
update_icon()
return secured
/obj/item/device/assembly/timer/proc/timer_end()
if((!secured)||(cooldown > 0))
return 0
pulse(0)
audible_message("\icon[src] *beep* *beep*", null, 3)
cooldown = 2
spawn(10)
process_cooldown()
if(loop)
timing = 1
update_icon()
/obj/item/device/assembly/timer/process()
if(timing)
time--
if(time <= 0)
timing = 0
timer_end()
time = saved_time
/obj/item/device/assembly/timer/update_icon()
cut_overlays()
attached_overlays = list()
if(timing)
add_overlay("timer_timing")
attached_overlays += "timer_timing"
if(holder)
holder.update_icon()
/obj/item/device/assembly/timer/interact(mob/user)//TODO: Have this use the wires
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>"
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>"
var/datum/browser/popup = new(user, "timer", name)
popup.set_content(dat)
popup.open()
/obj/item/device/assembly/timer/Topic(href, href_list)
..()
if(usr.incapacitated() || !in_range(loc, usr))
usr << browse(null, "window=timer")
onclose(usr, "timer")
return
if(href_list["time"])
timing = text2num(href_list["time"])
if(timing && istype(holder, /obj/item/device/transfer_valve))
var/timer_message = "[key_name_admin(usr)](<A HREF='?_src_=holder;adminmoreinfo=\ref[usr]'>?</A>) (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[usr]'>FLW</A>) activated [src] attachment on [holder]."
message_admins(timer_message)
bombers += timer_message
log_game("[key_name(usr)] activated [src] attachment for [loc]")
update_icon()
if(href_list["repeat"])
loop = text2num(href_list["repeat"])
if(href_list["tp"])
var/tp = text2num(href_list["tp"])
time += tp
time = min(max(round(time), 1), 600)
saved_time = time
if(href_list["close"])
usr << browse(null, "window=timer")
return
if(usr)
attack_self(usr)
+90
View File
@@ -0,0 +1,90 @@
/obj/item/device/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)
origin_tech = "magnets=1;engineering=1"
flags = HEAR
attachable = 1
verb_say = "beeps"
verb_ask = "beeps"
verb_exclaim = "beeps"
var/listening = 0
var/recorded = "" //the activation message
var/mode = 1
var/global/list/modes = list("inclusive",
"exclusive",
"recognizer",
"voice sensor")
/obj/item/device/assembly/voice/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans)
if(speaker == src)
return
if(listening && !radio_freq)
record_speech(speaker, raw_message)
else
if(check_activation(speaker, raw_message))
spawn(10)
pulse(0)
/obj/item/device/assembly/voice/proc/record_speech(atom/movable/speaker, raw_message)
switch(mode)
if(1)
recorded = raw_message
listening = 0
say("Activation message is '[recorded]'.")
if(2)
recorded = raw_message
listening = 0
say("Activation message is '[recorded]'.")
if(3)
recorded = speaker.GetVoice()
listening = 0
say("Your voice pattern is saved.")
if(4)
if(length(raw_message))
spawn(10)
pulse(0)
/obj/item/device/assembly/voice/proc/check_activation(atom/movable/speaker, raw_message)
. = 0
switch(mode)
if(1)
if(findtextEx(raw_message, recorded))
. = 1
if(2)
if(raw_message == recorded)
. = 1
if(3)
if(speaker.GetVoice() == recorded)
. = 1
if(4)
if(length(raw_message))
. = 1
/obj/item/device/assembly/voice/attackby(obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/device/multitool))
mode %= modes.len
mode++
user << "You set [src] into a [modes[mode]] mode."
listening = 0
recorded = ""
else
return ..()
/obj/item/device/assembly/voice/activate()
if(secured)
if(!holder)
listening = !listening
say("[listening ? "Now" : "No longer"] recording input.")
/obj/item/device/assembly/voice/attack_self(mob/user)
if(!user)
return 0
activate()
return 1
/obj/item/device/assembly/voice/toggle_secure()
. = ..()
listening = 0
@@ -0,0 +1,179 @@
/atom/proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
return null
/turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
return
/turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh)
var/datum/gas_mixture/air_contents = return_air()
if(!air_contents)
return 0
var/oxy = air_contents.gases["o2"] ? air_contents.gases["o2"][MOLES] : 0
var/tox = air_contents.gases["plasma"] ? air_contents.gases["plasma"][MOLES] : 0
if(active_hotspot)
if(soh)
if(tox > 0.5 && oxy > 0.5)
if(active_hotspot.temperature < exposed_temperature)
active_hotspot.temperature = exposed_temperature
if(active_hotspot.volume < exposed_volume)
active_hotspot.volume = exposed_volume
return 1
var/igniting = 0
if((exposed_temperature > PLASMA_MINIMUM_BURN_TEMPERATURE) && tox > 0.5)
igniting = 1
if(igniting)
if(oxy < 0.5 || tox < 0.5)
return 0
active_hotspot = PoolOrNew(/obj/effect/hotspot, src)
active_hotspot.temperature = exposed_temperature
active_hotspot.volume = exposed_volume
active_hotspot.just_spawned = (current_cycle < SSair.times_fired)
//remove just_spawned protection if no longer processing this cell
SSair.add_to_active(src, 0)
return igniting
//This is the icon for fire on turfs, also helps for nurturing small fires until they are full tile
/obj/effect/hotspot
anchored = 1
mouse_opacity = 0
unacidable = 1//So you can't melt fire with acid.
icon = 'icons/effects/fire.dmi'
icon_state = "1"
layer = ABOVE_OPEN_TURF_LAYER
luminosity = 3
var/volume = 125
var/temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
var/just_spawned = 1
var/bypassing = 0
/obj/effect/hotspot/New()
..()
SSair.hotspots += src
perform_exposure()
setDir(pick(cardinal))
air_update_turf()
/obj/effect/hotspot/proc/perform_exposure()
var/turf/open/location = loc
if(!istype(location) || !(location.air))
return 0
if(volume > CELL_VOLUME*0.95)
bypassing = 1
else
bypassing = 0
if(bypassing)
if(!just_spawned)
volume = location.air.fuel_burnt*FIRE_GROWTH_RATE
temperature = location.air.temperature
else
var/datum/gas_mixture/affected = location.air.remove_ratio(volume/location.air.volume)
affected.temperature = temperature
affected.react()
temperature = affected.temperature
volume = affected.fuel_burnt*FIRE_GROWTH_RATE
location.assume_air(affected)
for(var/A in loc)
var/atom/item = A
if(item && item != src) // It's possible that the item is deleted in temperature_expose
item.fire_act(null, temperature, volume)
return 0
/obj/effect/hotspot/process()
if(just_spawned)
just_spawned = 0
return 0
var/turf/open/location = loc
if(!istype(location))
qdel(src)
return
if(location.excited_group)
location.excited_group.reset_cooldowns()
if((temperature < FIRE_MINIMUM_TEMPERATURE_TO_EXIST) || (volume <= 1))
qdel(src)
return
if(!(location.air) || !location.air.gases["plasma"] || !location.air.gases["o2"] || location.air.gases["plasma"][MOLES] < 0.5 || location.air.gases["o2"][MOLES] < 0.5)
qdel(src)
return
perform_exposure()
if(bypassing)
icon_state = "3"
location.burn_tile()
//Possible spread due to radiated heat
if(location.air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_SPREAD)
var/radiated_temperature = location.air.temperature*FIRE_SPREAD_RADIOSITY_SCALE
for(var/t in location.atmos_adjacent_turfs)
var/turf/open/T = t
if(T.active_hotspot)
T.hotspot_expose(radiated_temperature, CELL_VOLUME/4)
else
if(volume > CELL_VOLUME*0.4)
icon_state = "2"
else
icon_state = "1"
if(temperature > location.max_fire_temperature_sustained)
location.max_fire_temperature_sustained = temperature
if(location.heat_capacity && temperature > location.heat_capacity)
location.to_be_destroyed = 1
/*if(prob(25))
location.ReplaceWithSpace()
return 0*/
return 1
/obj/effect/hotspot/Destroy()
SetLuminosity(0)
SSair.hotspots -= src
DestroyTurf()
if(istype(loc, /turf))
var/turf/open/T = loc
if(T.active_hotspot == src)
T.active_hotspot = null
loc = null
..()
return QDEL_HINT_PUTINPOOL
/obj/effect/hotspot/proc/DestroyTurf()
if(istype(loc, /turf))
var/turf/T = loc
if(T.to_be_destroyed)
var/chance_of_deletion
if (T.heat_capacity) //beware of division by zero
chance_of_deletion = T.max_fire_temperature_sustained / T.heat_capacity * 8 //there is no problem with prob(23456), min() was redundant --rastaf0
else
chance_of_deletion = 100
if(prob(chance_of_deletion))
T.ChangeTurf(T.baseturf)
else
T.to_be_destroyed = 0
T.max_fire_temperature_sustained = 0
/obj/effect/hotspot/Crossed(mob/living/L)
..()
if(isliving(L))
L.fire_act()
@@ -0,0 +1,124 @@
/turf/proc/CanAtmosPass(turf/T)
/turf/closed/CanAtmosPass(turf/T)
return 0
/turf/open/CanAtmosPass(turf/T)
var/R
if(blocks_air || T.blocks_air)
R = 1
for(var/obj/O in contents+T.contents)
var/turf/other = (O.loc == src ? T : src)
if(!O.CanAtmosPass(other))
R = 1
if(O.BlockSuperconductivity()) //the direction and open/closed are already checked on CanAtmosPass() so there are no arguments
var/D = get_dir(src, T)
atmos_supeconductivity |= D
D = get_dir(T, src)
T.atmos_supeconductivity |= D
return 0 //no need to keep going, we got all we asked
atmos_supeconductivity &= ~get_dir(src, T)
T.atmos_supeconductivity &= ~get_dir(T, src)
return !R
/atom/movable/proc/CanAtmosPass()
return 1
/atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5)
return (!density || !height)
/turf/CanPass(atom/movable/mover, turf/target, height=1.5)
if(!target) return 0
if(istype(mover)) // turf/Enter(...) will perform more advanced checks
return !density
else // Now, doing more detailed checks for air movement and air group formation
if(target.blocks_air||blocks_air)
return 0
for(var/obj/obstacle in src)
if(!obstacle.CanPass(mover, target, height))
return 0
for(var/obj/obstacle in target)
if(!obstacle.CanPass(mover, src, height))
return 0
return 1
/atom/movable/proc/BlockSuperconductivity() // objects that block air and don't let superconductivity act. Only firelocks atm.
return 0
/turf/proc/CalculateAdjacentTurfs()
for(var/direction in cardinal)
var/turf/open/T = get_step(src, direction)
if(!istype(T))
continue
if(CanAtmosPass(T))
atmos_adjacent_turfs |= T
T.atmos_adjacent_turfs |= src
else
atmos_adjacent_turfs -= T
T.atmos_adjacent_turfs -= src
//returns a list of adjacent turfs that can share air with this one.
//alldir includes adjacent diagonal tiles that can share
// air with both of the related adjacent cardinal tiles
/turf/proc/GetAtmosAdjacentTurfs(alldir = 0)
var/adjacent_turfs = atmos_adjacent_turfs.Copy()
if (!alldir)
return adjacent_turfs
var/turf/curloc = src
for (var/direction in diagonals)
var/matchingDirections = 0
var/turf/S = get_step(curloc, direction)
for (var/checkDirection in cardinal)
var/turf/checkTurf = get_step(S, checkDirection)
if(!(checkTurf in S.atmos_adjacent_turfs))
continue
if (checkTurf in adjacent_turfs)
matchingDirections++
if (matchingDirections >= 2)
adjacent_turfs += S
break
return adjacent_turfs
/atom/movable/proc/air_update_turf(command = 0)
if(!istype(loc,/turf) && command)
return
var/turf/T = get_turf(loc)
T.air_update_turf(command)
/turf/proc/air_update_turf(command = 0)
if(command)
CalculateAdjacentTurfs()
SSair.add_to_active(src,command)
/atom/movable/proc/move_update_air(turf/T)
if(istype(T,/turf))
T.air_update_turf(1)
air_update_turf(1)
/atom/movable/proc/atmos_spawn_air(text) //because a lot of people loves to copy paste awful code lets just make a easy proc to spawn your plasma fires
var/turf/open/T = get_turf(src)
if(!istype(T))
return
T.atmos_spawn_air(text)
/turf/open/proc/atmos_spawn_air(text)
if(!text || !air)
return
var/datum/gas_mixture/G = new
G.parse_gas_string(text)
air.merge(G)
SSair.add_to_active(src, 0)
@@ -0,0 +1,425 @@
/turf
//used for temperature calculations
var/thermal_conductivity = 0.05
var/heat_capacity = 1
var/temperature_archived
//list of open turfs adjacent to us
var/list/atmos_adjacent_turfs = list()
//bitfield of dirs in which we are superconducitng
var/atmos_supeconductivity = 0
//used to determine whether we should archive
var/archived_cycle = 0
var/current_cycle = 0
//used for mapping and for breathing while in walls (because that's a thing that needs to be accounted for...)
//string parsed by /datum/gas/proc/copy_from_turf
var/initial_gas_mix = "o2=22;n2=82;TEMP=293.15"
//approximation of MOLES_O2STANDARD and MOLES_N2STANDARD pending byond allowing constant expressions to be embedded in constant strings
// If someone will place 0 of some gas there, SHIT WILL BREAK. Do not do that.
/turf/open
//used for spacewind
var/pressure_difference = 0
var/pressure_direction = 0
var/datum/excited_group/excited_group
var/excited = 0
var/recently_active = 0
var/datum/gas_mixture/air
var/obj/effect/hotspot/active_hotspot
var/list/atmos_overlay_types = list() //gas IDs of current active gas overlays
/turf/open/New()
..()
if(!blocks_air)
air = new
air.copy_from_turf(src)
/turf/open/Destroy()
if(active_hotspot)
qdel(active_hotspot)
active_hotspot = null
// Adds the adjacent turfs to the current atmos processing
for(var/T in atmos_adjacent_turfs)
SSair.add_to_active(T)
return ..()
/////////////////GAS MIXTURE PROCS///////////////////
/turf/open/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
if(!giver)
return 0
air.merge(giver)
update_visuals()
return 1
/turf/open/remove_air(amount)
var/datum/gas_mixture/ours = return_air()
var/datum/gas_mixture/removed = ours.remove(amount)
update_visuals()
return removed
/turf/open/proc/copy_air_with_tile(turf/open/T)
if(istype(T))
air.copy_from(T.air)
/turf/open/proc/copy_air(datum/gas_mixture/copy)
if(copy)
air.copy_from(copy)
/turf/return_air()
var/datum/gas_mixture/GM = new
GM.copy_from_turf(src)
return GM
/turf/open/return_air()
return air
/turf/temperature_expose()
if(temperature > heat_capacity)
to_be_destroyed = 1
/turf/proc/archive()
temperature_archived = temperature
/turf/open/archive()
air.archive()
archived_cycle = SSair.times_fired
..()
/////////////////////////GAS OVERLAYS//////////////////////////////
/turf/open/proc/update_visuals()
var/list/new_overlay_types = tile_graphic()
for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
overlays -= overlay
atmos_overlay_types -= overlay
for(var/overlay in new_overlay_types-atmos_overlay_types) //doesn't add overlays that already exist
add_overlay(overlay)
atmos_overlay_types = new_overlay_types
/turf/open/proc/tile_graphic()
. = new /list
var/list/gases = air.gases
for(var/id in gases)
var/gas = gases[id]
if(gas[GAS_META][META_GAS_OVERLAY] && gas[MOLES] > gas[GAS_META][META_GAS_MOLES_VISIBLE])
. += gas[GAS_META][META_GAS_OVERLAY]
/////////////////////////////SIMULATION///////////////////////////////////
/turf/proc/process_cell(fire_count)
SSair.remove_from_active(src)
/turf/open/process_cell(fire_count)
if(archived_cycle < fire_count) //archive self if not already done
archive()
current_cycle = fire_count
var/remove = 1 //set by non simulated turfs who are sharing with this turf
//cache for sanic speed
var/list/adjacent_turfs = atmos_adjacent_turfs
var/datum/excited_group/our_excited_group = excited_group
var/adjacent_turfs_length = adjacent_turfs.len
for(var/t in adjacent_turfs)
var/turf/open/enemy_tile = t
if(fire_count > enemy_tile.current_cycle)
enemy_tile.archive()
/******************* GROUP HANDLING START *****************************************************************/
if(enemy_tile.excited)
//cache for sanic speed
var/datum/excited_group/enemy_excited_group = enemy_tile.excited_group
if(our_excited_group)
if(enemy_excited_group)
if(our_excited_group != enemy_excited_group)
//combine groups (this also handles updating the excited_group var of all involved turfs)
our_excited_group.merge_groups(enemy_excited_group)
our_excited_group = excited_group //update our cache
share_air(enemy_tile, fire_count, adjacent_turfs_length) //share
else
if((recently_active == 1 && enemy_tile.recently_active == 1) || air.compare(enemy_tile.air))
our_excited_group.add_turf(enemy_tile) //add enemy to our group
share_air(enemy_tile, fire_count, adjacent_turfs_length) //share
else
if(enemy_excited_group)
if((recently_active == 1 && enemy_tile.recently_active == 1) || air.compare(enemy_tile.air))
enemy_excited_group.add_turf(src) //join self to enemy group
our_excited_group = excited_group //update our cache
share_air(enemy_tile, fire_count, adjacent_turfs_length) //share
else
if((recently_active == 1 && enemy_tile.recently_active == 1) || air.compare(enemy_tile.air))
var/datum/excited_group/EG = new //generate new group
EG.add_turf(src)
EG.add_turf(enemy_tile)
our_excited_group = excited_group //update our cache
share_air(enemy_tile, fire_count, adjacent_turfs_length) //share
else
if(air.compare(enemy_tile.air)) //compare if
SSair.add_to_active(enemy_tile) //excite enemy
if(our_excited_group)
excited_group.add_turf(enemy_tile) //add enemy to group
else
var/datum/excited_group/EG = new //generate new group
EG.add_turf(src)
EG.add_turf(enemy_tile)
our_excited_group = excited_group //update our cache
share_air(enemy_tile, fire_count, adjacent_turfs_length) //share
/******************* GROUP HANDLING FINISH *********************************************************************/
air.react()
update_visuals()
if(air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
hotspot_expose(air.temperature, CELL_VOLUME)
for(var/atom/movable/item in src)
item.temperature_expose(air, air.temperature, CELL_VOLUME)
temperature_expose(air, air.temperature, CELL_VOLUME)
if(air.temperature > MINIMUM_TEMPERATURE_START_SUPERCONDUCTION)
if(consider_superconductivity(starting = 1))
remove = 0
if(!our_excited_group && remove == 1)
SSair.remove_from_active(src)
/turf/open/proc/share_air(turf/open/T, fire_count, adjacent_turfs_length)
if(T.current_cycle < fire_count)
var/difference = air.share(T.air, adjacent_turfs_length)
if(difference)
if(difference > 0)
consider_pressure_difference(T, difference)
else
T.consider_pressure_difference(src, -difference)
last_share_check()
//////////////////////////SPACEWIND/////////////////////////////
/turf/open/proc/consider_pressure_difference(turf/T, difference)
SSair.high_pressure_delta |= src
if(difference > pressure_difference)
pressure_direction = get_dir(src, T)
pressure_difference = difference
/turf/open/proc/last_share_check()
if(air.last_share > MINIMUM_AIR_TO_SUSPEND)
excited_group.reset_cooldowns()
else if(air.last_share > MINIMUM_MOLES_DELTA_TO_MOVE)
excited_group.dismantle_cooldown = 0
/turf/open/proc/high_pressure_movements()
for(var/atom/movable/M in src)
M.experience_pressure_difference(pressure_difference, pressure_direction)
/atom/movable/var/pressure_resistance = 5
/atom/movable/var/last_high_pressure_movement_air_cycle = 0
/atom/movable/proc/experience_pressure_difference(pressure_difference, direction)
set waitfor = 0
. = 0
if(!anchored && !pulledby)
. = 1
if(pressure_difference > pressure_resistance && last_high_pressure_movement_air_cycle < SSair.times_fired)
last_high_pressure_movement_air_cycle = SSair.times_fired
step(src, direction)
///////////////////////////EXCITED GROUPS/////////////////////////////
/datum/excited_group
var/list/turf_list = list()
var/breakdown_cooldown = 0
var/dismantle_cooldown = 0
/datum/excited_group/New()
SSair.excited_groups += src
/datum/excited_group/proc/add_turf(turf/open/T)
turf_list += T
T.excited_group = src
T.recently_active = 1
reset_cooldowns()
/datum/excited_group/proc/merge_groups(datum/excited_group/E)
if(turf_list.len > E.turf_list.len)
SSair.excited_groups -= E
for(var/t in E.turf_list)
var/turf/open/T = t
T.excited_group = src
turf_list += T
reset_cooldowns()
else
SSair.excited_groups -= src
for(var/t in turf_list)
var/turf/open/T = t
T.excited_group = E
E.turf_list += T
E.reset_cooldowns()
/datum/excited_group/proc/reset_cooldowns()
breakdown_cooldown = 0
dismantle_cooldown = 0
//argument is so world start can clear out any turf differences quickly.
/datum/excited_group/proc/self_breakdown(space_is_all_consuming = 0)
var/datum/gas_mixture/A = new
//make local for sanic speed
var/list/A_gases = A.gases
var/list/turf_list = src.turf_list
var/turflen = turf_list.len
var/space_in_group = 0
for(var/t in turf_list)
var/turf/open/T = t
if (space_is_all_consuming && !space_in_group && istype(T.air, /datum/gas_mixture/space))
space_in_group = 1
qdel(A)
A = new/datum/gas_mixture/space()
A.merge(T.air)
for(var/id in A_gases)
A_gases[id][MOLES] = A_gases[id][MOLES]/turflen
for(var/t in turf_list)
var/turf/open/T = t
T.air.copy_from(A)
T.update_visuals()
breakdown_cooldown = 0
/datum/excited_group/proc/dismantle()
for(var/t in turf_list)
var/turf/open/T = t
T.excited = 0
T.recently_active = 0
T.excited_group = null
SSair.active_turfs -= T
garbage_collect()
/datum/excited_group/proc/garbage_collect()
for(var/t in turf_list)
var/turf/open/T = t
T.excited_group = null
turf_list.Cut()
SSair.excited_groups -= src
////////////////////////SUPERCONDUCTIVITY/////////////////////////////
/turf/proc/conductivity_directions()
if(archived_cycle < SSair.times_fired)
archive()
return NORTH|SOUTH|EAST|WEST
/turf/open/conductivity_directions()
if(blocks_air)
return ..()
for(var/direction in cardinal)
var/turf/T = get_step(src, direction)
if(!(T in atmos_adjacent_turfs) && !(atmos_supeconductivity & direction))
. |= direction
/turf/proc/neighbor_conduct_with_src(turf/open/other)
if(!other.blocks_air) //Open but neighbor is solid
other.temperature_share_open_to_solid(src)
else //Both tiles are solid
other.share_temperature_mutual_solid(src, thermal_conductivity)
temperature_expose(null, temperature, null)
/turf/open/neighbor_conduct_with_src(turf/other)
if(blocks_air)
..()
return
if(!other.blocks_air) //Both tiles are open
var/turf/open/T = other
T.air.temperature_share(air, WINDOW_HEAT_TRANSFER_COEFFICIENT)
else //Solid but neighbor is open
temperature_share_open_to_solid(other)
SSair.add_to_active(src, 0)
/turf/proc/super_conduct()
var/conductivity_directions = conductivity_directions()
if(conductivity_directions)
//Conduct with tiles around me
for(var/direction in cardinal)
if(conductivity_directions & direction)
var/turf/neighbor = get_step(src,direction)
if(!neighbor.thermal_conductivity)
continue
if(neighbor.archived_cycle < SSair.times_fired)
neighbor.archive()
neighbor.neighbor_conduct_with_src(src)
neighbor.consider_superconductivity()
radiate_to_spess()
finish_superconduction()
/turf/proc/finish_superconduction(temp = temperature)
//Make sure still hot enough to continue conducting heat
if(temp < MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION)
SSair.active_super_conductivity -= src
return 0
/turf/open/finish_superconduction()
//Conduct with air on my tile if I have it
if(!blocks_air)
temperature = air.temperature_share(null, thermal_conductivity, temperature, heat_capacity)
..((blocks_air ? temperature : air.temperature))
/turf/proc/consider_superconductivity()
if(!thermal_conductivity)
return 0
SSair.active_super_conductivity |= src
return 1
/turf/open/consider_superconductivity(starting)
if(air.temperature < (starting?MINIMUM_TEMPERATURE_START_SUPERCONDUCTION:MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION))
return 0
if(air.heat_capacity() < M_CELL_WITH_RATIO) // Was: MOLES_CELLSTANDARD*0.1*0.05 Since there are no variables here we can make this a constant.
return 0
return ..()
/turf/closed/consider_superconductivity(starting)
if(temperature < (starting?MINIMUM_TEMPERATURE_START_SUPERCONDUCTION:MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION))
return 0
return ..()
/turf/proc/radiate_to_spess() //Radiate excess tile heat to space
if(temperature > T0C) //Considering 0 degC as te break even point for radiation in and out
var/delta_temperature = (temperature_archived - TCMB) //hardcoded space temperature
if((heat_capacity > 0) && (abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER))
var/heat = thermal_conductivity*delta_temperature* \
(heat_capacity*700000/(heat_capacity+700000)) //700000 is the heat_capacity from a space turf, hardcoded here
temperature -= heat/heat_capacity
/turf/open/proc/temperature_share_open_to_solid(turf/sharer)
sharer.temperature = air.temperature_share(null, sharer.thermal_conductivity, sharer.temperature, sharer.heat_capacity)
/turf/proc/share_temperature_mutual_solid(turf/sharer, conduction_coefficient) //to be understood
var/delta_temperature = (temperature_archived - sharer.temperature_archived)
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER && heat_capacity && sharer.heat_capacity)
var/heat = conduction_coefficient*delta_temperature* \
(heat_capacity*sharer.heat_capacity/(heat_capacity+sharer.heat_capacity))
temperature -= heat/heat_capacity
sharer.temperature += heat/sharer.heat_capacity
@@ -0,0 +1,562 @@
/*
What are the archived variables for?
Calculations are done using the archived variables with the results merged into the regular variables.
This prevents race conditions that arise based on the order of tile processing.
*/
#define MINIMUM_HEAT_CAPACITY 0.0003
#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 */
var/list/meta_gas_info = meta_gas_list() //see ATMOSPHERICS/gas_types.dm
var/list/gaslist_cache = null
/proc/gaslist(id)
var/list/cached_gas
//only instantiate the first time it's needed
if(!gaslist_cache)
gaslist_cache = new(meta_gas_info.len)
//only setup the individual lists the first time they're needed
if(!gaslist_cache[id])
if(!meta_gas_info[id])
CRASH("Gas [id] does not exist!")
cached_gas = new(3)
gaslist_cache[id] = cached_gas
cached_gas[MOLES] = 0
cached_gas[ARCHIVE] = 0
cached_gas[GAS_META] = meta_gas_info[id]
else
cached_gas = gaslist_cache[id]
//Copy() it because only GAS_META is static
return cached_gas.Copy()
/datum/gas_mixture
var/list/gases
var/temperature //kelvins
var/tmp/temperature_archived
var/volume //liters
var/last_share
var/tmp/fuel_burnt
/datum/gas_mixture/New(volume = CELL_VOLUME)
..()
gases = new
temperature = 0
temperature_archived = 0
src.volume = volume
last_share = 0
fuel_burnt = 0
//listmos procs
//assert_gas(gas_id) - used to guarantee that the gas list for this id exists.
//Must be used before adding to a gas. May be used before reading from a gas.
/datum/gas_mixture/proc/assert_gas(gas_id)
var/cached_gases = gases
if(cached_gases[gas_id])
return
cached_gases[gas_id] = gaslist(gas_id)
//assert_gases(args) - shorthand for calling assert_gas() once for each gas type.
/datum/gas_mixture/proc/assert_gases()
for(var/id in args)
assert_gas(id)
//add_gas(gas_id) - similar to assert_gas(), but does not check for an existing
//gas list for this id. This can clobber existing gases.
//Used instead of assert_gas() when you know the gas does not exist. Faster than assert_gas().
/datum/gas_mixture/proc/add_gas(gas_id)
gases[gas_id] = gaslist(gas_id)
//add_gases(args) - shorthand for calling add_gas() once for each gas_type.
/datum/gas_mixture/proc/add_gases()
for(var/id in args)
add_gas(id)
//garbage_collect() - removes any gas list which is empty.
//If called with a list as an argument, only removes gas lists with IDs from that list.
//Must be used after subtracting from a gas. Must be used after assert_gas()
//if assert_gas() was called only to read from the gas.
//By removing empty gases, processing speed is increased.
/datum/gas_mixture/proc/garbage_collect(list/tocheck)
var/list/cached_gases = gases
for(var/id in (tocheck || cached_gases))
if(cached_gases[id][MOLES] <= 0 && cached_gases[id][ARCHIVE] <= 0)
cached_gases -= id
//PV = nRT
/datum/gas_mixture/proc/heat_capacity() //joules per kelvin
var/list/cached_gases = gases
. = 0
for(var/id in cached_gases)
. += cached_gases[id][MOLES] * cached_gases[id][GAS_META][META_GAS_SPECIFIC_HEAT]
/datum/gas_mixture/proc/heat_capacity_archived() //joules per kelvin
var/list/cached_gases = gases
. = 0
for(var/id in cached_gases)
. += cached_gases[id][ARCHIVE] * cached_gases[id][GAS_META][META_GAS_SPECIFIC_HEAT]
/datum/gas_mixture/proc/total_moles() //moles
var/list/cached_gases = gases
. = 0
for(var/id in cached_gases)
. += cached_gases[id][MOLES]
/datum/gas_mixture/proc/return_pressure() //kilopascals
if(volume > 0) // to prevent division by zero
return total_moles() * R_IDEAL_GAS_EQUATION * temperature / volume
return 0
/datum/gas_mixture/proc/return_temperature() //kelvins
return temperature
/datum/gas_mixture/proc/return_volume() //liters
return max(0, volume)
/datum/gas_mixture/proc/thermal_energy() //joules
return temperature * heat_capacity()
//Procedures used for very specific events
/datum/gas_mixture/proc/react(atom/dump_location)
var/list/cached_gases = gases //this speeds things up because >byond
var/reacting = 0 //set to 1 if a notable reaction occured (used by pipe_network)
if(temperature < TCMB)
temperature = TCMB
if(cached_gases["agent_b"] && temperature > 900 && cached_gases["plasma"] && cached_gases["co2"])
//agent b converts hot co2 to o2 (endothermic)
if(cached_gases["plasma"][MOLES] > MINIMUM_HEAT_CAPACITY && cached_gases["co2"][MOLES] > MINIMUM_HEAT_CAPACITY)
var/reaction_rate = min(cached_gases["co2"][MOLES]*0.75, cached_gases["plasma"][MOLES]*0.25, cached_gases["agent_b"][MOLES]*0.05)
cached_gases["co2"][MOLES] -= reaction_rate
assert_gas("o2") //only need to assert oxygen, as this reaction doesn't occur without the other gases existing
cached_gases["o2"][MOLES] += reaction_rate
cached_gases["agent_b"][MOLES] -= reaction_rate*0.05
temperature -= (reaction_rate*20000)/heat_capacity()
garbage_collect()
reacting = 1
/*
if(thermal_energy() > (PLASMA_BINDING_ENERGY*10))
if(cached_gases["plasma"] && cached_gases["co2"] && cached_gases["plasma"][MOLES] > MINIMUM_HEAT_CAPACITY && cached_gases["co2"][MOLES] > MINIMUM_HEAT_CAPACITY && (cached_gases["plasma"][MOLES]+cached_gases["co2"][MOLES])/total_moles() >= FUSION_PURITY_THRESHOLD)//Fusion wont occur if the level of impurities is too high.
//fusion converts plasma and co2 to o2 and n2 (exothermic)
//world << "pre [temperature, [cached_gases["plasma"][MOLES]], [cached_gases["co2"][MOLES]]
var/old_heat_capacity = heat_capacity()
var/carbon_efficency = min(cached_gases["plasma"][MOLES]/cached_gases["co2"][MOLES],MAX_CARBON_EFFICENCY)
var/reaction_energy = thermal_energy()
var/moles_impurities = total_moles()-(cached_gases["plasma"][MOLES]+cached_gases["co2"][MOLES])
var/plasma_fused = (PLASMA_FUSED_COEFFICENT*carbon_efficency)*(temperature/PLASMA_BINDING_ENERGY)
var/carbon_catalyzed = (CARBON_CATALYST_COEFFICENT*carbon_efficency)*(temperature/PLASMA_BINDING_ENERGY)
var/oxygen_added = carbon_catalyzed
var/nitrogen_added = (plasma_fused-oxygen_added)-(thermal_energy()/PLASMA_BINDING_ENERGY)
reaction_energy = max(reaction_energy+((carbon_efficency*cached_gases["plasma"][MOLES])/((moles_impurities/carbon_efficency)+2)*10)+((plasma_fused/(moles_impurities/carbon_efficency))*PLASMA_BINDING_ENERGY),0)
assert_gases("o2", "n2")
cached_gases["plasma"][MOLES] -= plasma_fused
cached_gases["co2"][MOLES] -= carbon_catalyzed
cached_gases["o2"][MOLES] += oxygen_added
cached_gases["n2"][MOLES] += nitrogen_added
garbage_collect()
if(reaction_energy > 0)
reacting = 1
var/new_heat_capacity = heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
temperature = max(((temperature*old_heat_capacity + reaction_energy)/new_heat_capacity),TCMB)
//Prevents whatever mechanism is causing it to hit negative temperatures.
//world << "post [temperature], [cached_gases["plasma"][MOLES]], [cached_gases["co2"][MOLES]]
*/
fuel_burnt = 0
if(temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
//world << "pre [temperature], [cached_gases["o2"][MOLES]], [cached_gases["plasma"][MOLES]]"
if(fire())
reacting = 1
//world << "post [temperature], [cached_gases["o2"][MOLES]], [cached_gases["plasma"][MOLES]]"
return reacting
/datum/gas_mixture/proc/fire()
//combustion of plasma and volatile fuel, which both act as hydrocarbons (exothermic)
var/energy_released = 0
var/old_heat_capacity = heat_capacity()
var/list/cached_gases = gases //this speeds things up because accessing datum vars is slow
//General volatile gas burn
if(cached_gases["v_fuel"] && cached_gases["v_fuel"][MOLES])
var/burned_fuel
if(!cached_gases["o2"])
burned_fuel = 0
else if(cached_gases["o2"][MOLES] < cached_gases["v_fuel"][MOLES])
burned_fuel = cached_gases["o2"][MOLES]
cached_gases["v_fuel"][MOLES] -= burned_fuel
cached_gases["o2"][MOLES] = 0
else
burned_fuel = cached_gases["v_fuel"][MOLES]
cached_gases["o2"][MOLES] -= cached_gases["v_fuel"][MOLES]
if(burned_fuel)
energy_released += FIRE_CARBON_ENERGY_RELEASED * burned_fuel
assert_gas("co2")
cached_gases["co2"][MOLES] += burned_fuel
fuel_burnt += burned_fuel
//Handle plasma burning
if(cached_gases["plasma"] && cached_gases["plasma"][MOLES] > MINIMUM_HEAT_CAPACITY)
var/plasma_burn_rate = 0
var/oxygen_burn_rate = 0
//more plasma released at higher temperatures
var/temperature_scale
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)
assert_gas("o2")
oxygen_burn_rate = OXYGEN_BURN_RATE_BASE - temperature_scale
if(cached_gases["o2"][MOLES] > cached_gases["plasma"][MOLES]*PLASMA_OXYGEN_FULLBURN)
plasma_burn_rate = (cached_gases["plasma"][MOLES]*temperature_scale)/PLASMA_BURN_RATE_DELTA
else
plasma_burn_rate = (temperature_scale*(cached_gases["o2"][MOLES]/PLASMA_OXYGEN_FULLBURN))/PLASMA_BURN_RATE_DELTA
if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY)
assert_gas("co2")
cached_gases["plasma"][MOLES] = QUANTIZE(cached_gases["plasma"][MOLES] - plasma_burn_rate)
cached_gases["o2"][MOLES] = QUANTIZE(cached_gases["o2"][MOLES] - (plasma_burn_rate * oxygen_burn_rate))
cached_gases["co2"][MOLES] += plasma_burn_rate
energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate)
fuel_burnt += (plasma_burn_rate)*(1+oxygen_burn_rate)
garbage_collect()
if(energy_released > 0)
var/new_heat_capacity = heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
temperature = (temperature*old_heat_capacity + energy_released)/new_heat_capacity
return fuel_burnt
/datum/gas_mixture/proc/archive()
//Update archived versions of variables
//Returns: 1 in all cases
/datum/gas_mixture/proc/merge(datum/gas_mixture/giver)
//Merges all air from giver into self. Deletes giver.
//Returns: 1 if we are mutable, 0 otherwise
/datum/gas_mixture/proc/remove(amount)
//Proportionally removes amount of gas from the gas_mixture
//Returns: gas_mixture with the gases removed
/datum/gas_mixture/proc/remove_ratio(ratio)
//Proportionally removes amount of gas from the gas_mixture
//Returns: gas_mixture with the gases removed
/datum/gas_mixture/proc/copy()
//Creates new, identical gas mixture
//Returns: duplicate gas mixture
/datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample)
//Copies variables from sample
//Returns: 1 if we are mutable, 0 otherwise
/datum/gas_mixture/proc/copy_from_turf(turf/model)
//Copies all gas info from the turf into the gas list along with temperature
//Returns: 1 if we are mutable, 0 otherwise
/datum/gas_mixture/proc/parse_gas_string(gas_string)
//Copies variables from a particularly formatted string.
//Returns: 1 if we are mutable, 0 otherwise
/datum/gas_mixture/proc/share(datum/gas_mixture/sharer)
//Performs air sharing calculations between two gas_mixtures assuming only 1 boundary length
//Returns: amount of gas exchanged (+ if sharer received)
/datum/gas_mixture/proc/after_share(datum/gas_mixture/sharer)
//called on share's sharer to let it know it just got some gases
/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient)
//Performs temperature sharing calculations (via conduction) between two gas_mixtures assuming only 1 boundary length
//Returns: new temperature of the sharer
/datum/gas_mixture/proc/compare(datum/gas_mixture/sample)
//Compares sample to self to see if within acceptable ranges that group processing may be enabled
//Returns: a string indicating what check failed, or "" if check passes
/datum/gas_mixture/archive()
var/list/cached_gases = gases
temperature_archived = temperature
for(var/id in cached_gases)
cached_gases[id][ARCHIVE] = cached_gases[id][MOLES]
return 1
/datum/gas_mixture/merge(datum/gas_mixture/giver)
if(!giver)
return 0
//heat transfer
if(abs(temperature - giver.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/self_heat_capacity = heat_capacity()
var/giver_heat_capacity = giver.heat_capacity()
var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity
if(combined_heat_capacity)
temperature = (giver.temperature * giver_heat_capacity + temperature * self_heat_capacity) / combined_heat_capacity
var/list/cached_gases = gases //accessing datum vars is slower than proc vars
var/list/giver_gases = giver.gases
//gas transfer
for(var/giver_id in giver_gases)
assert_gas(giver_id)
cached_gases[giver_id][MOLES] += giver_gases[giver_id][MOLES]
return 1
/datum/gas_mixture/remove(amount)
var/sum = total_moles()
amount = min(amount, sum) //Can not take more air than tile has!
if(amount <= 0)
return null
var/list/cached_gases = gases
var/datum/gas_mixture/removed = new
var/list/removed_gases = removed.gases //accessing datum vars is slower than proc vars
removed.temperature = temperature
for(var/id in cached_gases)
removed.add_gas(id)
removed_gases[id][MOLES] = QUANTIZE((cached_gases[id][MOLES] / sum) * amount)
cached_gases[id][MOLES] -= removed_gases[id][MOLES]
garbage_collect()
return removed
/datum/gas_mixture/remove_ratio(ratio)
if(ratio <= 0)
return null
ratio = min(ratio, 1)
var/list/cached_gases = gases
var/datum/gas_mixture/removed = new
var/list/removed_gases = removed.gases //accessing datum vars is slower than proc vars
removed.temperature = temperature
for(var/id in cached_gases)
removed.add_gas(id)
removed_gases[id][MOLES] = QUANTIZE(cached_gases[id][MOLES] * ratio)
cached_gases[id][MOLES] -= removed_gases[id][MOLES]
garbage_collect()
return removed
/datum/gas_mixture/copy()
var/list/cached_gases = gases
var/datum/gas_mixture/copy = new
var/list/copy_gases = copy.gases
copy.temperature = temperature
for(var/id in cached_gases)
add_gas(id)
copy_gases[id][MOLES] = cached_gases[id][MOLES]
return copy
/datum/gas_mixture/copy_from(datum/gas_mixture/sample)
var/list/cached_gases = gases //accessing datum vars is slower than proc vars
var/list/sample_gases = sample.gases
temperature = sample.temperature
for(var/id in sample_gases)
assert_gas(id)
cached_gases[id][MOLES] = sample_gases[id][MOLES]
//remove all gases not in the sample
cached_gases &= sample_gases
return 1
/datum/gas_mixture/copy_from_turf(turf/model)
parse_gas_string(model.initial_gas_mix)
//acounts for changes in temperature
var/turf/model_parent = model.parent_type
if(model.temperature != initial(model.temperature) || model.temperature != initial(model_parent.temperature))
temperature = model.temperature
return 1
/datum/gas_mixture/parse_gas_string(gas_string)
var/list/gases = src.gases
var/list/gas = params2list(gas_string)
if(gas["TEMP"])
temperature = text2num(gas["TEMP"])
gas -= "TEMP"
gases.Cut()
for(var/id in gas)
add_gas(id)
gases[id][MOLES] = text2num(gas[id])
return 1
/datum/gas_mixture/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
if(!sharer)
return 0
var/list/cached_gases = gases
var/list/sharer_gases = sharer.gases
var/temperature_delta = temperature_archived - sharer.temperature_archived
var/abs_temperature_delta = abs(temperature_delta)
var/old_self_heat_capacity = 0
var/old_sharer_heat_capacity = 0
if(abs_temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
old_self_heat_capacity = heat_capacity()
old_sharer_heat_capacity = sharer.heat_capacity()
var/heat_capacity_self_to_sharer = 0 //heat capacity of the moles transferred from us to the sharer
var/heat_capacity_sharer_to_self = 0 //heat capacity of the moles transferred from the sharer to us
var/moved_moles = 0
var/abs_moved_moles = 0
//GAS TRANSFER
for(var/id in sharer_gases - cached_gases) // create gases not in our cache
add_gas(id)
for(var/id in cached_gases) // transfer gases
if(!sharer_gases[id]) //checking here prevents an uneeded proc call if the check fails.
sharer.add_gas(id)
var/gas = cached_gases[id]
var/sharergas = sharer_gases[id]
var/delta = QUANTIZE(gas[ARCHIVE] - sharergas[ARCHIVE])/(atmos_adjacent_turfs+1) //the amount of gas that gets moved between the mixtures
if(delta && abs_temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/gas_heat_capacity = delta * gas[GAS_META][META_GAS_SPECIFIC_HEAT]
if(delta > 0)
heat_capacity_self_to_sharer += gas_heat_capacity
else
heat_capacity_sharer_to_self -= gas_heat_capacity //subtract here instead of adding the absolute value because we know that delta is negative. saves a proc call.
gas[MOLES] -= delta
sharergas[MOLES] += delta
moved_moles += delta
abs_moved_moles += abs(delta)
last_share = abs_moved_moles
//THERMAL ENERGY TRANSFER
if(abs_temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/new_self_heat_capacity = old_self_heat_capacity + heat_capacity_sharer_to_self - heat_capacity_self_to_sharer
var/new_sharer_heat_capacity = old_sharer_heat_capacity + heat_capacity_self_to_sharer - heat_capacity_sharer_to_self
//transfer of thermal energy (via changed heat capacity) between self and sharer
if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY)
temperature = (old_self_heat_capacity*temperature - heat_capacity_self_to_sharer*temperature_archived + heat_capacity_sharer_to_self*sharer.temperature_archived)/new_self_heat_capacity
if(new_sharer_heat_capacity > MINIMUM_HEAT_CAPACITY)
sharer.temperature = (old_sharer_heat_capacity*sharer.temperature-heat_capacity_sharer_to_self*sharer.temperature_archived + heat_capacity_self_to_sharer*temperature_archived)/new_sharer_heat_capacity
//thermal energy of the system (self and sharer) is unchanged
if(abs(old_sharer_heat_capacity) > MINIMUM_HEAT_CAPACITY)
if(abs(new_sharer_heat_capacity/old_sharer_heat_capacity - 1) < 0.10) // <10% change in sharer heat capacity
temperature_share(sharer, OPEN_HEAT_TRANSFER_COEFFICIENT)
var/list/unique_gases = cached_gases ^ sharer_gases
if(unique_gases.len) //if all gases were present in both mixtures, we know that no gases are 0
garbage_collect(cached_gases - sharer_gases) //any gases the sharer had, we are guaranteed to have. gases that it didn't have we are not.
sharer.garbage_collect(sharer_gases - cached_gases) //the reverse is equally true
sharer.after_share(src, atmos_adjacent_turfs)
if(temperature_delta > MINIMUM_TEMPERATURE_TO_MOVE || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
var/delta_pressure = temperature_archived*(total_moles() + moved_moles) - sharer.temperature_archived*(sharer.total_moles() - moved_moles)
return delta_pressure * R_IDEAL_GAS_EQUATION / volume
/datum/gas_mixture/after_share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
return
/datum/gas_mixture/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity)
//transfer of thermal energy (via conduction) between self and sharer
if(sharer)
sharer_temperature = sharer.temperature_archived
var/temperature_delta = temperature_archived - sharer_temperature
if(abs(temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/self_heat_capacity = heat_capacity_archived()
sharer_heat_capacity = sharer_heat_capacity || sharer.heat_capacity_archived()
if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
var/heat = conduction_coefficient*temperature_delta* \
(self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity))
temperature = max(temperature - heat/self_heat_capacity, TCMB)
sharer_temperature = max(sharer_temperature + heat/sharer_heat_capacity, TCMB)
if(sharer)
sharer.temperature = sharer_temperature
return sharer_temperature
//thermal energy of the system (self and sharer) is unchanged
/datum/gas_mixture/compare(datum/gas_mixture/sample, datatype = MOLES, adjacents = 0)
var/list/sample_gases = sample.gases //accessing datum vars is slower than proc vars
var/list/cached_gases = gases
for(var/id in cached_gases | sample_gases) // compare gases from either mixture
var/gas_moles = cached_gases[id] ? cached_gases[id][datatype] : 0
var/sample_moles = sample_gases[id] ? sample_gases[id][datatype] : 0
var/delta = abs(gas_moles - sample_moles)/(adjacents+1)
if(delta > MINIMUM_MOLES_DELTA_TO_MOVE && \
delta > gas_moles * MINIMUM_AIR_RATIO_TO_MOVE)
return id
if(total_moles() > MINIMUM_MOLES_DELTA_TO_MOVE)
var/temp
var/sample_temp
switch(datatype)
if(MOLES)
temp = temperature
sample_temp = sample.temperature
if(ARCHIVE)
temp = temperature_archived
sample_temp = sample.temperature_archived
var/temperature_delta = abs(temp - sample_temp)
if((temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && \
temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND * temp)
return "temp"
return ""
//Takes the amount of the gas you want to PP as an argument
//So I don't have to do some hacky switches/defines/magic strings
//eg:
//Tox_PP = get_partial_pressure(gas_mixture.toxins)
//O2_PP = get_partial_pressure(gas_mixture.oxygen)
//Does handle trace gases!
/datum/gas_mixture/proc/get_breath_partial_pressure(gas_pressure)
return (gas_pressure * R_IDEAL_GAS_EQUATION * temperature) / BREATH_VOLUME
//inverse
/datum/gas_mixture/proc/get_true_breath_pressure(partial_pressure)
return (partial_pressure * BREATH_VOLUME) / (R_IDEAL_GAS_EQUATION * temperature)
//Mathematical proofs:
/*
get_breath_partial_pressure(gas_pp) --> gas_pp/total_moles()*breath_pp = pp
get_true_breath_pressure(pp) --> gas_pp = pp/breath_pp*total_moles()
10/20*5 = 2.5
10 = 2.5/5*20
*/
@@ -0,0 +1,84 @@
var/list/hardcoded_gases = list("o2","n2","co2","plasma") //the main four gases, which were at one time hardcoded
/proc/meta_gas_list()
. = new /list
for(var/gas_path in subtypesof(/datum/gas))
var/list/gas_info = new(4)
var/datum/gas/gas = gas_path
gas_info[META_GAS_SPECIFIC_HEAT] = initial(gas.specific_heat)
gas_info[META_GAS_NAME] = initial(gas.name)
gas_info[META_GAS_MOLES_VISIBLE] = initial(gas.moles_visible)
if(initial(gas.moles_visible) != null)
gas_info[META_GAS_OVERLAY] = new /obj/effect/overlay/gas(initial(gas.gas_overlay))
.[initial(gas.id)] = gas_info
/*||||||||||||||/----------\||||||||||||||*\
||||||||||||||||[GAS DATUMS]||||||||||||||||
||||||||||||||||\__________/||||||||||||||||
||||These should never be instantiated. ||||
||||They exist only to make it easier ||||
||||to add a new gas. They are accessed ||||
||||only by meta_gas_list(). ||||
\*||||||||||||||||||||||||||||||||||||||||*/
/datum/gas
var/id = ""
var/specific_heat = 0
var/name = ""
var/gas_overlay = "" //icon_state in icons/effects/tile_effects.dmi
var/moles_visible = null
/datum/gas/oxygen
id = "o2"
specific_heat = 20
name = "Oxygen"
/datum/gas/nitrogen
id = "n2"
specific_heat = 20
name = "Nitrogen"
/datum/gas/carbon_dioxide //what the fuck is this?
id = "co2"
specific_heat = 30
name = "Carbon Dioxide"
/datum/gas/plasma
id = "plasma"
specific_heat = 200
name = "Plasma"
gas_overlay = "plasma"
moles_visible = MOLES_PLASMA_VISIBLE
/datum/gas/nitrous_oxide
id = "n2o"
specific_heat = 40
name = "Nitrous Oxide"
gas_overlay = "nitrous_oxide"
moles_visible = 1
/datum/gas/oxygen_agent_b
id = "agent_b"
specific_heat = 300
name = "Oxygen Agent B"
/datum/gas/volatile_fuel
id = "v_fuel"
specific_heat = 30
name = "Volatile Fuel"
/datum/gas/bz
id = "bz"
specific_heat = 20
name = "BZ"
/obj/effect/overlay/gas/
icon = 'icons/effects/tile_effects.dmi'
mouse_opacity = 0
layer = FLY_LAYER
appearance_flags = RESET_COLOR|TILE_BOUND
/obj/effect/overlay/gas/New(state)
. = ..()
icon_state = state
@@ -0,0 +1,61 @@
//"immutable" gas mixture used for space calculations
//it can be changed, but any changes will ultimately be undone before they can have any effect
/datum/gas_mixture/space
/datum/gas_mixture/space/New()
..()
temperature = TCMB
temperature_archived = TCMB
/datum/gas_mixture/space/garbage_collect()
gases.Cut() //clever way of ensuring we always are empty.
/datum/gas_mixture/space/archive()
return 1 //nothing changes, so we do nothing and the archive is successful
/datum/gas_mixture/space/merge()
return 0 //we're immutable.
/datum/gas_mixture/space/heat_capacity()
. = 7000
/datum/gas_mixture/space/heat_capacity_archived()
. = heat_capacity()
/datum/gas_mixture/space/remove()
return copy() //we're immutable, so we can just return a copy.
/datum/gas_mixture/space/remove_ratio()
return copy() //we're immutable, so we can just return a copy.
/datum/gas_mixture/space/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
. = ..(sharer, 0)
temperature = TCMB
gases.Cut()
/datum/gas_mixture/space/after_share()
temperature = TCMB
gases.Cut()
/datum/gas_mixture/space/react()
return 0 //we're immutable.
/datum/gas_mixture/space/fire()
return 0 //we're immutable.
/datum/gas_mixture/space/copy()
return new /datum/gas_mixture/space //we're immutable, so we can just return a new instance.
/datum/gas_mixture/space/copy_from()
return 0 //we're immutable.
/datum/gas_mixture/space/copy_from_turf()
return 0 //we're immutable.
/datum/gas_mixture/space/parse_gas_string()
return 0 //we're immutable.
/datum/gas_mixture/space/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity)
. = ..()
temperature = TCMB
@@ -0,0 +1,702 @@
/datum/tlv
var/min2
var/min1
var/max1
var/max2
/datum/tlv/New(min2 as num, min1 as num, max1 as num, max2 as num)
src.min2 = min2
src.min1 = min1
src.max1 = max1
src.max2 = max2
/datum/tlv/proc/get_danger_level(val as num)
if(max2 != -1 && val >= max2)
return 2
if(min2 != -1 && val <= min2)
return 2
if(max1 != -1 && val >= max1)
return 1
if(min1 != -1 && val <= min1)
return 1
return 0
/obj/item/weapon/electronics/airalarm
name = "air alarm electronics"
icon_state = "airalarm_electronics"
/obj/item/wallframe/airalarm
name = "air alarm frame"
desc = "Used for building Air Alarms"
icon = 'icons/obj/monitors.dmi'
icon_state = "alarm_bitem"
result_path = /obj/machinery/airalarm
#define AALARM_MODE_SCRUBBING 1
#define AALARM_MODE_VENTING 2 //makes draught
#define AALARM_MODE_PANIC 3 //like siphon, but stronger (enables widenet)
#define AALARM_MODE_REPLACEMENT 4 //sucks off all air, then refill and swithes to scrubbing
#define AALARM_MODE_OFF 5
#define AALARM_MODE_FLOOD 6 //Emagged mode; turns off scrubbers and pressure checks on vents
#define AALARM_MODE_SIPHON 7 //Scrubbers suck air
#define AALARM_MODE_CONTAMINATED 8 //Turns on all filtering and widenet scrubbing.
#define AALARM_MODE_REFILL 9 //just like normal, but with triple the air output
#define AALARM_REPORT_TIMEOUT 100
/obj/machinery/airalarm
name = "air alarm"
desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous."
icon = 'icons/obj/monitors.dmi'
icon_state = "alarm0"
anchored = 1
use_power = 1
idle_power_usage = 4
active_power_usage = 8
power_channel = ENVIRON
req_access = list(access_atmospherics)
var/danger_level = 0
var/mode = AALARM_MODE_SCRUBBING
var/locked = 1
var/aidisabled = 0
var/shorted = 0
var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
var/frequency = 1439
var/alarm_frequency = 1437
var/datum/radio_frequency/radio_connection
var/list/TLV = list( // Breathable air.
"pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE* 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20), // kPa
"temperature" = new/datum/tlv(T0C, T0C+10, T0C+40, T0C+66), // K
"o2" = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa
"n2" = new/datum/tlv(-1, -1, 1000, 1000), // Partial pressure, kpa
"co2" = new/datum/tlv(-1, -1, 5, 10), // Partial pressure, kpa
"plasma" = new/datum/tlv(-1, -1, 0.2, 0.5), // Partial pressure, kpa
"n2o" = new/datum/tlv(-1, -1, 0.2, 0.5), // Partial pressure, kpa
"bz" = new/datum/tlv(-1, -1, 0.2, 0.5)
)
/obj/machinery/airalarm/server // No checks here.
TLV = list(
"pressure" = new/datum/tlv(-1, -1, -1, -1),
"temperature" = new/datum/tlv(-1, -1, -1, -1),
"o2" = new/datum/tlv(-1, -1, -1, -1),
"n2" = new/datum/tlv(-1, -1, -1, -1),
"co2" = new/datum/tlv(-1, -1, -1, -1),
"plasma" = new/datum/tlv(-1, -1, -1, -1),
"n2o" = new/datum/tlv(-1, -1, -1, -1),
"bz" = new/datum/tlv(-1, -1, -1, -1),
)
/obj/machinery/airalarm/kitchen_cold_room // Copypasta: to check temperatures.
TLV = list(
"pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE* 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20), // kPa
"temperature" = new/datum/tlv(200,210,273.15,283.15), // K
"o2" = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa
"n2" = new/datum/tlv(-1, -1, 1000, 1000), // Partial pressure, kpa
"co2" = new/datum/tlv(-1, -1, 5, 10), // Partial pressure, kpa
"plasma" = new/datum/tlv(-1, -1, 0.2, 0.5), // Partial pressure, kpa
"n2o" = new/datum/tlv(-1, -1, 0.2, 0.5), // Partial pressure, kpa
"bz" = new/datum/tlv(-1, -1, 0.2, 0.5), // Partial pressure, kpa
)
//all air alarms in area are connected via magic
/area
var/list/air_vent_names = list()
var/list/air_scrub_names = list()
var/list/air_vent_info = list()
var/list/air_scrub_info = list()
/obj/machinery/airalarm/New(loc, ndir, nbuild)
..()
wires = new /datum/wires/airalarm(src)
if(ndir)
setDir(ndir)
if(nbuild)
buildstage = 0
panel_open = 1
pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
pixel_y = (dir & 3)? (dir == 1 ? -24 : 24) : 0
var/area/A = get_area_master(src)
if(name == initial(name))
name = "[A.name] Air Alarm"
update_icon()
if(ticker && ticker.current_state == 3)//if the game is running
initialize()
/obj/machinery/airalarm/Destroy()
if(SSradio)
SSradio.remove_object(src, frequency)
qdel(wires)
wires = null
return ..()
/obj/machinery/airalarm/initialize()
set_frequency(frequency)
/obj/machinery/airalarm/ui_status(mob/user)
if(user.has_unlimited_silicon_privilege && aidisabled)
user << "AI control has been disabled."
else if(!shorted)
return ..()
return UI_CLOSE
/obj/machinery/airalarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
datum/tgui/master_ui = null, datum/ui_state/state = default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "airalarm", name, 440, 650, master_ui, state)
ui.open()
/obj/machinery/airalarm/ui_data(mob/user)
var/data = list(
"locked" = locked,
"siliconUser" = user.has_unlimited_silicon_privilege,
"emagged" = emagged,
"danger_level" = danger_level,
)
var/area/A = get_area(src)
data["atmos_alarm"] = A.atmosalm
data["fire_alarm"] = A.fire
var/turf/T = get_turf(src)
var/datum/gas_mixture/environment = T.return_air()
var/datum/tlv/cur_tlv
data["environment_data"] = list()
var/pressure = environment.return_pressure()
cur_tlv = TLV["pressure"]
data["environment_data"] += list(list(
"name" = "Pressure",
"value" = pressure,
"unit" = "kPa",
"danger_level" = cur_tlv.get_danger_level(pressure)
))
var/temperature = environment.temperature
cur_tlv = TLV["temperature"]
data["environment_data"] += list(list(
"name" = "Temperature",
"value" = temperature,
"unit" = "K ([round(temperature - T0C, 0.1)]C)",
"danger_level" = cur_tlv.get_danger_level(temperature)
))
var/total_moles = environment.total_moles()
var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume
for(var/gas_id in environment.gases)
if(!(gas_id in TLV)) // We're not interested in this gas, it seems.
continue
cur_tlv = TLV[gas_id]
data["environment_data"] += list(list(
"name" = environment.gases[gas_id][GAS_META][META_GAS_NAME],
"value" = environment.gases[gas_id][MOLES] / total_moles * 100,
"unit" = "%",
"danger_level" = cur_tlv.get_danger_level(environment.gases[gas_id][MOLES] * partial_pressure)
))
if(!locked || user.has_unlimited_silicon_privilege)
data["vents"] = list()
for(var/id_tag in A.air_vent_names)
var/long_name = A.air_vent_names[id_tag]
var/list/info = A.air_vent_info[id_tag]
if(!info || info["frequency"] != frequency)
continue
data["vents"] += list(list(
"id_tag" = id_tag,
"long_name" = sanitize(long_name),
"power" = info["power"],
"checks" = info["checks"],
"excheck" = info["checks"]&1,
"incheck" = info["checks"]&2,
"direction" = info["direction"],
"external" = info["external"],
"extdefault"= (info["external"] == ONE_ATMOSPHERE)
))
data["scrubbers"] = list()
for(var/id_tag in A.air_scrub_names)
var/long_name = A.air_scrub_names[id_tag]
var/list/info = A.air_scrub_info[id_tag]
if(!info || info["frequency"] != frequency)
continue
data["scrubbers"] += list(list(
"id_tag" = id_tag,
"long_name" = sanitize(long_name),
"power" = info["power"],
"scrubbing" = info["scrubbing"],
"widenet" = info["widenet"],
"filter_co2" = info["filter_co2"],
"filter_toxins" = info["filter_toxins"],
"filter_n2o" = info["filter_n2o"],
"filter_bz" = info["filter_bz"]
))
data["mode"] = mode
data["modes"] = list()
data["modes"] += list(list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0))
data["modes"] += list(list("name" = "Contaminated - Scrubs out ALL contaminants quickly","mode" = AALARM_MODE_CONTAMINATED, "selected" = mode == AALARM_MODE_CONTAMINATED, "danger" = 0))
data["modes"] += list(list("name" = "Draught - Siphons out air while replacing", "mode" = AALARM_MODE_VENTING, "selected" = mode == AALARM_MODE_VENTING, "danger" = 0))
data["modes"] += list(list("name" = "Refill - Triple vent output", "mode" = AALARM_MODE_REFILL, "selected" = mode == AALARM_MODE_REFILL, "danger" = 0))
data["modes"] += list(list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 1))
data["modes"] += list(list("name" = "Siphon - Siphons air out of the room", "mode" = AALARM_MODE_SIPHON, "selected" = mode == AALARM_MODE_SIPHON, "danger" = 1))
data["modes"] += list(list("name" = "Panic Siphon - Siphons air out of the room quickly","mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1))
data["modes"] += list(list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0))
if(emagged)
data["modes"] += list(list("name" = "Flood - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FLOOD, "selected" = mode == AALARM_MODE_FLOOD, "danger" = 1))
var/datum/tlv/selected
var/list/thresholds = list()
selected = TLV["pressure"]
thresholds += list(list("name" = "Pressure", "settings" = list()))
thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2))
thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1))
thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1))
thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2))
selected = TLV["temperature"]
thresholds += list(list("name" = "Temperature", "settings" = list()))
thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2))
thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1))
thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1))
thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2))
for(var/gas_id in meta_gas_info)
if(!(gas_id in TLV)) // We're not interested in this gas, it seems.
continue
selected = TLV[gas_id]
thresholds += list(list("name" = meta_gas_info[gas_id][META_GAS_NAME], "settings" = list()))
thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min2", "selected" = selected.min2))
thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min1", "selected" = selected.min1))
thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max1", "selected" = selected.max1))
thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max2", "selected" = selected.max2))
data["thresholds"] = thresholds
return data
/obj/machinery/airalarm/ui_act(action, params)
if(..() || buildstage != 2)
return
if((locked && !usr.has_unlimited_silicon_privilege) || (usr.has_unlimited_silicon_privilege && aidisabled))
return
var/device_id = params["id_tag"]
switch(action)
if("lock")
if(usr.has_unlimited_silicon_privilege && !wires.is_cut(WIRE_IDSCAN))
locked = !locked
. = TRUE
if("power", "co2_scrub", "tox_scrub", "n2o_scrub", "bz_scrub", "widenet", "scrubbing")
send_signal(device_id, list("[action]" = text2num(params["val"])))
. = TRUE
if("excheck")
send_signal(device_id, list("checks" = text2num(params["val"])^1))
. = TRUE
if("incheck")
send_signal(device_id, list("checks" = text2num(params["val"])^2))
. = TRUE
if("set_external_pressure")
var/area/A = get_area_master(src)
var/target = input("New target pressure:", name, A.air_vent_info[device_id]["external"]) as num|null
if(!isnull(target) && !..())
send_signal(device_id, list("set_external_pressure" = target))
. = TRUE
if("reset_external_pressure")
send_signal(device_id, list("reset_external_pressure"))
. = TRUE
if("threshold")
var/env = params["env"]
var/name = params["var"]
var/datum/tlv/tlv = TLV[env]
if(isnull(tlv))
return
var/value = input("New [name] for [env]:", name, tlv.vars[name]) as num|null
if(!isnull(value) && !..())
if(value < 0)
tlv.vars[name] = -1
else
tlv.vars[name] = round(value, 0.01)
. = TRUE
if("mode")
mode = text2num(params["mode"])
apply_mode()
. = TRUE
if("alarm")
var/area/A = get_area_master(src)
if(A.atmosalert(2, src))
post_alert(2)
. = TRUE
if("reset")
var/area/A = get_area_master(src)
if(A.atmosalert(0, src))
post_alert(0)
. = TRUE
update_icon()
/obj/machinery/airalarm/proc/reset(wire)
switch(wire)
if(WIRE_POWER)
if(!wires.is_cut(WIRE_POWER))
shorted = FALSE
update_icon()
if(WIRE_AI)
if(!wires.is_cut(WIRE_AI))
aidisabled = FALSE
/obj/machinery/airalarm/proc/shock(mob/user, prb)
if((stat & (NOPOWER))) // unpowered, no shock
return 0
if(!prob(prb))
return 0 //you lucked out, no shock for you
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(5, 1, src)
s.start() //sparks always.
if (electrocute_mob(user, get_area(src), src))
return 1
else
return 0
/obj/machinery/airalarm/proc/refresh_all()
var/area/A = get_area_master(src)
for(var/id_tag in A.air_vent_names)
var/list/I = A.air_vent_info[id_tag]
if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time)
continue
send_signal(id_tag, list("status"))
for(var/id_tag in A.air_scrub_names)
var/list/I = A.air_scrub_info[id_tag]
if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time)
continue
send_signal(id_tag, list("status"))
/obj/machinery/airalarm/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM)
/obj/machinery/airalarm/proc/send_signal(target, list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise
if(!radio_connection)
return 0
var/datum/signal/signal = new
signal.transmission_method = 1 //radio signal
signal.source = src
signal.data = command
signal.data["tag"] = target
signal.data["sigtype"] = "command"
radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM)
// world << text("Signal [] Broadcasted to []", command, target)
return 1
/obj/machinery/airalarm/proc/apply_mode()
var/area/A = get_area_master(src)
switch(mode)
if(AALARM_MODE_SCRUBBING)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 1,
"co2_scrub" = 1,
"tox_scrub" = 0,
"n2o_scrub" = 0,
"bz_scrub" = 0,
"scrubbing" = 1,
"widenet" = 0,
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE
))
if(AALARM_MODE_CONTAMINATED)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 1,
"co2_scrub" = 1,
"tox_scrub" = 1,
"n2o_scrub" = 1,
"bz_scrub" = 1,
"scrubbing" = 1,
"widenet" = 1,
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE
))
if(AALARM_MODE_VENTING)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 1,
"widenet" = 0,
"scrubbing" = 0
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE*2
))
if(AALARM_MODE_REFILL)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 1,
"co2_scrub" = 1,
"tox_scrub" = 0,
"n2o_scrub" = 0,
"bz_scrub" = 0,
"scrubbing" = 1,
"widenet" = 0,
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE * 3
))
if(AALARM_MODE_PANIC,
AALARM_MODE_REPLACEMENT)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 1,
"widenet" = 1,
"scrubbing" = 0
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 0
))
if(AALARM_MODE_SIPHON)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 1,
"widenet" = 0,
"scrubbing" = 0
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 0
))
if(AALARM_MODE_OFF)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 0
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 0
))
if(AALARM_MODE_FLOOD)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 0
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 1,
"checks" = 2,
"set_internal_pressure" = 0
))
/obj/machinery/airalarm/update_icon()
if(panel_open)
switch(buildstage)
if(2)
icon_state = "alarmx"
if(1)
icon_state = "alarm_b2"
if(0)
icon_state = "alarm_b1"
return
if((stat & (NOPOWER|BROKEN)) || shorted)
icon_state = "alarmp"
return
var/area/A = get_area_master(src)
switch(max(danger_level, A.atmosalm))
if(0)
icon_state = "alarm0"
if(1)
icon_state = "alarm2" //yes, alarm2 is yellow alarm
if(2)
icon_state = "alarm1"
/obj/machinery/airalarm/process()
if((stat & (NOPOWER|BROKEN)) || shorted)
return
var/turf/location = get_turf(src)
if(!location)
return
var/datum/tlv/cur_tlv
var/datum/gas_mixture/environment = location.return_air()
var/list/env_gases = environment.gases
var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume
cur_tlv = TLV["pressure"]
var/environment_pressure = environment.return_pressure()
var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)
cur_tlv = TLV["temperature"]
var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature)
var/gas_dangerlevel = 0
for(var/gas_id in env_gases)
if(!(gas_id in TLV)) // We're not interested in this gas, it seems.
continue
cur_tlv = TLV[gas_id]
gas_dangerlevel = max(gas_dangerlevel, cur_tlv.get_danger_level(env_gases[gas_id][MOLES] * partial_pressure))
environment.garbage_collect()
var/old_danger_level = danger_level
danger_level = max(pressure_dangerlevel, temperature_dangerlevel, gas_dangerlevel)
if(old_danger_level != danger_level)
apply_danger_level()
if(mode == AALARM_MODE_REPLACEMENT && environment_pressure < ONE_ATMOSPHERE * 0.05)
mode = AALARM_MODE_SCRUBBING
apply_mode()
/obj/machinery/airalarm/proc/post_alert(alert_level)
var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency)
if(!frequency)
return
var/area/A = get_area_master(src)
var/datum/signal/alert_signal = new
alert_signal.source = src
alert_signal.transmission_method = 1
alert_signal.data["zone"] = A.name
alert_signal.data["type"] = "Atmospheric"
if(alert_level==2)
alert_signal.data["alert"] = "severe"
else if (alert_level==1)
alert_signal.data["alert"] = "minor"
else if (alert_level==0)
alert_signal.data["alert"] = "clear"
frequency.post_signal(src, alert_signal,null,-1)
/obj/machinery/airalarm/proc/apply_danger_level()
var/area/A = get_area_master(src)
var/new_area_danger_level = 0
for(var/area/R in A.related)
for(var/obj/machinery/airalarm/AA in R)
if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted)
new_area_danger_level = max(new_area_danger_level,AA.danger_level)
if(A.atmosalert(new_area_danger_level,src)) //if area was in normal state or if area was in alert state
post_alert(new_area_danger_level)
update_icon()
/obj/machinery/airalarm/attackby(obj/item/W, mob/user, params)
switch(buildstage)
if(2)
if(istype(W, /obj/item/weapon/wirecutters) && panel_open && wires.is_all_cut())
playsound(src.loc, 'sound/items/Wirecutter.ogg', 50, 1)
user << "<span class='notice'>You cut the final wires.</span>"
var/obj/item/stack/cable_coil/cable = new /obj/item/stack/cable_coil(loc)
cable.amount = 5
buildstage = 1
update_icon()
return
else if(istype(W, /obj/item/weapon/screwdriver)) // Opening that Air Alarm up.
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
panel_open = !panel_open
user << "<span class='notice'>The wires have been [panel_open ? "exposed" : "unexposed"].</span>"
update_icon()
return
else if(istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card
if(stat & (NOPOWER|BROKEN))
user << "<span class='warning'>It does nothing!</span>"
else
if(src.allowed(usr) && !wires.is_cut(WIRE_IDSCAN))
locked = !locked
user << "<span class='notice'>You [ locked ? "lock" : "unlock"] the air alarm interface.</span>"
else
user << "<span class='danger'>Access denied.</span>"
return
else if(panel_open && is_wire_tool(W))
wires.interact(user)
return
if(1)
if(istype(W, /obj/item/weapon/crowbar))
user.visible_message("[user.name] removes the electronics from [src.name].",\
"<span class='notice'>You start prying out the circuit...</span>")
playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1)
if (do_after(user, 20/W.toolspeed, target = src))
if (buildstage == 1)
user <<"<span class='notice'>You remove the air alarm electronics.</span>"
new /obj/item/weapon/electronics/airalarm( src.loc )
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
buildstage = 0
update_icon()
return
if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/cable = W
if(cable.get_amount() < 5)
user << "<span class='warning'>You need five lengths of cable to wire the fire alarm!</span>"
return
user.visible_message("[user.name] wires the air alarm.", \
"<span class='notice'>You start wiring the air alarm...</span>")
if (do_after(user, 20, target = src))
if (cable.get_amount() >= 5 && buildstage == 1)
cable.use(5)
user << "<span class='notice'>You wire the air alarm.</span>"
wires.repair()
aidisabled = 0
locked = 1
mode = 1
shorted = 0
post_alert(0)
buildstage = 2
update_icon()
return
if(0)
if(istype(W, /obj/item/weapon/electronics/airalarm))
if(user.unEquip(W))
user << "<span class='notice'>You insert the circuit.</span>"
buildstage = 1
update_icon()
qdel(W)
return
if(istype(W, /obj/item/weapon/wrench))
user << "<span class='notice'>You detach \the [src] from the wall.</span>"
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
new /obj/item/wallframe/airalarm( user.loc )
qdel(src)
return
return ..()
/obj/machinery/airalarm/power_change()
..()
update_icon()
/obj/machinery/airalarm/emag_act(mob/user)
if(emagged)
return
emagged = TRUE
visible_message("<span class='warning'>Sparks fly out of the [src]!</span>", "<span class='notice'>You emag the [src], disabling its safeties.</span>")
playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1)
@@ -0,0 +1,290 @@
/*
Quick overview:
Pipes combine to form pipelines
Pipelines and other atmospheric objects combine to form pipe_networks
Note: A single pipe_network represents a completely open space
Pipes -> Pipelines
Pipelines + Other Objects -> Pipe network
*/
/obj/machinery/atmospherics
anchored = 1
idle_power_usage = 0
active_power_usage = 0
power_channel = ENVIRON
on_blueprints = TRUE
layer = GAS_PIPE_LAYER //under wires
var/nodealert = 0
var/can_unwrench = 0
var/initialize_directions = 0
var/pipe_color
var/obj/item/pipe/stored
var/global/list/iconsetids = list()
var/global/list/pipeimages = list()
var/image/pipe_vision_img = null
var/device_type = 0
var/list/obj/machinery/atmospherics/nodes = list()
/obj/machinery/atmospherics/New(loc, process = TRUE)
nodes.len = device_type
..()
if(process)
SSair.atmos_machinery += src
SetInitDirections()
if(can_unwrench)
stored = new(src, make_from=src)
/obj/machinery/atmospherics/Destroy()
for(DEVICE_TYPE_LOOP)
nullifyNode(I)
SSair.atmos_machinery -= src
if(stored)
qdel(stored)
stored = null
dropContents()
if(pipe_vision_img)
qdel(pipe_vision_img)
return ..()
//return QDEL_HINT_FINDREFERENCE
/obj/machinery/atmospherics/proc/nullifyNode(I)
if(NODE_I)
var/obj/machinery/atmospherics/N = NODE_I
N.disconnect(src)
NODE_I = null
//this is called just after the air controller sets up turfs
/obj/machinery/atmospherics/proc/atmosinit(var/list/node_connects)
if(!node_connects) //for pipes where order of nodes doesn't matter
node_connects = list()
node_connects.len = device_type
for(DEVICE_TYPE_LOOP)
for(var/D in cardinal)
if(D & GetInitDirections())
if(D in node_connects)
continue
node_connects[I] = D
break
for(DEVICE_TYPE_LOOP)
for(var/obj/machinery/atmospherics/target in get_step(src,node_connects[I]))
if(can_be_node(target, I))
NODE_I = target
break
update_icon()
/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target)
if(target.initialize_directions & get_dir(target,src))
return 1
/obj/machinery/atmospherics/proc/pipeline_expansion()
return nodes
/obj/machinery/atmospherics/proc/SetInitDirections()
return
/obj/machinery/atmospherics/proc/GetInitDirections()
return initialize_directions
/obj/machinery/atmospherics/proc/returnPipenet()
return
/obj/machinery/atmospherics/proc/returnPipenetAir()
return
/obj/machinery/atmospherics/proc/setPipenet()
return
/obj/machinery/atmospherics/proc/replacePipenet()
return
/obj/machinery/atmospherics/proc/build_network()
// Called to build a network from this node
return
/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
if(istype(reference, /obj/machinery/atmospherics/pipe))
var/obj/machinery/atmospherics/pipe/P = reference
qdel(P.parent)
var/I = nodes.Find(reference)
NODE_I = null
update_icon()
/obj/machinery/atmospherics/update_icon()
return
/obj/machinery/atmospherics/attackby(obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/weapon/wrench))
if(can_unwrench(user))
var/turf/T = get_turf(src)
if (level==1 && isturf(T) && T.intact)
user << "<span class='warning'>You must remove the plating first!</span>"
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
add_fingerprint(user)
var/unsafe_wrenching = FALSE
var/internal_pressure = int_air.return_pressure()-env_air.return_pressure()
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (internal_pressure > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?</span>"
unsafe_wrenching = TRUE //Oh dear oh dear
if (do_after(user, 20/W.toolspeed, target = src) && !qdeleted(src))
user.visible_message( \
"[user] unfastens \the [src].", \
"<span class='notice'>You unfasten \the [src].</span>", \
"<span class='italics'>You hear ratchet.</span>")
investigate_log("was <span class='warning'>REMOVED</span> by [key_name(usr)]", "atmos")
//You unwrenched a pipe full of pressure? Let's splat you into the wall, silly.
if(unsafe_wrenching)
unsafe_pressure_release(user, internal_pressure)
Deconstruct()
else
return ..()
/obj/machinery/atmospherics/proc/can_unwrench(mob/user)
return can_unwrench
// Throws the user when they unwrench a pipe with a major difference between the internal and environmental pressure.
/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures = null)
if(!user)
return
if(!pressures)
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
pressures = int_air.return_pressure() - env_air.return_pressure()
var/fuck_you_dir = get_dir(src, user) // Because fuck you...
if(!fuck_you_dir)
fuck_you_dir = pick(cardinal)
var/turf/target = get_edge_target_turf(user, fuck_you_dir)
var/range = pressures/250
var/speed = range/5
user.visible_message("<span class='danger'>[user] is sent flying by pressure!</span>","<span class='userdanger'>The pressure sends you flying!</span>")
user.throw_at(target, range, speed)
/obj/machinery/atmospherics/Deconstruct()
if(can_unwrench)
stored.loc = src.loc
transfer_fingerprints_to(stored)
stored = null
qdel(src)
/obj/machinery/atmospherics/proc/getpipeimage(iconset, iconstate, direction, col=rgb(255,255,255))
//Add identifiers for the iconset
if(iconsetids[iconset] == null)
iconsetids[iconset] = num2text(iconsetids.len + 1)
//Generate a unique identifier for this image combination
var/identifier = iconsetids[iconset] + "_[iconstate]_[direction]_[col]"
var/image/img
if(pipeimages[identifier] == null)
img = image(iconset, icon_state=iconstate, dir=direction)
img.color = col
pipeimages[identifier] = img
else
img = pipeimages[identifier]
return img
/obj/machinery/atmospherics/construction(pipe_type, obj_color)
if(can_unwrench)
color = obj_color
pipe_color = obj_color
stored.setDir(src.dir )//need to define them here, because the obj directions...
stored.pipe_type = pipe_type //... were not set at the time the stored pipe was created
stored.color = obj_color
var/turf/T = loc
level = T.intact ? 2 : 1
atmosinit()
var/list/nodes = pipeline_expansion()
for(var/obj/machinery/atmospherics/A in nodes)
A.atmosinit()
A.addMember(src)
build_network()
/obj/machinery/atmospherics/singularity_pull(S, current_size)
if(current_size >= STAGE_FIVE)
Deconstruct()
//Find a connecting /obj/machinery/atmospherics in specified direction
/obj/machinery/atmospherics/proc/findConnecting(direction)
for(var/obj/machinery/atmospherics/target in get_step(src, direction))
if(target.initialize_directions & get_dir(target,src))
return target
#define VENT_SOUND_DELAY 30
/obj/machinery/atmospherics/relaymove(mob/living/user, direction)
if(!(direction & initialize_directions)) //cant go this way.
return
if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug
return
var/obj/machinery/atmospherics/target_move = findConnecting(direction)
if(target_move)
if(target_move.can_crawl_through())
if(is_type_in_list(target_move, ventcrawl_machinery))
user.forceMove(target_move.loc) //handle entering and so on.
user.visible_message("<span class='notice'>You hear something squeezing through the ducts...</span>","<span class='notice'>You climb out the ventilation system.")
else
var/list/pipenetdiff = returnPipenets() ^ target_move.returnPipenets()
if(pipenetdiff.len)
user.update_pipe_vision(target_move)
user.loc = target_move
user.client.eye = target_move //Byond only updates the eye every tick, This smooths out the movement
if(world.time - user.last_played_vent > VENT_SOUND_DELAY)
user.last_played_vent = world.time
playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
else
if((direction & initialize_directions) || is_type_in_list(src, ventcrawl_machinery) && can_crawl_through()) //if we move in a way the pipe can connect, but doesn't - or we're in a vent
user.forceMove(src.loc)
user.visible_message("<span class='notice'>You hear something squeezing through the ducts...</span>","<span class='notice'>You climb out the ventilation system.")
user.canmove = 0
spawn(1)
user.canmove = 1
/obj/machinery/atmospherics/AltClick(mob/living/L)
if(is_type_in_list(src, ventcrawl_machinery))
L.handle_ventcrawl(src)
return
..()
/obj/machinery/atmospherics/proc/can_crawl_through()
return 1
/obj/machinery/atmospherics/proc/returnPipenets()
return list()
/obj/machinery/atmospherics/update_remote_sight(mob/user)
user.sight |= (SEE_TURFS|BLIND)
//Used for certain children of obj/machinery/atmospherics to not show pipe vision when mob is inside it.
/obj/machinery/atmospherics/proc/can_see_pipes()
return 1
@@ -0,0 +1,34 @@
/obj/machinery/atmospherics/components/binary
icon = 'icons/obj/atmospherics/components/binary_devices.dmi'
dir = SOUTH
initialize_directions = SOUTH|NORTH
use_power = 1
device_type = BINARY
/obj/machinery/atmospherics/components/binary/SetInitDirections()
switch(dir)
if(NORTH)
initialize_directions = NORTH|SOUTH
if(SOUTH)
initialize_directions = NORTH|SOUTH
if(EAST)
initialize_directions = EAST|WEST
if(WEST)
initialize_directions = EAST|WEST
/*
Iconnery
*/
/obj/machinery/atmospherics/components/binary/hide(intact)
update_icon()
..(intact)
/*
Housekeeping and pipe network stuff
*/
/obj/machinery/atmospherics/components/binary/atmosinit()
var/node2_connect = dir
var/node1_connect = turn(dir, 180)
var/list/node_connects = list(node1_connect, node2_connect)
..(node_connects)
@@ -0,0 +1,70 @@
//node2, air2, network2 correspond to input
//node1, air1, network1 correspond to output
/obj/machinery/atmospherics/components/binary/circulator
name = "circulator/heat exchanger"
desc = "A gas circulator pump and heat exchanger."
icon_state = "circ1-off"
var/side = CIRC_LEFT
var/status = 0
var/last_pressure_delta = 0
anchored = 1
density = 1
var/global/const/CIRC_LEFT = 1
var/global/const/CIRC_RIGHT = 2
/obj/machinery/atmospherics/components/binary/circulator/proc/return_transfer_air()
var/datum/gas_mixture/air1 = AIR1
var/datum/gas_mixture/air2 = AIR2
var/output_starting_pressure = air1.return_pressure()
var/input_starting_pressure = air2.return_pressure()
if(output_starting_pressure >= input_starting_pressure-10)
//Need at least 10 KPa difference to overcome friction in the mechanism
last_pressure_delta = 0
return null
//Calculate necessary moles to transfer using PV = nRT
if(air2.temperature>0)
var/pressure_delta = (input_starting_pressure - output_starting_pressure)/2
var/transfer_moles = pressure_delta*air1.volume/(air2.temperature * R_IDEAL_GAS_EQUATION)
last_pressure_delta = pressure_delta
//world << "pressure_delta = [pressure_delta]; transfer_moles = [transfer_moles];"
//Actually transfer the gas
var/datum/gas_mixture/removed = air2.remove(transfer_moles)
update_parents()
return removed
else
last_pressure_delta = 0
/obj/machinery/atmospherics/components/binary/circulator/process_atmos()
..()
update_icon()
/obj/machinery/atmospherics/components/binary/circulator/update_icon()
if(stat & (BROKEN|NOPOWER))
icon_state = "circ[side]-p"
else if(last_pressure_delta > 0)
if(last_pressure_delta > ONE_ATMOSPHERE)
icon_state = "circ[side]-run"
else
icon_state = "circ[side]-slow"
else
icon_state = "circ[side]-off"
return 1
@@ -0,0 +1,198 @@
/*
Acts like a normal vent, but has an input AND output.
*/
#define EXT_BOUND 1
#define INPUT_MIN 2
#define OUTPUT_MAX 4
/obj/machinery/atmospherics/components/binary/dp_vent_pump
icon = 'icons/obj/atmospherics/components/unary_devices.dmi' //We reuse the normal vent icons!
icon_state = "dpvent_map"
//node2 is output port
//node1 is input port
name = "dual-port air vent"
desc = "Has a valve and pump attached to it. There are two ports."
level = 1
var/frequency = 0
var/id = null
var/datum/radio_frequency/radio_connection
var/on = 0
var/pump_direction = 1 //0 = siphoning, 1 = releasing
var/external_pressure_bound = ONE_ATMOSPHERE
var/input_pressure_min = 0
var/output_pressure_max = 0
var/pressure_checks = EXT_BOUND
//EXT_BOUND: Do not pass external_pressure_bound
//INPUT_MIN: Do not pass input_pressure_min
//OUTPUT_MAX: Do not pass output_pressure_max
/obj/machinery/atmospherics/components/binary/dp_vent_pump/Destroy()
if(SSradio)
SSradio.remove_object(src, frequency)
return ..()
/obj/machinery/atmospherics/components/binary/dp_vent_pump/high_volume
name = "large dual-port air vent"
/obj/machinery/atmospherics/components/binary/dp_vent_pump/high_volume/New()
..()
var/datum/gas_mixture/air1 = AIR1
var/datum/gas_mixture/air2 = AIR2
air1.volume = 1000
air2.volume = 1000
/obj/machinery/atmospherics/components/binary/dp_vent_pump/update_icon_nopipes()
cut_overlays()
if(showpipe)
add_overlay(getpipeimage('icons/obj/atmospherics/components/unary_devices.dmi', "dpvent_cap"))
if(!on || stat & (NOPOWER|BROKEN))
icon_state = "vent_off"
return
if(pump_direction)
icon_state = "vent_out"
else
icon_state = "vent_in"
/obj/machinery/atmospherics/components/binary/dp_vent_pump/process_atmos()
..()
if(!on)
return 0
var/datum/gas_mixture/air1 = AIR1
var/datum/gas_mixture/air2 = AIR2
var/datum/gas_mixture/environment = loc.return_air()
var/environment_pressure = environment.return_pressure()
if(pump_direction) //input -> external
var/pressure_delta = 10000
if(pressure_checks&EXT_BOUND)
pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure))
if(pressure_checks&INPUT_MIN)
pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min))
if(pressure_delta > 0)
if(air1.temperature > 0)
var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = air1.remove(transfer_moles)
loc.assume_air(removed)
air_update_turf()
var/datum/pipeline/parent1 = PARENT1
parent1.update = 1
else //external -> output
var/pressure_delta = 10000
if(pressure_checks&EXT_BOUND)
pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound))
if(pressure_checks&INPUT_MIN)
pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure()))
if(pressure_delta > 0)
if(environment.temperature > 0)
var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
air2.merge(removed)
air_update_turf()
var/datum/pipeline/parent2 = PARENT2
parent2.update = 1
return 1
//Radio remote control
/obj/machinery/atmospherics/components/binary/dp_vent_pump/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
if(frequency)
radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA)
/obj/machinery/atmospherics/components/binary/dp_vent_pump/proc/broadcast_status()
if(!radio_connection)
return 0
var/datum/signal/signal = new
signal.transmission_method = 1 //radio signal
signal.source = src
signal.data = list(
"tag" = id,
"device" = "ADVP",
"power" = on,
"direction" = pump_direction?("release"):("siphon"),
"checks" = pressure_checks,
"input" = input_pressure_min,
"output" = output_pressure_max,
"external" = external_pressure_bound,
"sigtype" = "status"
)
radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
return 1
/obj/machinery/atmospherics/components/binary/dp_vent_pump/atmosinit()
..()
if(frequency)
set_frequency(frequency)
broadcast_status()
/obj/machinery/atmospherics/components/binary/dp_vent_pump/receive_signal(datum/signal/signal)
if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
return 0
if("power" in signal.data)
on = text2num(signal.data["power"])
if("power_toggle" in signal.data)
on = !on
if("set_direction" in signal.data)
pump_direction = text2num(signal.data["set_direction"])
if("checks" in signal.data)
pressure_checks = text2num(signal.data["checks"])
if("purge" in signal.data)
pressure_checks &= ~1
pump_direction = 0
if("stabalize" in signal.data)
pressure_checks |= 1
pump_direction = 1
if("set_input_pressure" in signal.data)
input_pressure_min = Clamp(text2num(signal.data["set_input_pressure"]),0,ONE_ATMOSPHERE*50)
if("set_output_pressure" in signal.data)
output_pressure_max = Clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50)
if("set_external_pressure" in signal.data)
external_pressure_bound = Clamp(text2num(signal.data["set_external_pressure"]),0,ONE_ATMOSPHERE*50)
if("status" in signal.data)
spawn(2)
broadcast_status()
return //do not update_icon
//if(signal.data["tag"])
spawn(2)
broadcast_status()
update_icon()
#undef EXT_BOUND
#undef INPUT_MIN
#undef OUTPUT_MAX
@@ -0,0 +1,175 @@
/*
Passive gate is similar to the regular pump except:
* It doesn't require power
* Can not transfer low pressure to higher pressure (so it's more like a valve where you can control the flow)
*/
/obj/machinery/atmospherics/components/binary/passive_gate
icon_state = "passgate_map"
name = "passive gate"
desc = "A one-way air valve that does not require power."
can_unwrench = 1
var/on = 0
var/target_pressure = ONE_ATMOSPHERE
var/frequency = 0
var/id = null
var/datum/radio_frequency/radio_connection
/obj/machinery/atmospherics/components/binary/passive_gate/Destroy()
if(SSradio)
SSradio.remove_object(src,frequency)
return ..()
/obj/machinery/atmospherics/components/binary/passive_gate/update_icon_nopipes()
if(!on)
icon_state = "passgate_off"
cut_overlays()
return
add_overlay(getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', "passgate_on"))
/obj/machinery/atmospherics/components/binary/passive_gate/process_atmos()
..()
if(!on)
return 0
var/datum/gas_mixture/air1 = AIR1
var/datum/gas_mixture/air2 = AIR2
var/output_starting_pressure = air2.return_pressure()
var/input_starting_pressure = air1.return_pressure()
if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10))
//No need to pump gas if target is already reached or input pressure is too low
//Need at least 10 KPa difference to overcome friction in the mechanism
return 1
//Calculate necessary moles to transfer using PV = nRT
if((air1.total_moles() > 0) && (air1.temperature>0))
var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2)
//Can not have a pressure delta that would cause output_pressure > input_pressure
var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed = air1.remove(transfer_moles)
air2.merge(removed)
update_parents()
//Radio remote control
/obj/machinery/atmospherics/components/binary/passive_gate/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
if(frequency)
radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA)
/obj/machinery/atmospherics/components/binary/passive_gate/proc/broadcast_status()
if(!radio_connection)
return 0
var/datum/signal/signal = new
signal.transmission_method = 1 //radio signal
signal.source = src
signal.data = list(
"tag" = id,
"device" = "AGP",
"power" = on,
"target_output" = target_pressure,
"sigtype" = "status"
)
radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
return 1
/obj/machinery/atmospherics/components/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
datum/tgui/master_ui = null, datum/ui_state/state = default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_pump", name, 335, 115, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/binary/passive_gate/ui_data()
var/data = list()
data["on"] = on
data["pressure"] = round(target_pressure)
data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
return data
/obj/machinery/atmospherics/components/binary/passive_gate/ui_act(action, params)
if(..())
return
switch(action)
if("power")
on = !on
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
. = TRUE
if("pressure")
var/pressure = params["pressure"]
if(pressure == "max")
pressure = MAX_OUTPUT_PRESSURE
. = TRUE
else if(pressure == "input")
pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
if(!isnull(pressure) || !..())
. = TRUE
else if(text2num(pressure) != null)
pressure = text2num(pressure)
. = TRUE
if(.)
target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
update_icon()
/obj/machinery/atmospherics/components/binary/passive_gate/atmosinit()
..()
if(frequency)
set_frequency(frequency)
/obj/machinery/atmospherics/components/binary/passive_gate/receive_signal(datum/signal/signal)
if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
return 0
var/old_on = on //for logging
if("power" in signal.data)
on = text2num(signal.data["power"])
if("power_toggle" in signal.data)
on = !on
if("set_output_pressure" in signal.data)
target_pressure = Clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50)
if(on != old_on)
investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
if("status" in signal.data)
broadcast_status()
return
broadcast_status()
update_icon()
return
/obj/machinery/atmospherics/components/binary/passive_gate/power_change()
..()
update_icon()
/obj/machinery/atmospherics/components/binary/passive_gate/can_unwrench(mob/user)
if(..())
if(on)
user << "<span class='warning'>You cannot unwrench this [src], turn it off first!</span>"
else
return 1
@@ -0,0 +1,182 @@
/*
Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure.
node1, air1, network1 correspond to input
node2, air2, network2 correspond to output
Thus, the two variables affect pump operation are set in New():
air1.volume
This is the volume of gas available to the pump that may be transfered to the output
air2.volume
Higher quantities of this cause more air to be perfected later
but overall network volume is also increased as this increases...
*/
/obj/machinery/atmospherics/components/binary/pump
icon_state = "pump_map"
name = "gas pump"
desc = "A pump that moves gas by pressure."
can_unwrench = 1
var/on = 0
var/target_pressure = ONE_ATMOSPHERE
var/frequency = 0
var/id = null
var/datum/radio_frequency/radio_connection
/obj/machinery/atmospherics/components/binary/pump/on
on = 1
/obj/machinery/atmospherics/components/binary/pump/Destroy()
if(SSradio)
SSradio.remove_object(src,frequency)
if(radio_connection)
radio_connection = null
return ..()
/obj/machinery/atmospherics/components/binary/pump/update_icon_nopipes()
if(stat & NOPOWER)
icon_state = "pump_off"
return
icon_state = "pump_[on?"on":"off"]"
/obj/machinery/atmospherics/components/binary/pump/process_atmos()
// ..()
if(stat & (NOPOWER|BROKEN))
return 0
if(!on)
return 0
var/datum/gas_mixture/air1 = AIR1
var/datum/gas_mixture/air2 = AIR2
var/output_starting_pressure = air2.return_pressure()
if((target_pressure - output_starting_pressure) < 0.01)
//No need to pump gas if target is already reached!
return 1
//Calculate necessary moles to transfer using PV=nRT
if((air1.total_moles() > 0) && (air1.temperature>0))
var/pressure_delta = target_pressure - output_starting_pressure
var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed = air1.remove(transfer_moles)
air2.merge(removed)
update_parents()
return 1
//Radio remote control
/obj/machinery/atmospherics/components/binary/pump/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
if(frequency)
radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA)
/obj/machinery/atmospherics/components/binary/pump/proc/broadcast_status()
if(!radio_connection)
return 0
var/datum/signal/signal = new
signal.transmission_method = 1 //radio signal
signal.source = src
signal.data = list(
"tag" = id,
"device" = "AGP",
"power" = on,
"target_output" = target_pressure,
"sigtype" = "status"
)
radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
return 1
/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
datum/tgui/master_ui = null, datum/ui_state/state = default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_pump", name, 335, 115, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/binary/pump/ui_data()
var/data = list()
data["on"] = on
data["pressure"] = round(target_pressure)
data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
return data
/obj/machinery/atmospherics/components/binary/pump/ui_act(action, params)
if(..())
return
switch(action)
if("power")
on = !on
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
. = TRUE
if("pressure")
var/pressure = params["pressure"]
if(pressure == "max")
pressure = MAX_OUTPUT_PRESSURE
. = TRUE
else if(pressure == "input")
pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
if(!isnull(pressure) && !..())
. = TRUE
else if(text2num(pressure) != null)
pressure = text2num(pressure)
. = TRUE
if(.)
target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
update_icon()
/obj/machinery/atmospherics/components/binary/pump/atmosinit()
..()
if(frequency)
set_frequency(frequency)
/obj/machinery/atmospherics/components/binary/pump/receive_signal(datum/signal/signal)
if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
return 0
var/old_on = on //for logging
if("power" in signal.data)
on = text2num(signal.data["power"])
if("power_toggle" in signal.data)
on = !on
if("set_output_pressure" in signal.data)
target_pressure = Clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50)
if(on != old_on)
investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
if("status" in signal.data)
broadcast_status()
return
broadcast_status()
update_icon()
return
/obj/machinery/atmospherics/components/binary/pump/power_change()
..()
update_icon()
/obj/machinery/atmospherics/components/binary/pump/can_unwrench(mob/user)
if(..())
if(!(stat & NOPOWER) && on)
user << "<span class='warning'>You cannot unwrench this [src], turn it off first!</span>"
else
return 1
@@ -0,0 +1,72 @@
/*
It's like a regular ol' straight pipe, but you can turn it on and off.
*/
/obj/machinery/atmospherics/components/binary/valve
icon_state = "mvalve_map"
name = "manual valve"
desc = "A pipe valve"
can_unwrench = 1
var/frequency = 0
var/id = null
var/open = 0
var/valve_type = "m" //lets us have a nice, clean, OOP update_icon_nopipes()
/obj/machinery/atmospherics/components/binary/valve/open
open = 1
/obj/machinery/atmospherics/components/binary/valve/update_icon_nopipes(animation = 0)
normalize_dir()
if(animation)
flick("[valve_type]valve_[open][!open]",src)
icon_state = "[valve_type]valve_[open?"on":"off"]"
/obj/machinery/atmospherics/components/binary/valve/proc/open()
open = 1
update_icon_nopipes()
update_parents()
var/datum/pipeline/parent1 = PARENT1
parent1.reconcile_air()
investigate_log("was opened by [usr ? key_name(usr) : "a remote signal"]", "atmos")
/obj/machinery/atmospherics/components/binary/valve/proc/close()
open = 0
update_icon_nopipes()
investigate_log("was closed by [usr ? key_name(usr) : "a remote signal"]", "atmos")
/obj/machinery/atmospherics/components/binary/valve/proc/normalize_dir()
if(dir==SOUTH)
setDir(NORTH)
else if(dir==WEST)
setDir(EAST)
/obj/machinery/atmospherics/components/binary/valve/attack_ai(mob/user)
return
/obj/machinery/atmospherics/components/binary/valve/attack_hand(mob/user)
add_fingerprint(usr)
update_icon_nopipes(1)
sleep(10)
if(open)
close()
return
open()
/obj/machinery/atmospherics/components/binary/valve/digital // can be controlled by AI
name = "digital valve"
desc = "A digitally controlled valve."
icon_state = "dvalve_map"
valve_type = "d"
/obj/machinery/atmospherics/components/binary/valve/digital/attack_ai(mob/user)
return src.attack_hand(user)
/obj/machinery/atmospherics/components/binary/valve/digital/update_icon_nopipes(animation)
if(stat & NOPOWER)
normalize_dir()
icon_state = "dvalve_nopower"
return
..()
@@ -0,0 +1,178 @@
/*
Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure.
node1, air1, network1 correspond to input
node2, air2, network2 correspond to output
Thus, the two variables affect pump operation are set in New():
air1.volume
This is the volume of gas available to the pump that may be transfered to the output
air2.volume
Higher quantities of this cause more air to be perfected later
but overall network volume is also increased as this increases...
*/
/obj/machinery/atmospherics/components/binary/volume_pump
icon_state = "volpump_map"
name = "volumetric gas pump"
desc = "A pump that moves gas by volume."
can_unwrench = 1
var/on = 0
var/transfer_rate = MAX_TRANSFER_RATE
var/frequency = 0
var/id = null
var/datum/radio_frequency/radio_connection
/obj/machinery/atmospherics/components/binary/volume_pump/Destroy()
if(SSradio)
SSradio.remove_object(src,frequency)
return ..()
/obj/machinery/atmospherics/components/binary/volume_pump/on
on = 1
/obj/machinery/atmospherics/components/binary/volume_pump/update_icon_nopipes()
if(stat & NOPOWER)
icon_state = "volpump_off"
return
icon_state = "volpump_[on?"on":"off"]"
/obj/machinery/atmospherics/components/binary/volume_pump/process_atmos()
// ..()
if(stat & (NOPOWER|BROKEN))
return
if(!on)
return 0
var/datum/gas_mixture/air1 = AIR1
var/datum/gas_mixture/air2 = AIR2
// Pump mechanism just won't do anything if the pressure is too high/too low
var/input_starting_pressure = air1.return_pressure()
var/output_starting_pressure = air2.return_pressure()
if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000))
return 1
var/transfer_ratio = min(1, transfer_rate/air1.volume)
var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio)
air2.merge(removed)
update_parents()
return 1
/obj/machinery/atmospherics/components/binary/volume_pump/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
if(frequency)
radio_connection = SSradio.add_object(src, frequency)
/obj/machinery/atmospherics/components/binary/volume_pump/proc/broadcast_status()
if(!radio_connection)
return 0
var/datum/signal/signal = new
signal.transmission_method = 1 //radio signal
signal.source = src
signal.data = list(
"tag" = id,
"device" = "APV",
"power" = on,
"transfer_rate" = transfer_rate,
"sigtype" = "status"
)
radio_connection.post_signal(src, signal)
return 1
/obj/machinery/atmospherics/components/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
datum/tgui/master_ui = null, datum/ui_state/state = default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_pump", name, 310, 115, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/binary/volume_pump/ui_data()
var/data = list()
data["on"] = on
data["rate"] = round(transfer_rate)
data["max_rate"] = round(MAX_TRANSFER_RATE)
return data
/obj/machinery/atmospherics/components/binary/volume_pump/atmosinit()
..()
set_frequency(frequency)
/obj/machinery/atmospherics/components/binary/volume_pump/ui_act(action, params)
if(..())
return
switch(action)
if("power")
on = !on
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
. = TRUE
if("rate")
var/rate = params["rate"]
if(rate == "max")
rate = MAX_TRANSFER_RATE
. = TRUE
else if(rate == "input")
rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null
if(!isnull(rate) && !..())
. = TRUE
else if(text2num(rate) != null)
rate = text2num(rate)
. = TRUE
if(.)
transfer_rate = Clamp(rate, 0, MAX_TRANSFER_RATE)
investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos")
update_icon()
/obj/machinery/atmospherics/components/binary/volume_pump/receive_signal(datum/signal/signal)
if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
return 0
var/old_on = on //for logging
if("power" in signal.data)
on = text2num(signal.data["power"])
if("power_toggle" in signal.data)
on = !on
if("set_transfer_rate" in signal.data)
var/datum/gas_mixture/air1 = AIR1
transfer_rate = Clamp(text2num(signal.data["set_transfer_rate"]),0,air1.volume)
if(on != old_on)
investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
if("status" in signal.data)
broadcast_status()
return //do not update_icon
broadcast_status()
update_icon()
return
/obj/machinery/atmospherics/components/binary/volume_pump/power_change()
..()
update_icon()
/obj/machinery/atmospherics/components/binary/volume_pump/can_unwrench(mob/user)
if(..())
if(!(stat & NOPOWER) && on)
user << "<span class='warning'>You cannot unwrench this [src], turn it off first!</span>"
else
return 1
@@ -0,0 +1,172 @@
/*
So much of atmospherics.dm was used solely by components, so separating this makes things all a lot cleaner.
On top of that, now people can add component-speciic procs/vars if they want!
*/
/obj/machinery/atmospherics/components
var/welded = 0 //Used on pumps and scrubbers
var/showpipe = 0
var/list/datum/pipeline/parents = list()
var/list/datum/gas_mixture/airs = list()
/obj/machinery/atmospherics/components/New()
parents.len = device_type
airs.len = device_type
..()
for(DEVICE_TYPE_LOOP)
var/datum/gas_mixture/A = new
A.volume = 200
AIR_I = A
/*
Iconnery
*/
/obj/machinery/atmospherics/components/proc/icon_addintact(var/obj/machinery/atmospherics/node)
var/image/img = getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', "pipe_intact", get_dir(src,node), node.pipe_color)
underlays += img
return img.dir
/obj/machinery/atmospherics/components/proc/icon_addbroken(var/connected = 0)
var/unconnected = (~connected) & initialize_directions
for(var/direction in cardinal)
if(unconnected & direction)
underlays += getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', "pipe_exposed", direction)
/obj/machinery/atmospherics/components/proc/update_icon_nopipes()
return
/obj/machinery/atmospherics/components/update_icon()
update_icon_nopipes()
underlays.Cut()
var/turf/T = loc
if(level == 2 || !T.intact)
showpipe = 1
else
showpipe = 0
if(!showpipe)
return //no need to update the pipes if they aren't showing
var/connected = 0
for(DEVICE_TYPE_LOOP) //adds intact pieces
if(NODE_I)
connected |= icon_addintact(NODE_I)
icon_addbroken(connected) //adds broken pieces
/*
Pipenet stuff; housekeeping
*/
/obj/machinery/atmospherics/components/nullifyNode(I)
..()
if(NODE_I)
nullifyPipenet(PARENT_I)
qdel(AIR_I)
AIR_I = null
/obj/machinery/atmospherics/components/construction()
..()
update_parents()
/obj/machinery/atmospherics/components/build_network()
for(DEVICE_TYPE_LOOP)
if(!PARENT_I)
PARENT_I = new /datum/pipeline()
var/datum/pipeline/P = PARENT_I
P.build_pipeline(src)
/obj/machinery/atmospherics/components/proc/nullifyPipenet(datum/pipeline/reference)
var/I = parents.Find(reference)
reference.other_airs -= AIR_I
reference.other_atmosmch -= src
PARENT_I = null
/obj/machinery/atmospherics/components/returnPipenetAir(datum/pipeline/reference)
var/I = parents.Find(reference)
return AIR_I
/obj/machinery/atmospherics/components/pipeline_expansion(datum/pipeline/reference)
if(reference)
var/I = parents.Find(reference)
return list(NODE_I)
else
return ..()
/obj/machinery/atmospherics/components/setPipenet(datum/pipeline/reference, obj/machinery/atmospherics/A)
var/I = nodes.Find(A)
PARENT_I = reference
/obj/machinery/atmospherics/components/returnPipenet(obj/machinery/atmospherics/A = NODE1) //returns PARENT1 if called without argument
var/I = nodes.Find(A)
return PARENT_I
/obj/machinery/atmospherics/components/replacePipenet(datum/pipeline/Old, datum/pipeline/New)
var/I = parents.Find(Old)
PARENT_I = New
/obj/machinery/atmospherics/components/unsafe_pressure_release(var/mob/user, var/pressures)
..()
var/turf/T = get_turf(src)
if(T)
//Remove the gas from airs and assume it
var/datum/gas_mixture/environment = T.return_air()
var/lost = null
var/times_lost = 0
for(DEVICE_TYPE_LOOP)
var/datum/gas_mixture/air = AIR_I
lost += pressures*environment.volume/(air.temperature * R_IDEAL_GAS_EQUATION)
times_lost++
var/shared_loss = lost/times_lost
var/datum/gas_mixture/to_release
for(DEVICE_TYPE_LOOP)
var/datum/gas_mixture/air = AIR_I
if(!to_release)
to_release = air.remove(shared_loss)
continue
to_release.merge(air.remove(shared_loss))
T.assume_air(to_release)
air_update_turf(1)
/obj/machinery/atmospherics/components/proc/safe_input(var/title, var/text, var/default_set)
var/new_value = input(usr,text,title,default_set) as num
if(usr.canUseTopic(src))
return new_value
return default_set
/*
Helpers
*/
/obj/machinery/atmospherics/components/proc/update_parents()
for(DEVICE_TYPE_LOOP)
var/datum/pipeline/parent = PARENT_I
if(!parent)
throw EXCEPTION("Component is missing a pipenet! Rebuilding...")
build_network()
parent.update = 1
/obj/machinery/atmospherics/components/returnPipenets()
. = list()
for(DEVICE_TYPE_LOOP)
. += returnPipenet(NODE_I)
/*
UI Stuff
*/
/obj/machinery/atmospherics/components/ui_status(mob/user)
if(allowed(user))
return ..()
user << "<span class='danger'>Access denied.</span>"
return UI_CLOSE
@@ -0,0 +1,155 @@
/obj/machinery/atmospherics/components/trinary/filter
name = "gas filter"
icon_state = "filter_off"
density = 0
can_unwrench = 1
var/on = 0
var/target_pressure = ONE_ATMOSPHERE
var/filter_type = ""
var/frequency = 0
var/datum/radio_frequency/radio_connection
/obj/machinery/atmospherics/components/trinary/filter/flipped
icon_state = "filter_off_f"
flipped = 1
/obj/machinery/atmospherics/components/trinary/filter/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
if(frequency)
radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA)
/obj/machinery/atmospherics/components/trinary/filter/Destroy()
if(SSradio)
SSradio.remove_object(src,frequency)
return ..()
/obj/machinery/atmospherics/components/trinary/filter/update_icon()
cut_overlays()
for(var/direction in cardinal)
if(direction & initialize_directions)
var/obj/machinery/atmospherics/node = findConnecting(direction)
if(node)
add_overlay(getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction, node.pipe_color))
continue
add_overlay(getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction))
..()
/obj/machinery/atmospherics/components/trinary/filter/update_icon_nopipes()
if(!(stat & NOPOWER) && on && NODE1 && NODE2 && NODE3)
icon_state = "filter_on[flipped?"_f":""]"
return
icon_state = "filter_off[flipped?"_f":""]"
/obj/machinery/atmospherics/components/trinary/filter/power_change()
var/old_stat = stat
..()
if(stat & NOPOWER)
on = 0
if(old_stat != stat)
update_icon()
/obj/machinery/atmospherics/components/trinary/filter/process_atmos()
..()
if(!on)
return 0
if(!(NODE1 && NODE2 && NODE3))
return 0
var/datum/gas_mixture/air1 = AIR1
var/datum/gas_mixture/air2 = AIR2
var/datum/gas_mixture/air3 = AIR3
var/output_starting_pressure = air3.return_pressure()
if(output_starting_pressure >= target_pressure)
//No need to mix if target is already full!
return 1
//Calculate necessary moles to transfer using PV=nRT
var/pressure_delta = target_pressure - output_starting_pressure
var/transfer_moles
if(air1.temperature > 0)
transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
if(transfer_moles > 0)
var/datum/gas_mixture/removed = air1.remove(transfer_moles)
if(!removed)
return
var/datum/gas_mixture/filtered_out = new
filtered_out.temperature = removed.temperature
if(filter_type && removed.gases[filter_type])
filtered_out.assert_gas(filter_type)
filtered_out.gases[filter_type][MOLES] = removed.gases[filter_type][MOLES]
removed.gases[filter_type][MOLES] = 0
removed.garbage_collect()
else
filtered_out = null
air2.merge(filtered_out)
air3.merge(removed)
update_parents()
return 1
/obj/machinery/atmospherics/components/trinary/filter/atmosinit()
set_frequency(frequency)
return ..()
/obj/machinery/atmospherics/components/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
datum/tgui/master_ui = null, datum/ui_state/state = default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_filter", name, 475, 140, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/trinary/filter/ui_data()
var/data = list()
data["on"] = on
data["pressure"] = round(target_pressure)
data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
data["filter_type"] = filter_type
return data
/obj/machinery/atmospherics/components/trinary/filter/ui_act(action, params)
if(..())
return
switch(action)
if("power")
on = !on
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
. = TRUE
if("pressure")
var/pressure = params["pressure"]
if(pressure == "max")
pressure = MAX_OUTPUT_PRESSURE
. = TRUE
else if(pressure == "input")
pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
if(!isnull(pressure) && !..())
. = TRUE
else if(text2num(pressure) != null)
pressure = text2num(pressure)
. = TRUE
if(.)
target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
if("filter")
filter_type = ""
var/filter_name = "nothing"
var/gas = params["mode"]
if(gas in meta_gas_info)
filter_type = gas
filter_name = meta_gas_info[gas][META_GAS_NAME]
investigate_log("was set to filter [filter_name] by [key_name(usr)]", "atmos")
. = TRUE
update_icon()
@@ -0,0 +1,170 @@
/obj/machinery/atmospherics/components/trinary/mixer
icon_state = "mixer_off"
density = 0
name = "gas mixer"
can_unwrench = 1
var/on = 0
var/target_pressure = ONE_ATMOSPHERE
var/node1_concentration = 0.5
var/node2_concentration = 0.5
//node 3 is the outlet, nodes 1 & 2 are intakes
/obj/machinery/atmospherics/components/trinary/mixer/flipped
icon_state = "mixer_off_f"
flipped = 1
/obj/machinery/atmospherics/components/trinary/mixer/update_icon()
cut_overlays()
for(var/direction in cardinal)
if(direction & initialize_directions)
var/obj/machinery/atmospherics/node = findConnecting(direction)
if(node)
add_overlay(getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction, node.pipe_color))
continue
add_overlay(getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction))
return ..()
/obj/machinery/atmospherics/components/trinary/mixer/update_icon_nopipes()
if(!(stat & NOPOWER) && on && NODE1 && NODE2 && NODE3)
icon_state = "mixer_on[flipped?"_f":""]"
return
icon_state = "mixer_off[flipped?"_f":""]"
/obj/machinery/atmospherics/components/trinary/mixer/power_change()
var/old_stat = stat
..()
if(stat & NOPOWER)
on = 0
if(old_stat != stat)
update_icon()
/obj/machinery/atmospherics/components/trinary/mixer/New()
..()
var/datum/gas_mixture/air3 = AIR3
air3.volume = 300
AIR3 = air3
/obj/machinery/atmospherics/components/trinary/mixer/process_atmos()
..()
if(!on)
return 0
if(!(NODE1 && NODE2 && NODE3))
return 0
var/datum/gas_mixture/air1 = AIR1
var/datum/gas_mixture/air2 = AIR2
var/datum/gas_mixture/air3 = AIR3
var/output_starting_pressure = air3.return_pressure()
if(output_starting_pressure >= target_pressure)
//No need to mix if target is already full!
return 1
//Calculate necessary moles to transfer using PV=nRT
var/pressure_delta = target_pressure - output_starting_pressure
var/transfer_moles1 = 0
var/transfer_moles2 = 0
if(air1.temperature > 0)
transfer_moles1 = (node1_concentration * pressure_delta) * air3.volume / (air1.temperature * R_IDEAL_GAS_EQUATION)
if(air2.temperature > 0)
transfer_moles2 = (node2_concentration * pressure_delta) * air3.volume / (air2.temperature * R_IDEAL_GAS_EQUATION)
var/air1_moles = air1.total_moles()
var/air2_moles = air2.total_moles()
if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2))
var/ratio = 0
if((transfer_moles1 > 0 ) && (transfer_moles2 > 0))
ratio = min(air1_moles / transfer_moles1, air2_moles / transfer_moles2)
if((transfer_moles2 == 0 ) && ( transfer_moles1 > 0))
ratio = air1_moles / transfer_moles1
if((transfer_moles1 == 0 ) && ( transfer_moles2 > 0))
ratio = air2_moles / transfer_moles2
transfer_moles1 *= ratio
transfer_moles2 *= ratio
//Actually transfer the gas
if(transfer_moles1 > 0)
var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1)
air3.merge(removed1)
if(transfer_moles2 > 0)
var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2)
air3.merge(removed2)
if(transfer_moles1)
var/datum/pipeline/parent1 = PARENT1
parent1.update = TRUE
if(transfer_moles2)
var/datum/pipeline/parent2 = PARENT2
parent2.update = TRUE
var/datum/pipeline/parent3 = PARENT3
parent3.update = TRUE
return TRUE
/obj/machinery/atmospherics/components/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
datum/tgui/master_ui = null, datum/ui_state/state = default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_mixer", name, 370, 165, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/trinary/mixer/ui_data()
var/data = list()
data["on"] = on
data["set_pressure"] = round(target_pressure)
data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
data["node1_concentration"] = round(node1_concentration*100)
data["node2_concentration"] = round(node2_concentration*100)
return data
/obj/machinery/atmospherics/components/trinary/mixer/ui_act(action, params)
if(..())
return
switch(action)
if("power")
on = !on
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
. = TRUE
if("pressure")
var/pressure = params["pressure"]
if(pressure == "max")
pressure = MAX_OUTPUT_PRESSURE
. = TRUE
else if(pressure == "input")
pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
if(!isnull(pressure) && !..())
. = TRUE
else if(text2num(pressure) != null)
pressure = text2num(pressure)
. = TRUE
if(.)
target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
if("node1")
var/value = text2num(params["concentration"])
node1_concentration = max(0, min(1, node1_concentration + value))
node2_concentration = max(0, min(1, node2_concentration - value))
investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos")
. = TRUE
if("node2")
var/value = text2num(params["concentration"])
node2_concentration = max(0, min(1, node2_concentration + value))
node1_concentration = max(0, min(1, node1_concentration - value))
investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos")
. = TRUE
update_icon()
@@ -0,0 +1,47 @@
/obj/machinery/atmospherics/components/trinary
icon = 'icons/obj/atmospherics/components/trinary_devices.dmi'
dir = SOUTH
initialize_directions = SOUTH|NORTH|WEST
use_power = 1
device_type = TRINARY
var/flipped = 0
/obj/machinery/atmospherics/components/trinary/SetInitDirections()
switch(dir)
if(NORTH)
initialize_directions = EAST|NORTH|SOUTH
if(SOUTH)
initialize_directions = SOUTH|WEST|NORTH
if(EAST)
initialize_directions = EAST|WEST|SOUTH
if(WEST)
initialize_directions = WEST|NORTH|EAST
/*
Housekeeping and pipe network stuff
*/
/obj/machinery/atmospherics/components/trinary/atmosinit()
//Mixer:
//1 and 2 is input
//Node 3 is output
//If we flip the mixer, 1 and 3 shall exchange positions
//Filter:
//Node 1 is input
//Node 2 is filtered output
//Node 3 is rest output
//If we flip the filter, 1 and 3 shall exchange positions
var/node1_connect = turn(dir, -180)
var/node2_connect = turn(dir, -90)
var/node3_connect = dir
if(flipped)
node1_connect = turn(node1_connect, 180)
node3_connect = turn(node3_connect, 180)
var/list/node_connects = list(node1_connect, node2_connect, node3_connect)
..(node_connects)
@@ -0,0 +1,271 @@
/obj/machinery/atmospherics/components/unary/cryo_cell
name = "cryo cell"
icon = 'icons/obj/cryogenics.dmi'
icon_state = "cell-off"
density = 1
anchored = 1
var/on = FALSE
state_open = FALSE
var/autoeject = FALSE
var/volume = 100
var/efficiency = 1
var/sleep_factor = 750
var/paralyze_factor = 1000
var/heat_capacity = 20000
var/conduction_coefficient = 0.30
var/obj/item/weapon/reagent_containers/glass/beaker = null
var/reagent_transfer = 0
/obj/machinery/atmospherics/components/unary/cryo_cell/New()
..()
initialize_directions = dir
var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/cryo_tube(null)
B.apply_default_parts(src)
/obj/item/weapon/circuitboard/machine/cryo_tube
name = "circuit board (Cryotube)"
build_path = /obj/machinery/atmospherics/components/unary/cryo_cell
origin_tech = "programming=4;biotech=3;engineering=4;plasmatech=3"
req_components = list(
/obj/item/weapon/stock_parts/matter_bin = 1,
/obj/item/stack/cable_coil = 1,
/obj/item/weapon/stock_parts/console_screen = 1,
/obj/item/stack/sheet/glass = 2)
/obj/machinery/atmospherics/components/unary/cryo_cell/construction()
..(dir, dir)
/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts()
var/C
for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
C += M.rating
efficiency = initial(efficiency) * C
sleep_factor = initial(sleep_factor) * C
paralyze_factor = initial(paralyze_factor) * C
heat_capacity = initial(heat_capacity) / C
conduction_coefficient = initial(conduction_coefficient) * C
/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy()
beaker = null
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon()
if(panel_open)
icon_state = "cell-o"
else if(state_open)
icon_state = "cell-open"
else if(on && is_operational())
if(occupant)
icon_state = "cell-occupied"
else
icon_state = "cell-on"
else
icon_state = "cell-off"
/obj/machinery/atmospherics/components/unary/cryo_cell/process()
..()
if(!on)
return
if(!is_operational())
on = FALSE
update_icon()
return
var/datum/gas_mixture/air1 = AIR1
if(occupant)
if(occupant.health >= 100) // Don't bother with fully healed people.
on = FALSE
update_icon()
playsound(src.loc, 'sound/machines/ding.ogg', volume, 1) // Bug the doctors.
if(autoeject) // Eject if configured.
open_machine()
return
else if(occupant.stat == DEAD) // We don't bother with dead people.
return
if(occupant.bodytemperature < T0C) // Sleepytime. Why? More cryo magic.
occupant.Sleeping((occupant.bodytemperature / sleep_factor) * 100)
occupant.Paralyse((occupant.bodytemperature / paralyze_factor) * 100)
if(beaker)
if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic.
beaker.reagents.trans_to(occupant, 1, 10 * efficiency) // Transfer reagents, multiplied because cryo magic.
beaker.reagents.reaction(occupant, VAPOR)
air1.gases["o2"][MOLES] -= 2 / efficiency // Lets use gas for this.
if(++reagent_transfer >= 10 * efficiency) // Throttle reagent transfer (higher efficiency will transfer the same amount but consume less from the beaker).
reagent_transfer = 0
return 1
/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos()
..()
if(!on)
return
var/datum/gas_mixture/air1 = AIR1
if(!NODE1 || !AIR1 || air1.gases["o2"][MOLES] < 5) // Turn off if the machine won't work.
on = FALSE
update_icon()
return
if(occupant)
var/cold_protection = 0
var/mob/living/carbon/human/H = occupant
if(istype(H))
cold_protection = H.get_cold_protection(air1.temperature)
var/temperature_delta = air1.temperature - occupant.bodytemperature // The only semi-realistic thing here: share temperature between the cell and the occupant.
if(abs(temperature_delta) > 1)
var/air_heat_capacity = air1.heat_capacity()
var/heat = ((1 - cold_protection) / 10 + conduction_coefficient) \
* temperature_delta * \
(air_heat_capacity * heat_capacity / (air_heat_capacity + heat_capacity))
air1.temperature = max(air1.temperature - heat / air_heat_capacity, TCMB)
occupant.bodytemperature = max(occupant.bodytemperature + heat / heat_capacity, TCMB)
air1.gases["o2"][MOLES] -= 0.5 / efficiency // Magically consume gas? Why not, we run on cryo magic.
/obj/machinery/atmospherics/components/unary/cryo_cell/power_change()
..()
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user)
container_resist(user)
/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine()
if(!state_open && !panel_open)
on = FALSE
..()
if(beaker)
beaker.loc = src
/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/user)
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
return occupant
/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/user)
user << "<span class='notice'>You struggle inside the cryotube, kicking the release with your foot... (This will take around 30 seconds.)</span>"
audible_message("<span class='notice'>You hear a thump from [src].</span>")
if(do_after(user, 300))
if(occupant == user) // Check they're still here.
open_machine()
/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user)
..()
if(occupant)
if(on)
user << "Someone's inside [src]!"
else
user << "You can barely make out a form floating in [src]."
else
user << "[src] seems empty."
/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
return
close_machine(target)
/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weapon/reagent_containers/glass))
. = 1 //no afterattack
if(!user.drop_item())
return
if(beaker)
user << "<span class='warning'>A beaker is already loaded into [src]!</span>"
return
beaker = I
I.loc = src
user.visible_message("[user] places [I] in [src].", \
"<span class='notice'>You place [I] in [src].</span>")
return
if(!on && !occupant && !state_open)
if(default_deconstruction_screwdriver(user, "cell-o", "cell-off", I))
return
if(exchange_parts(user, I))
return
if(default_change_direction_wrench(user, I))
return
if(default_pry_open(I))
return
if(default_deconstruction_crowbar(I))
return
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
datum/tgui/master_ui = null, datum/ui_state/state = notcontained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "cryo", name, 400, 550, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_data()
var/list/data = list()
data["isOperating"] = on
data["hasOccupant"] = occupant ? 1 : 0
data["isOpen"] = state_open
data["autoEject"] = autoeject
var/list/occupantData = list()
if(occupant)
occupantData["name"] = occupant.name
occupantData["stat"] = occupant.stat
occupantData["health"] = occupant.health
occupantData["maxHealth"] = occupant.maxHealth
occupantData["minHealth"] = config.health_threshold_dead
occupantData["bruteLoss"] = occupant.getBruteLoss()
occupantData["oxyLoss"] = occupant.getOxyLoss()
occupantData["toxLoss"] = occupant.getToxLoss()
occupantData["fireLoss"] = occupant.getFireLoss()
occupantData["bodyTemperature"] = occupant.bodytemperature
data["occupant"] = occupantData
var/datum/gas_mixture/air1 = AIR1
data["cellTemperature"] = round(air1.temperature)
data["isBeakerLoaded"] = beaker ? 1 : 0
var beakerContents = list()
if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
for(var/datum/reagent/R in beaker.reagents.reagent_list)
beakerContents += list(list("name" = R.name, "volume" = R.volume))
data["beakerContents"] = beakerContents
return data
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params)
if(..())
return
switch(action)
if("power")
if(on)
on = FALSE
else if(!state_open)
on = TRUE
. = TRUE
if("door")
if(state_open)
close_machine()
else
open_machine()
. = TRUE
if("autoeject")
autoeject = !autoeject
. = TRUE
if("ejectbeaker")
if(beaker)
beaker.loc = loc
beaker = null
. = TRUE
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user)
return //we don't see the pipe network while inside cryo.
/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user)
user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1)
/obj/machinery/atmospherics/components/unary/cryo_cell/can_crawl_through()
return //can't ventcrawl in or out of cryo.
/obj/machinery/atmospherics/components/unary/cryo_cell/can_see_pipes()
return 0 //you can't see the pipe network when inside a cryo cell.
@@ -0,0 +1,20 @@
/obj/machinery/atmospherics/components/unary/generator_input
icon_state = "he_intact"
density = 1
name = "generator input"
desc = "Placeholder"
var/update_cycle
/obj/machinery/atmospherics/components/unary/generator_input/update_icon()
if(NODE1)
icon_state = "intact"
else
icon_state = "exposed"
return
/obj/machinery/atmospherics/components/unary/generator_input/proc/return_exchange_air()
return AIR1
@@ -0,0 +1,69 @@
/obj/machinery/atmospherics/components/unary/heat_exchanger
icon_state = "he_intact"
name = "heat exchanger"
desc = "Exchanges heat between two input gases. Setup for fast heat transfer"
can_unwrench = 1
var/obj/machinery/atmospherics/components/unary/heat_exchanger/partner = null
var/update_cycle
/obj/machinery/atmospherics/components/unary/heat_exchanger/update_icon()
if(NODE1)
icon_state = "he_intact"
var/obj/machinery/atmospherics/node = NODE1
color = node.color
else
icon_state = "he_exposed"
return
/obj/machinery/atmospherics/components/unary/heat_exchanger/atmosinit()
if(!partner)
var/partner_connect = turn(dir,180)
for(var/obj/machinery/atmospherics/components/unary/heat_exchanger/target in get_step(src,partner_connect))
if(target.dir & get_dir(src,target))
partner = target
partner.partner = src
break
..()
/obj/machinery/atmospherics/components/unary/heat_exchanger/process_atmos()
..()
if(!partner)
return 0
if(SSair.times_fired <= update_cycle)
return 0
update_cycle = SSair.times_fired
partner.update_cycle = SSair.times_fired
var/datum/gas_mixture/air_contents = AIR1
var/datum/gas_mixture/partner_air_contents = partner.AIR1
var/air_heat_capacity = air_contents.heat_capacity()
var/other_air_heat_capacity = partner_air_contents.heat_capacity()
var/combined_heat_capacity = other_air_heat_capacity + air_heat_capacity
var/old_temperature = air_contents.temperature
var/other_old_temperature = partner_air_contents.temperature
if(combined_heat_capacity > 0)
var/combined_energy = partner_air_contents.temperature*other_air_heat_capacity + air_heat_capacity*air_contents.temperature
var/new_temperature = combined_energy/combined_heat_capacity
air_contents.temperature = new_temperature
partner_air_contents.temperature = new_temperature
if(abs(old_temperature-air_contents.temperature) > 1)
update_parents()
if(abs(other_old_temperature-partner_air_contents.temperature) > 1)
partner.update_parents()
return 1
@@ -0,0 +1,141 @@
/obj/machinery/atmospherics/components/unary/outlet_injector
icon_state = "inje_map"
use_power = 1
name = "air injector"
desc = "Has a valve and pump attached to it"
var/on = 0
var/injecting = 0
var/volume_rate = 50
var/frequency = 0
var/id = null
var/datum/radio_frequency/radio_connection
level = 1
/obj/machinery/atmospherics/components/unary/outlet_injector/Destroy()
if(SSradio)
SSradio.remove_object(src,frequency)
return ..()
/obj/machinery/atmospherics/components/unary/outlet_injector/on
on = 1
/obj/machinery/atmospherics/components/unary/outlet_injector/update_icon_nopipes()
if(!NODE1 || !on || stat & (NOPOWER|BROKEN))
icon_state = "inje_off"
return
icon_state = "inje_on"
/obj/machinery/atmospherics/components/unary/outlet_injector/power_change()
var/old_stat = stat
..()
if(old_stat != stat)
update_icon()
/obj/machinery/atmospherics/components/unary/outlet_injector/process_atmos()
..()
injecting = 0
if(!on || stat & NOPOWER)
return 0
var/datum/gas_mixture/air_contents = AIR1
if(air_contents.temperature > 0)
var/transfer_moles = (air_contents.return_pressure())*volume_rate/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
loc.assume_air(removed)
air_update_turf()
update_parents()
return 1
/obj/machinery/atmospherics/components/unary/outlet_injector/proc/inject()
if(on || injecting)
return 0
var/datum/gas_mixture/air_contents = AIR1
injecting = 1
if(air_contents.temperature > 0)
var/transfer_moles = (air_contents.return_pressure())*volume_rate/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
loc.assume_air(removed)
update_parents()
flick("inje_inject", src)
/obj/machinery/atmospherics/components/unary/outlet_injector/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
if(frequency)
radio_connection = SSradio.add_object(src, frequency)
/obj/machinery/atmospherics/components/unary/outlet_injector/proc/broadcast_status()
if(!radio_connection)
return 0
var/datum/signal/signal = new
signal.transmission_method = 1 //radio signal
signal.source = src
signal.data = list(
"tag" = id,
"device" = "AO",
"power" = on,
"volume_rate" = volume_rate,
//"timestamp" = world.time,
"sigtype" = "status"
)
radio_connection.post_signal(src, signal)
return 1
/obj/machinery/atmospherics/components/unary/outlet_injector/atmosinit()
set_frequency(frequency)
broadcast_status()
..()
/obj/machinery/atmospherics/components/unary/outlet_injector/receive_signal(datum/signal/signal)
if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
return 0
if("power" in signal.data)
on = text2num(signal.data["power"])
if("power_toggle" in signal.data)
on = !on
if("inject" in signal.data)
spawn inject()
return
if("set_volume_rate" in signal.data)
var/number = text2num(signal.data["set_volume_rate"])
var/datum/gas_mixture/air_contents = AIR1
volume_rate = Clamp(number, 0, air_contents.volume)
if("status" in signal.data)
spawn(2)
broadcast_status()
return //do not update_icon
//log_admin("DEBUG \[[world.timeofday]\]: outlet_injector/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]")
//return
spawn(2)
broadcast_status()
update_icon()
@@ -0,0 +1,54 @@
/obj/machinery/atmospherics/components/unary/oxygen_generator
icon_state = "o2gen_map"
name = "oxygen generator"
desc = "Generates oxygen"
dir = SOUTH
initialize_directions = SOUTH
var/on = 0
var/oxygen_content = 10
/obj/machinery/atmospherics/components/unary/oxygen_generator/update_icon_nopipes()
cut_overlays()
if(showpipe)
add_overlay(getpipeimage('icons/obj/atmospherics/components/unary_devices.dmi', "scrub_cap", initialize_directions)) //it works for now
if(!NODE1 || !on || stat & BROKEN)
icon_state = "o2gen_off"
return
else
icon_state = "o2gen_on"
/obj/machinery/atmospherics/components/unary/oxygen_generator/New()
..()
var/datum/gas_mixture/air_contents = AIR1
air_contents.volume = 50
AIR1 = air_contents
/obj/machinery/atmospherics/components/unary/oxygen_generator/process_atmos()
..()
if(!on)
return 0
var/datum/gas_mixture/air_contents = AIR1
var/total_moles = air_contents.total_moles()
if(total_moles < oxygen_content)
var/current_heat_capacity = air_contents.heat_capacity()
var/added_oxygen = oxygen_content - total_moles
air_contents.temperature = (current_heat_capacity*air_contents.temperature + 20*added_oxygen*T0C)/(current_heat_capacity+20*added_oxygen)
air_contents.assert_gas("o2")
air_contents.gases["o2"][MOLES] += added_oxygen
update_parents()
return 1
@@ -0,0 +1,40 @@
/obj/machinery/atmospherics/components/unary/portables_connector
name = "connector port"
desc = "For connecting portables devices related to atmospherics control."
icon = 'icons/obj/atmospherics/components/unary_devices.dmi'
icon_state = "connector_map" //Only for mapping purposes, so mappers can see direction
can_unwrench = 1
var/obj/machinery/portable_atmospherics/connected_device
use_power = 0
level = 0
/obj/machinery/atmospherics/components/unary/portables_connector/New()
..()
var/datum/gas_mixture/air_contents = AIR1
air_contents.volume = 0
/obj/machinery/atmospherics/components/unary/portables_connector/visible
level = 2
/obj/machinery/atmospherics/components/unary/portables_connector/process_atmos()
if(!connected_device)
return
update_parents()
/obj/machinery/atmospherics/components/unary/portables_connector/Destroy()
if(connected_device)
connected_device.disconnect()
return ..()
/obj/machinery/atmospherics/components/unary/portables_connector/can_unwrench(mob/user)
if(..())
if(connected_device)
user << "<span class='warning'>You cannot unwrench this [src], detach [connected_device] first!</span>"
else
return 1
/obj/machinery/atmospherics/components/unary/portables_connector/portableConnectorReturnAir()
return connected_device.portableConnectorReturnAir()
/obj/proc/portableConnectorReturnAir()
@@ -0,0 +1,49 @@
#define AIR_CONTENTS (25*ONE_ATMOSPHERE)*(air_contents.volume)/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
/obj/machinery/atmospherics/components/unary/tank
icon = 'icons/obj/atmospherics/pipes/pressure_tank.dmi'
icon_state = "generic"
name = "pressure tank"
desc = "A large vessel containing pressurized gas."
var/volume = 10000 //in liters, 1 meters by 1 meters by 2 meters
density = 1
var/gas_type = 0
/obj/machinery/atmospherics/components/unary/tank/New()
..()
var/datum/gas_mixture/air_contents = AIR1
air_contents.volume = volume
air_contents.temperature = T20C
if(gas_type)
air_contents.assert_gas(gas_type)
air_contents.gases[gas_type][MOLES] = AIR_CONTENTS
name = "[name] ([air_contents.gases[gas_type][GAS_META][META_GAS_NAME]])"
/obj/machinery/atmospherics/components/unary/tank/carbon_dioxide
gas_type = "co2"
/obj/machinery/atmospherics/components/unary/tank/toxins
icon_state = "orange"
gas_type = "plasma"
/obj/machinery/atmospherics/components/unary/tank/oxygen_agent_b
icon_state = "orange_2"
gas_type = "agent_b"
/obj/machinery/atmospherics/components/unary/tank/oxygen
icon_state = "blue"
gas_type = "o2"
/obj/machinery/atmospherics/components/unary/tank/nitrogen
icon_state = "red"
gas_type = "n2"
/obj/machinery/atmospherics/components/unary/tank/air
icon_state = "grey"
name = "pressure tank (Air)"
/obj/machinery/atmospherics/components/unary/tank/air/New()
..()
var/datum/gas_mixture/air_contents = AIR1
air_contents.assert_gases("o2", "n2")
air_contents.gases["o2"][MOLES] = AIR_CONTENTS * 0.2
air_contents.gases["n2"][MOLES] = AIR_CONTENTS * 0.8
@@ -0,0 +1,232 @@
/obj/machinery/atmospherics/components/unary/thermomachine
name = "thermomachine"
desc = "Heats or cools gas in connected pipes."
icon_state = "cold_map"
var/icon_state_on = "cold_on"
var/icon_state_open = "cold_off"
density = TRUE
anchored = TRUE
var/on = FALSE
var/min_temperature = 0
var/max_temperature = 0
var/target_temperature = T20C
var/heat_capacity = 0
var/interactive = TRUE // So mapmakers can disable interaction.
/obj/machinery/atmospherics/components/unary/thermomachine/New()
..()
initialize_directions = dir
var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/thermomachine(null)
B.apply_default_parts(src)
/obj/item/weapon/circuitboard/machine/thermomachine
name = "circuit board (Thermomachine)"
desc = "You can use a screwdriver to switch between heater and freezer."
origin_tech = "programming=3;plasmatech=3"
req_components = list(
/obj/item/weapon/stock_parts/matter_bin = 2,
/obj/item/weapon/stock_parts/micro_laser = 2,
/obj/item/stack/cable_coil = 1,
/obj/item/weapon/stock_parts/console_screen = 1)
/obj/item/weapon/circuitboard/machine/thermomachine/attackby(obj/item/I, mob/user, params)
var/obj/item/weapon/circuitboard/machine/freezer = /obj/item/weapon/circuitboard/machine/thermomachine/freezer
var/obj/item/weapon/circuitboard/machine/heater = /obj/item/weapon/circuitboard/machine/thermomachine/heater
var/obj/item/weapon/circuitboard/machine/newtype
if(istype(I, /obj/item/weapon/screwdriver))
var/new_setting = "Heater"
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(build_path == initial(heater.build_path))
newtype = freezer
new_setting = "Freezer"
else
newtype = heater
name = initial(newtype.name)
build_path = initial(newtype.build_path)
user << "<span class='notice'>You change the circuitboard setting to \"[new_setting]\".</span>"
else
return ..()
/obj/machinery/atmospherics/components/unary/thermomachine/construction()
..(dir,dir)
/obj/machinery/atmospherics/components/unary/thermomachine/RefreshParts()
var/B
for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
B += M.rating
heat_capacity = 5000 * ((B - 1) ** 2)
/obj/machinery/atmospherics/components/unary/thermomachine/update_icon()
if(panel_open)
icon_state = icon_state_open
else if(on && is_operational())
icon_state = icon_state_on
else
icon_state = initial(icon_state)
return
/obj/machinery/atmospherics/components/unary/thermomachine/update_icon_nopipes()
cut_overlays()
if(showpipe)
add_overlay(getpipeimage(icon, "scrub_cap", initialize_directions))
/obj/machinery/atmospherics/components/unary/thermomachine/process_atmos()
..()
if(!on || !NODE1)
return
var/datum/gas_mixture/air_contents = AIR1
var/air_heat_capacity = air_contents.heat_capacity()
var/combined_heat_capacity = heat_capacity + air_heat_capacity
var/old_temperature = air_contents.temperature
if(combined_heat_capacity > 0)
var/combined_energy = heat_capacity * target_temperature + air_heat_capacity * air_contents.temperature
air_contents.temperature = combined_energy/combined_heat_capacity
var/temperature_delta= abs(old_temperature - air_contents.temperature)
if(temperature_delta > 1)
active_power_usage = (heat_capacity * temperature_delta) / 10 + idle_power_usage
update_parents()
else
active_power_usage = idle_power_usage
return 1
/obj/machinery/atmospherics/components/unary/thermomachine/power_change()
..()
update_icon()
/obj/machinery/atmospherics/components/unary/thermomachine/attackby(obj/item/I, mob/user, params)
if(!(on || state_open))
if(default_deconstruction_screwdriver(user, icon_state_open, initial(icon_state), I))
return
if(exchange_parts(user, I))
return
if(default_change_direction_wrench(user, I))
return
if(default_deconstruction_crowbar(I))
return
return ..()
/obj/machinery/atmospherics/components/unary/thermomachine/default_change_direction_wrench(mob/user, obj/item/weapon/wrench/W)
if(!..())
return 0
SetInitDirections()
var/obj/machinery/atmospherics/node = NODE1
if(node)
node.disconnect(src)
NODE1 = null
nullifyPipenet(PARENT1)
atmosinit()
node = NODE1
if(node)
node.atmosinit()
node.addMember(src)
build_network()
return 1
/obj/machinery/atmospherics/components/unary/thermomachine/ui_status(mob/user)
if(interactive)
return ..()
return UI_CLOSE
/obj/machinery/atmospherics/components/unary/thermomachine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
datum/tgui/master_ui = null, datum/ui_state/state = default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "thermomachine", name, 400, 240, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/unary/thermomachine/ui_data(mob/user)
var/list/data = list()
data["on"] = on
data["min"] = min_temperature
data["max"] = max_temperature
data["target"] = target_temperature
data["initial"] = initial(target_temperature)
var/datum/gas_mixture/air1 = AIR1
data["temperature"] = air1.temperature
data["pressure"] = air1.return_pressure()
return data
/obj/machinery/atmospherics/components/unary/thermomachine/ui_act(action, params)
if(..())
return
switch(action)
if("power")
on = !on
use_power = 1 + on
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
. = TRUE
if("target")
var/target = params["target"]
var/adjust = text2num(params["adjust"])
if(target == "input")
target = input("Set new target ([min_temperature]-[max_temperature] K):", name, target_temperature) as num|null
if(!isnull(target) && !..())
. = TRUE
else if(adjust)
target = target_temperature + adjust
. = TRUE
else if(text2num(target) != null)
target = text2num(target)
. = TRUE
if(.)
target_temperature = Clamp(target, min_temperature, max_temperature)
investigate_log("was set to [target_temperature] K by [key_name(usr)]", "atmos")
update_icon()
/obj/machinery/atmospherics/components/unary/thermomachine/freezer
name = "freezer"
icon = 'icons/obj/Cryogenic2.dmi'
icon_state = "freezer"
icon_state_on = "freezer_1"
icon_state_open = "freezer-o"
max_temperature = T20C
min_temperature = 170
/obj/machinery/atmospherics/components/unary/thermomachine/freezer/New()
..()
var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/thermomachine/freezer(null)
B.apply_default_parts(src)
/obj/item/weapon/circuitboard/machine/thermomachine/freezer
name = "circuit board (Freezer)"
build_path = /obj/machinery/atmospherics/components/unary/thermomachine/freezer
/obj/machinery/atmospherics/components/unary/thermomachine/freezer/RefreshParts()
..()
var/L
for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts)
L += M.rating
min_temperature = max(T0C - (initial(min_temperature) + L * 15), TCMB)
/obj/machinery/atmospherics/components/unary/thermomachine/heater
name = "heater"
icon = 'icons/obj/Cryogenic2.dmi'
icon_state = "heater"
icon_state_on = "heater_1"
icon_state_open = "heater-o"
max_temperature = 140
min_temperature = T20C
/obj/machinery/atmospherics/components/unary/thermomachine/heater/New()
..()
var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/thermomachine/heater(null)
B.apply_default_parts(src)
/obj/item/weapon/circuitboard/machine/thermomachine/heater
name = "circuit board (Heater)"
build_path = /obj/machinery/atmospherics/components/unary/thermomachine/heater
/obj/machinery/atmospherics/components/unary/thermomachine/heater/RefreshParts()
..()
var/L
for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts)
L += M.rating
max_temperature = T20C + (initial(max_temperature) * L)
@@ -0,0 +1,17 @@
/obj/machinery/atmospherics/components/unary
icon = 'icons/obj/atmospherics/components/unary_devices.dmi'
dir = SOUTH
initialize_directions = SOUTH
device_type = UNARY
/obj/machinery/atmospherics/components/unary/SetInitDirections()
initialize_directions = dir
/*
Iconnery
*/
/obj/machinery/atmospherics/components/unary/hide(intact)
update_icon()
..(intact)
@@ -0,0 +1,301 @@
#define EXT_BOUND 1
#define INT_BOUND 2
#define NO_BOUND 3
#define SIPHONING 0
#define RELEASING 1
/obj/machinery/atmospherics/components/unary/vent_pump
name = "air vent"
desc = "Has a valve and pump attached to it"
icon_state = "vent_map"
use_power = 1
can_unwrench = 1
welded = 0
level = 1
var/id_tag = null
var/on = 0
var/pump_direction = RELEASING
var/pressure_checks = EXT_BOUND
var/external_pressure_bound = ONE_ATMOSPHERE
var/internal_pressure_bound = 0
//EXT_BOUND: Do not pass external_pressure_bound
//INT_BOUND: Do not pass internal_pressure_bound
//NO_BOUND: Do not pass either
var/frequency = 1439
var/datum/radio_frequency/radio_connection
var/radio_filter_out
var/radio_filter_in
/obj/machinery/atmospherics/components/unary/vent_pump/on
on = 1
icon_state = "vent_out"
/obj/machinery/atmospherics/components/unary/vent_pump/siphon
pump_direction = SIPHONING
/obj/machinery/atmospherics/components/unary/vent_pump/siphon/on
on = 1
icon_state = "vent_in"
/obj/machinery/atmospherics/components/unary/vent_pump/New()
..()
if(!id_tag)
assign_uid()
id_tag = num2text(uid)
/obj/machinery/atmospherics/components/unary/vent_pump/Destroy()
var/area/A = get_area_master(src)
A.air_vent_names -= id_tag
A.air_vent_info -= id_tag
if(SSradio)
SSradio.remove_object(src,frequency)
radio_connection = null
return ..()
/obj/machinery/atmospherics/components/unary/vent_pump/high_volume
name = "large air vent"
power_channel = EQUIP
/obj/machinery/atmospherics/components/unary/vent_pump/high_volume/New()
..()
var/datum/gas_mixture/air_contents = AIR1
air_contents.volume = 1000
/obj/machinery/atmospherics/components/unary/vent_pump/update_icon_nopipes()
cut_overlays()
if(showpipe)
add_overlay(getpipeimage('icons/obj/atmospherics/components/unary_devices.dmi', "vent_cap", initialize_directions))
if(welded)
icon_state = "vent_welded"
return
if(!NODE1 || !on || stat & (NOPOWER|BROKEN))
icon_state = "vent_off"
return
if(pump_direction & RELEASING)
icon_state = "vent_out"
else //pump_direction == SIPHONING
icon_state = "vent_in"
/obj/machinery/atmospherics/components/unary/vent_pump/process_atmos()
..()
if(stat & (NOPOWER|BROKEN))
return
if (!NODE1)
on = 0
if(!on || welded)
return 0
var/datum/gas_mixture/air_contents = AIR1
var/datum/gas_mixture/environment = loc.return_air()
var/environment_pressure = environment.return_pressure()
if(pump_direction & RELEASING) //internal -> external
var/pressure_delta = 10000
if(pressure_checks&EXT_BOUND)
pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure))
if(pressure_checks&INT_BOUND)
pressure_delta = min(pressure_delta, (air_contents.return_pressure() - internal_pressure_bound))
if(pressure_delta > 0)
if(air_contents.temperature > 0)
var/transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
loc.assume_air(removed)
air_update_turf()
else //external -> internal
var/pressure_delta = 10000
if(pressure_checks&EXT_BOUND)
pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound))
if(pressure_checks&INT_BOUND)
pressure_delta = min(pressure_delta, (internal_pressure_bound - air_contents.return_pressure()))
if(pressure_delta > 0)
if(environment.temperature > 0)
var/transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
if (isnull(removed)) //in space
return
air_contents.merge(removed)
air_update_turf()
update_parents()
return 1
//Radio remote control
/obj/machinery/atmospherics/components/unary/vent_pump/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
if(frequency)
radio_connection = SSradio.add_object(src, frequency,radio_filter_in)
/obj/machinery/atmospherics/components/unary/vent_pump/proc/broadcast_status()
if(!radio_connection)
return 0
var/datum/signal/signal = new
signal.transmission_method = 1 //radio signal
signal.source = src
signal.data = list(
"tag" = id_tag,
"frequency" = frequency,
"device" = "VP",
"timestamp" = world.time,
"power" = on,
"direction" = pump_direction ? "release" : "siphon",
"checks" = pressure_checks,
"internal" = internal_pressure_bound,
"external" = external_pressure_bound,
"sigtype" = "status"
)
var/area/A = get_area_master(src)
if(!A.air_vent_names[id_tag])
name = "\improper [A.name] vent pump #[A.air_vent_names.len + 1]"
A.air_vent_names[id_tag] = name
A.air_vent_info[id_tag] = signal.data
radio_connection.post_signal(src, signal, radio_filter_out)
return 1
/obj/machinery/atmospherics/components/unary/vent_pump/atmosinit()
//some vents work his own spesial way
radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null
radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null
if(frequency)
set_frequency(frequency)
broadcast_status()
..()
/obj/machinery/atmospherics/components/unary/vent_pump/receive_signal(datum/signal/signal)
if(stat & (NOPOWER|BROKEN))
return
//log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/atmospherics/components/unary/vent_pump/receive_signal([signal.debug_print()])")
if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command"))
return 0
if("purge" in signal.data)
pressure_checks &= ~EXT_BOUND
pump_direction = SIPHONING
if("stabalize" in signal.data)
pressure_checks |= EXT_BOUND
pump_direction = RELEASING
if("power" in signal.data)
on = text2num(signal.data["power"])
if("power_toggle" in signal.data)
on = !on
if("checks" in signal.data)
pressure_checks = text2num(signal.data["checks"])
if("checks_toggle" in signal.data)
pressure_checks = (pressure_checks?0:NO_BOUND)
if("direction" in signal.data)
pump_direction = text2num(signal.data["direction"])
if("set_internal_pressure" in signal.data)
internal_pressure_bound = Clamp(text2num(signal.data["set_internal_pressure"]),0,ONE_ATMOSPHERE*50)
if("set_external_pressure" in signal.data)
external_pressure_bound = Clamp(text2num(signal.data["set_external_pressure"]),0,ONE_ATMOSPHERE*50)
if("reset_external_pressure" in signal.data)
external_pressure_bound = ONE_ATMOSPHERE
if("adjust_internal_pressure" in signal.data)
internal_pressure_bound = Clamp(internal_pressure_bound + text2num(signal.data["adjust_internal_pressure"]),0,ONE_ATMOSPHERE*50)
if("adjust_external_pressure" in signal.data)
external_pressure_bound = Clamp(external_pressure_bound + text2num(signal.data["adjust_external_pressure"]),0,ONE_ATMOSPHERE*50)
if("init" in signal.data)
name = signal.data["init"]
return
if("status" in signal.data)
broadcast_status()
return //do not update_icon
//log_admin("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]")
broadcast_status()
update_icon()
return
/obj/machinery/atmospherics/components/unary/vent_pump/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/WT = W
if (WT.remove_fuel(0,user))
playsound(loc, 'sound/items/Welder.ogg', 40, 1)
user << "<span class='notice'>You begin welding the vent...</span>"
if(do_after(user, 20/W.toolspeed, target = src))
if(!src || !WT.isOn()) return
playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
if(!welded)
user.visible_message("[user] welds the vent shut.", "<span class='notice'>You weld the vent shut.</span>", "<span class='italics'>You hear welding.</span>")
welded = 1
else
user.visible_message("[user] unwelds the vent.", "<span class='notice'>You unweld the vent.</span>", "<span class='italics'>You hear welding.</span>")
welded = 0
update_icon()
pipe_vision_img = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir)
return 0
else
return ..()
/obj/machinery/atmospherics/components/unary/vent_pump/can_unwrench(mob/user)
if(..())
if(!(stat & NOPOWER) && on)
user << "<span class='warning'>You cannot unwrench this [src], turn it off first!</span>"
else
return 1
/obj/machinery/atmospherics/components/unary/vent_pump/examine(mob/user)
..()
if(welded)
user << "It seems welded shut."
/obj/machinery/atmospherics/components/unary/vent_pump/power_change()
..()
update_icon_nopipes()
/obj/machinery/atmospherics/components/unary/vent_pump/can_crawl_through()
return !welded
/obj/machinery/atmospherics/components/unary/vent_pump/attack_alien(mob/user)
if(!welded || !(do_after(user, 20, target = src)))
return
user.visible_message("[user] furiously claws at [src]!", "You manage to clear away the stuff blocking the vent", "You hear loud scraping noises.")
welded = 0
update_icon()
pipe_vision_img = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir)
playsound(loc, 'sound/weapons/bladeslice.ogg', 100, 1)
#undef INT_BOUND
#undef EXT_BOUND
#undef NO_BOUND
#undef SIPHONING
#undef RELEASING

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